From b2d85698135766547c02e140b2f02cb9f2008f7e Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sun, 7 Jun 2026 13:21:59 -0700 Subject: [PATCH 1/2] overlay_params: detect sensor availability for "full" overlay preset Replace the hardcoded handheld device-id list in preset 4 with a per-GPU check of which sensors are actually reported, so on handhelds information is only shown when it is actually available on the device. AMD and Intel report availability via hwmon (a missing node hides the field); AMD voltage is special-cased since its node exists but reads 0, so it is probed at init. Also drops the redundant gpu_efficiency=0, already set for all of preset 4. --- src/amdgpu.cpp | 9 +++++++++ src/amdgpu.h | 8 ++++++++ src/gpu.h | 7 +++++++ src/gpu_fdinfo.h | 18 ++++++++++++++++++ src/overlay_params.cpp | 28 ++++++++++++++++------------ 5 files changed, 58 insertions(+), 12 deletions(-) diff --git a/src/amdgpu.cpp b/src/amdgpu.cpp index 388fb5e418..cec6b842ba 100644 --- a/src/amdgpu.cpp +++ b/src/amdgpu.cpp @@ -553,6 +553,15 @@ AMDGPU::AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id) { } } + // Probe voltage before the polling thread starts; it shares this FILE*. + if (sysfs_nodes.gpu_voltage_soc) { + int64_t value = 0; + rewind(sysfs_nodes.gpu_voltage_soc); + fflush(sysfs_nodes.gpu_voltage_soc); + if (fscanf(sysfs_nodes.gpu_voltage_soc, "%" PRId64, &value) == 1 && value > 0) + voltage_is_valid = true; + } + throttling = std::make_shared(0x1002); #ifndef TEST_ONLY fdinfo_helper = std::make_unique("amdgpu", pci_dev, "", /*called_from_amdgpu_cpp=*/ true); diff --git a/src/amdgpu.h b/src/amdgpu.h index 9d3088523d..ac2a014444 100644 --- a/src/amdgpu.h +++ b/src/amdgpu.h @@ -506,6 +506,13 @@ class AMDGPU { cond_var.notify_one(); } + bool has_fan_sensor() const { return sysfs_nodes.fan != nullptr; } + bool has_junction_temp_sensor() const { return sysfs_nodes.junction_temp != nullptr; } + bool has_memory_temp_sensor() const { return sysfs_nodes.memory_temp != nullptr; } + bool has_power_limit_sensor() const { return sysfs_nodes.power_limit != nullptr; } + // in0_input exists on some APUs but always reads 0, so require a reading + bool has_voltage_sensor() const { return sysfs_nodes.gpu_voltage_soc != nullptr && voltage_is_valid; } + private: std::string pci_dev; std::string gpu_metrics_path; @@ -515,6 +522,7 @@ class AMDGPU { std::thread thread; struct amdgpu_files sysfs_nodes = {}; bool gpu_metrics_is_valid = false; + bool voltage_is_valid = false; // gpu_voltage_soc read non-zero at init std::condition_variable cond_var; std::atomic stop_thread{false}; std::atomic paused{false}; diff --git a/src/gpu.h b/src/gpu.h index 675d64f8a9..ffde8cc7d3 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -100,6 +100,13 @@ class GPU { return false; } + // Sensor availability: AMD and Intel report it from hwmon + bool has_fan_sensor() { return amdgpu ? amdgpu->has_fan_sensor() : fdinfo ? fdinfo->has_fan_sensor() : true; } + bool has_junction_temp_sensor() { return amdgpu ? amdgpu->has_junction_temp_sensor() : fdinfo ? fdinfo->has_junction_temp_sensor() : true; } + bool has_memory_temp_sensor() { return amdgpu ? amdgpu->has_memory_temp_sensor() : fdinfo ? fdinfo->has_memory_temp_sensor() : true; } + bool has_voltage_sensor() { return amdgpu ? amdgpu->has_voltage_sensor() : fdinfo ? fdinfo->has_voltage_sensor() : true; } + bool has_power_limit_sensor() { return amdgpu ? amdgpu->has_power_limit_sensor() : fdinfo ? fdinfo->has_power_limit_sensor() : true; } + std::shared_ptr throttling() { if (nvidia) return nvidia->throttling; diff --git a/src/gpu_fdinfo.h b/src/gpu_fdinfo.h index 97e62e5265..a2f697dbe2 100644 --- a/src/gpu_fdinfo.h +++ b/src/gpu_fdinfo.h @@ -60,6 +60,8 @@ class GPU_fdinfo { uint64_t fdinfo_last_update_ms = 0; std::map hwmon_sensors; + bool fan_avail = false, voltage_avail = false; + bool mem_temp_avail = false, power_limit_avail = false; std::string drm_engine_type = "EMPTY"; std::string drm_memory_type = "EMPTY"; @@ -83,6 +85,10 @@ class GPU_fdinfo { float get_memory_used(); + bool hwmon_has(const std::string& key) const { + auto it = hwmon_sensors.find(key); + return it != hwmon_sensors.end() && !it->second.filename.empty(); + } void find_hwmon_sensors(); std::string find_hwmon_dir(); std::string find_hwmon_sensor_dir(std::string name); @@ -208,6 +214,12 @@ class GPU_fdinfo { find_hwmon_sensors(); + // Snapshot availability before the worker thread starts mutating the map. + fan_avail = hwmon_has("fan_speed"); + voltage_avail = hwmon_has("voltage"); + mem_temp_avail = hwmon_has("vram_temp"); + power_limit_avail = hwmon_has("power_limit"); + if (module == "i915") find_i915_gt_dir(); else if (module == "xe") @@ -242,4 +254,10 @@ class GPU_fdinfo { } float amdgpu_helper_get_proc_vram(); + + bool has_fan_sensor() const { return fan_avail; } + bool has_voltage_sensor() const { return voltage_avail; } + bool has_memory_temp_sensor() const { return mem_temp_avail; } + bool has_power_limit_sensor() const { return power_limit_avail; } + bool has_junction_temp_sensor() const { return false; } // not exposed via fdinfo }; diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp index 58620c4997..78ddab1f47 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -1380,22 +1380,26 @@ void presets(int preset, struct overlay_params *params, bool inherit) { add_to_options(params, "gpu_efficiency", "0"); add_to_options(params, "cpu_efficiency", "0"); - // Disable some options if steamdeck / other known handhelds + // Hide GPU fields with no backing sensor on any detected GPU if (!gpus) gpus = std::make_unique(params); - for (auto gpu : gpus->available_gpus) { - if (gpu->device_id == 0x1435 || gpu->device_id == 0x163f || gpu->device_id == 0x1681 || gpu->device_id == 0x15bf){ - add_to_options(params, "gpu_fan", "0"); - add_to_options(params, "gpu_junction_temp", "0"); - add_to_options(params, "gpu_voltage", "0"); - add_to_options(params, "gpu_mem_temp", "0"); - add_to_options(params, "gpu_efficiency", "0"); - } - // Rembrandt and Phoenix APUs (Z1, Z1E, Z2 Go) - if (gpu->device_id == 0x1681 || gpu->device_id == 0x15bf){ - add_to_options(params, "gpu_power_limit", "0"); + if (!gpus->available_gpus.empty()) { + bool has_fan = false, has_junction_temp = false, has_memory_temp = false; + bool has_voltage = false, has_power_limit = false; + for (auto gpu : gpus->available_gpus) { + has_fan |= gpu->has_fan_sensor(); + has_junction_temp |= gpu->has_junction_temp_sensor(); + has_memory_temp |= gpu->has_memory_temp_sensor(); + has_voltage |= gpu->has_voltage_sensor(); + has_power_limit |= gpu->has_power_limit_sensor(); } + + if (!has_fan) add_to_options(params, "gpu_fan", "0"); + if (!has_junction_temp) add_to_options(params, "gpu_junction_temp", "0"); + if (!has_memory_temp) add_to_options(params, "gpu_mem_temp", "0"); + if (!has_voltage) add_to_options(params, "gpu_voltage", "0"); + if (!has_power_limit) add_to_options(params, "gpu_power_limit", "0"); } break; From 9e5b9f10ab4fe3191efcfc920cc10e07d96cea48 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sun, 7 Jun 2026 14:26:35 -0700 Subject: [PATCH 2/2] overlay: hide ram_temp when no RAM temperature sensor is present ram_temp is read from an spd5118 hwmon sensor. With no such sensor, like on handhelds, mem_temp stays 0 and the "full" layout shows a bogus "0 C". Track whether a sensor was found and only draw ram_temp when one is, so a real 0 C reading still shows. --- src/hud_elements.cpp | 3 ++- src/memory.cpp | 2 ++ src/memory.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp index eee161f3c8..f76970c3e7 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -778,7 +778,8 @@ void HudElements::ram(){ ImGui::PopFont(); } - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram_temp]) { + // only when an spd5118 RAM temp sensor exists, else mem_temp is a bogus 0 + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_ram_temp] && mem_temp_available) { ImguiNextColumnOrNewRow(); if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(mem_temp)); diff --git a/src/memory.cpp b/src/memory.cpp index fc117f555d..b530c2f4b7 100644 --- a/src/memory.cpp +++ b/src/memory.cpp @@ -12,6 +12,7 @@ float memused, memmax, swapused; int mem_temp; +bool mem_temp_available = false; uint64_t proc_mem_resident, proc_mem_shared, proc_mem_virt; void update_meminfo() { @@ -46,6 +47,7 @@ void update_mem_temp() { if (read_line(path + dir + "/name") == "spd5118") mem_temp_files.emplace_back(path + dir + "/temp1_input"); } + mem_temp_available = !mem_temp_files.empty(); if (mem_temp_files.empty()) SPDLOG_ERROR("failed to find known ram temp sensors"); } diff --git a/src/memory.h b/src/memory.h index c490b2b6ea..209672d907 100644 --- a/src/memory.h +++ b/src/memory.h @@ -6,6 +6,7 @@ extern float memused, memmax, swapused; extern int mem_temp; +extern bool mem_temp_available; extern uint64_t proc_mem_resident, proc_mem_shared, proc_mem_virt; void update_meminfo();