110 lines
4.6 KiB
C++
110 lines
4.6 KiB
C++
#include <array>
|
|
#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 {
|
|
|
|
const std::string rel_metrics_path = "/../../gpu_metrics"; // gpu_metrics path relative to hwmon path
|
|
constexpr uint16_t max_metrics_length = 512; // length of the metrics buffer
|
|
|
|
/* takes pointer to a target gpu::sensors struct and updates its hwmon path and detects gpu_metrics */
|
|
void detectHwmon(gpuSensors* target, const std::string& pci) {
|
|
target->hwmon = utils::getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon");
|
|
applog(LOG::INF, "Detected path \"{}\"", target->hwmon);
|
|
|
|
if (utils::fileExists(target->hwmon + rel_metrics_path)) {
|
|
applog(LOG::INF, "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 std::array<char, 32> buf;
|
|
utils::fastReadNum<1000>(g->hwmon + "/in0_input", buf, g->vcore);
|
|
// g->fan_pwm = std::stoi(readFile(g->hwmon + "/pwm1", 4)) * 100 / 256;
|
|
utils::fastReadNum(g->hwmon + "/fan1_input", buf, g->fan_speed);
|
|
utils::fastReadNum<1000000>(g->hwmon + "/power1_cap", buf, g->power_cap);
|
|
// utils::fastReadNum(g->hwmon + "/power1_cap_max", g->power_cap_max) / 1000000;
|
|
utils::fastReadNum(g->hwmon + "/../../mem_info_vram_used", buf, g->vram_used);
|
|
// utils::fastReadNum(g->hwmon + "/../../mem_info_vram_total", g->vram_total);
|
|
}
|
|
|
|
/* get sensors for pre-vega cards (rx480/580) */
|
|
void updateFromFs(gpuSensors* g) {
|
|
static std::array<char, 32> buf;
|
|
utils::fastReadNum<1000000>(g->hwmon + "/freq1_input", buf, g->clock_core);
|
|
utils::fastReadNum<1000000>(g->hwmon + "/freq2_input", buf, g->clock_mem);
|
|
utils::fastReadNum<1000000>(g->hwmon + "/power1_input", buf, g->power_avg);
|
|
utils::fastReadNum<1000>(g->hwmon + "/temp1_input", buf, g->temp_edge);
|
|
utils::fastReadNum(g->hwmon + "/../../gpu_busy_percent", buf, g->usage_core);
|
|
}
|
|
|
|
/* get sensors for vega and newer (rx5700xt, rx7800xt [*]) */
|
|
void updateFromMetrics(gpuSensors* g) {
|
|
std::array<char, max_metrics_length> raw_metrics{};
|
|
if (!utils::readFile(g->hwmon + rel_metrics_path, raw_metrics)) return;
|
|
|
|
switch (const auto header = reinterpret_cast<metrics::metrics_table_header*>(raw_metrics.data()); header->format_revision * 10 + header->content_revision) {
|
|
/* rx5700xt, rx7800xt */
|
|
case 13: {
|
|
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v1_3*>(raw_metrics.data());
|
|
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: {
|
|
const auto metrics = reinterpret_cast<metrics::gpu_metrics_v2_2*>(raw_metrics.data());
|
|
g->temp_edge = metrics->temperature_gfx / 100;
|
|
break;
|
|
}
|
|
|
|
/* werid hardware i probably dont like, maybe fallback to reading from fs? */
|
|
default: {
|
|
applog(LOG::WRN, "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)
|
|
updateFromMetrics(g);
|
|
else
|
|
updateFromFs(g);
|
|
|
|
if (g->fan_speed > 9999) g->fan_speed = 9999; // sanity check fan speed
|
|
}
|
|
|
|
} // namespace gpu
|