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/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(); 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;