Files
linuxSmartie/src/gpu.cpp
2025-12-14 14:45:06 +01:00

108 lines
4.3 KiB
C++

#include <gpu.hpp>
#include <gpu_metrics.hpp>
#include <log.hpp>
#include <string>
#include <utils.hpp>
/* maybe have a read pp_table function */
/* would be neat to wrap it in a class but eh namespace is good enough */
namespace gpu {
/* takes pointer to a target gpu::sensors struct and updates its hwmon path and detects gpu_metrics */
void detectHwmon(gpuSensors* target, std::string pci) {
target->hwmon = getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon");
log(LOG::INF, "gpu::detectHwmon", "Detected path \"{}\"", target->hwmon);
if (FILE* file = fopen((target->hwmon + REL_METRICS_PATH).c_str(), "r")) {
fclose(file);
log(LOG::INF, "gpu::detectHwmon", "found gpu_metrics");
target->hasGpuMetrics = true;
}
}
/* some metrics are missing in gpu_metrics, we always fill them from fs, unused metrics are commented out */
void fillMissingMetrics(gpuSensors* g) {
static char buf[32];
fastReadNum(g->hwmon + "/in0_input", g->vcore) / 1000.0;
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
fastReadNum(g->hwmon + "/fan1_input", g->fan_speed);
fastReadNum(g->hwmon + "/power1_cap", g->power_cap) / 1000000;
// fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
fastReadNum(g->hwmon + "/../../mem_info_vram_used", g->vram_used);
// fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
}
/* get sensors for pre-vega cards (rx480/580) */
void updateFromFs(gpuSensors* g) {
static char buf[32];
fastReadNum(g->hwmon + "/freq1_input", g->clock_core) / 1000000;
fastReadNum(g->hwmon + "/freq2_input", g->clock_mem) / 1000000;
fastReadNum(g->hwmon + "/power1_input", g->power_avg) / 1000000;
fastReadNum(g->hwmon + "/temp1_input", g->temp_edge) / 1000;
fastReadNum(g->hwmon + "/../../gpu_busy_percent", g->usage_core);
}
/* get sensors for vega and newer (rx5700xt, rx7800xt [*]) */
void updateFromMetrics(gpuSensors* g) {
char raw_metrics[MAX_METRICS_LENGTH];
if (!readFile(g->hwmon + REL_METRICS_PATH, raw_metrics, sizeof(raw_metrics))) return;
auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics);
switch (header->format_revision * 10 + header->content_revision) {
/* rx5700xt, rx7800xt */
case 13: {
auto metrics = reinterpret_cast<metrics::gpu_metrics_v1_3*>(raw_metrics);
g->usage_core = metrics->average_gfx_activity;
g->usage_umc = metrics->average_umc_activity;
g->usage_media = metrics->average_mm_activity;
g->temp_edge = metrics->temperature_edge;
g->temp_hotspot = metrics->temperature_hotspot;
g->temp_mem = metrics->temperature_mem;
g->temp_vrm_gfx = metrics->temperature_vrgfx;
g->temp_vrm_soc = metrics->temperature_vrsoc;
g->temp_vrm_mem = metrics->temperature_vrmem;
g->power_avg = metrics->average_socket_power;
g->fan_speed = metrics->current_fan_speed;
// hacks for rx7800xt
// g->clock_core = metrics->average_vclk1_frequency; // why not gfxclock?
// g->clock_mem = metrics->average_gfxclk_frequency; // why gfxclock? min mem clock seems to be 96mhz, this is probably including time sleeping
// hacks for rx5700xt
g->clock_core = metrics->average_gfxclk_frequency;
g->clock_mem = metrics->average_uclk_frequency * 2; // mem controller clock, actual mem is 2x that
break;
}
/* ryzen 5 5600g */
case 22: {
auto metrics = reinterpret_cast<metrics::gpu_metrics_v2_2*>(raw_metrics);
g->temp_edge = metrics->temperature_gfx / 100;
break;
}
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
default: {
log(LOG::WRN, "gpu::updateFromMetrics", "Unsupported gpu_metrics_v{}_{}", header->format_revision, header->content_revision);
break;
}
}
}
/* reads values from system and updates the target gpu::sensors struct */
void update(gpuSensors* g) {
fillMissingMetrics(g);
if (g->hasGpuMetrics)
gpu::updateFromMetrics(g);
else
gpu::updateFromFs(g);
if (g->fan_speed > 9999) g->fan_speed = 9999; // sanity check fan speed
}
} // namespace gpu