SystemStatService: prioritize discrete GPUs during detection

This commit is contained in:
Lemmy
2025-12-13 13:07:32 -05:00
parent cc114715f2
commit 8c74e0befd
+130 -17
View File
@@ -49,8 +49,12 @@ Singleton {
property int intelTempMaxFiles: 20 // Will test up to temp20_input
// GPU temperature detection
// On dual-GPU systems, we prioritize discrete GPUs over integrated GPUs
// Priority: NVIDIA > AMD dGPU > Intel Arc > AMD iGPU
readonly property var supportedTempGpuSensorNames: ["amdgpu", "xe"]
property string gpuTempHwmonPath: ""
property var foundGpuSensors: [] // [{hwmonPath, type, hasDedicatedVram}]
property int gpuVramCheckIndex: 0
// --------------------------------------------
Component.onCompleted: {
@@ -280,8 +284,10 @@ Singleton {
// --------------------------------------------
// --------------------------------------------
// GPU Temperature
// On dual-GPU systems (e.g., Intel iGPU + NVIDIA dGPU, or AMD APU + AMD dGPU),
// we scan all hwmon entries, then select the best GPU based on priority.
// ----
// #1 - Find GPU sensor: "amdgpu" or "xe" (Intel Arc)
// #1 - Scan all hwmon entries to find GPU sensors
FileView {
id: gpuTempNameReader
property int currentIndex: 0
@@ -289,8 +295,8 @@ Singleton {
function checkNext() {
if (currentIndex >= 16) {
// No sysfs GPU sensor found, try nvidia-smi
Logger.d("SystemStat", "No sysfs GPU sensor found, checking for nvidia-smi");
// Finished scanning all hwmon entries, now check for NVIDIA
Logger.d("SystemStat", `Found ${root.foundGpuSensors.length} sysfs GPU sensor(s), checking for nvidia-smi`);
nvidiaSmiCheck.running = true;
return;
}
@@ -302,16 +308,21 @@ Singleton {
onLoaded: {
const name = text().trim();
if (root.supportedTempGpuSensorNames.includes(name)) {
root.gpuTempHwmonPath = `/sys/class/hwmon/hwmon${currentIndex}`;
root.gpuType = name === "amdgpu" ? "amd" : "intel";
root.gpuAvailable = true;
Logger.i("SystemStat", `Found ${name} GPU thermal sensor at ${root.gpuTempHwmonPath}`);
} else {
currentIndex++;
Qt.callLater(() => {
checkNext();
});
// Collect this GPU sensor, don't stop - continue scanning for more
const hwmonPath = `/sys/class/hwmon/hwmon${currentIndex}`;
const gpuType = name === "amdgpu" ? "amd" : "intel";
root.foundGpuSensors.push({
"hwmonPath": hwmonPath,
"type": gpuType,
"hasDedicatedVram": false // Will be checked later for AMD
});
Logger.d("SystemStat", `Found ${name} GPU sensor at ${hwmonPath}`);
}
// Continue scanning regardless of whether we found a match
currentIndex++;
Qt.callLater(() => {
checkNext();
});
}
onLoadFailed: function (error) {
@@ -343,16 +354,51 @@ Singleton {
stdout: StdioCollector {
onStreamFinished: {
if (text.trim().length > 0) {
root.gpuType = "nvidia";
root.gpuAvailable = true;
Logger.i("SystemStat", "Found NVIDIA GPU (nvidia-smi available)");
} else {
Logger.d("SystemStat", "No GPU temperature sensor found");
// Add NVIDIA as a GPU option (always discrete, highest priority)
root.foundGpuSensors.push({
"hwmonPath": "",
"type": "nvidia",
"hasDedicatedVram": true // NVIDIA is always discrete
});
Logger.d("SystemStat", "Found NVIDIA GPU (nvidia-smi available)");
}
// After NVIDIA check, check VRAM for AMD GPUs to distinguish dGPU from iGPU
root.gpuVramCheckIndex = 0;
checkNextGpuVram();
}
}
}
// ----
// #4 - Check VRAM for AMD GPUs to distinguish dGPU from iGPU
// dGPUs have dedicated VRAM, iGPUs don't (use system RAM)
FileView {
id: gpuVramChecker
printErrors: false
onLoaded: {
// File exists and has content = dGPU with dedicated VRAM
const vramSize = parseInt(text().trim());
if (vramSize > 0) {
root.foundGpuSensors[root.gpuVramCheckIndex].hasDedicatedVram = true;
Logger.d("SystemStat", `GPU at ${root.foundGpuSensors[root.gpuVramCheckIndex].hwmonPath} has dedicated VRAM (dGPU)`);
}
root.gpuVramCheckIndex++;
Qt.callLater(() => {
checkNextGpuVram();
});
}
onLoadFailed: function (error) {
// File doesn't exist = iGPU (no dedicated VRAM)
// hasDedicatedVram is already false by default
root.gpuVramCheckIndex++;
Qt.callLater(() => {
checkNextGpuVram();
});
}
}
// ----
// #4 - Read GPU temperature via nvidia-smi (NVIDIA only)
Process {
@@ -582,6 +628,73 @@ Singleton {
cpuTempReader.reload();
}
// -------------------------------------------------------
// Function to check VRAM for each AMD GPU to determine if it's a dGPU
function checkNextGpuVram() {
// Skip non-AMD GPUs (NVIDIA and Intel Arc are always discrete)
while (root.gpuVramCheckIndex < root.foundGpuSensors.length) {
const gpu = root.foundGpuSensors[root.gpuVramCheckIndex];
if (gpu.type === "amd") {
// Check for dedicated VRAM at hwmonPath/device/mem_info_vram_total
gpuVramChecker.path = `${gpu.hwmonPath}/device/mem_info_vram_total`;
gpuVramChecker.reload();
return;
}
// Skip non-AMD GPUs
root.gpuVramCheckIndex++;
}
// All VRAM checks complete, now select the best GPU
selectBestGpu();
}
// -------------------------------------------------------
// Function to select the best GPU based on priority
// Priority: NVIDIA > AMD dGPU > Intel Arc > AMD iGPU
function selectBestGpu() {
if (root.foundGpuSensors.length === 0) {
Logger.d("SystemStat", "No GPU temperature sensor found");
return;
}
let best = null;
for (var i = 0; i < root.foundGpuSensors.length; i++) {
const gpu = root.foundGpuSensors[i];
// NVIDIA is always highest priority (always discrete)
if (gpu.type === "nvidia") {
best = gpu;
break;
}
// AMD dGPU is second priority
if (gpu.type === "amd" && gpu.hasDedicatedVram) {
best = gpu;
break;
}
// Intel Arc is third priority (always discrete)
if (gpu.type === "intel" && !best) {
best = gpu;
}
// AMD iGPU is lowest priority (fallback)
if (gpu.type === "amd" && !gpu.hasDedicatedVram && !best) {
best = gpu;
}
}
if (best) {
root.gpuTempHwmonPath = best.hwmonPath;
root.gpuType = best.type;
root.gpuAvailable = true;
const gpuDesc = best.type === "nvidia" ? "NVIDIA" : (best.type === "intel" ? "Intel Arc" : (best.hasDedicatedVram ? "AMD dGPU" : "AMD iGPU"));
Logger.i("SystemStat", `Selected ${gpuDesc} for temperature monitoring at ${best.hwmonPath || "nvidia-smi"}`);
}
}
// -------------------------------------------------------
// Function to update GPU temperature
function updateGpuTemperature() {