diff --git a/meson.build b/meson.build index 79c3d2d80c..a3cbad7df6 100644 --- a/meson.build +++ b/meson.build @@ -3,7 +3,7 @@ project('MangoHud', version : 'v0.7.2', license : 'MIT', meson_version: '>=0.60.0', - default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14', 'warning_level=2'] + default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++17', 'warning_level=2'] ) cc = meson.get_compiler('c') diff --git a/src/amdgpu.cpp b/src/amdgpu.cpp index c11e492ec7..eb8f4c764a 100644 --- a/src/amdgpu.cpp +++ b/src/amdgpu.cpp @@ -11,15 +11,7 @@ #include "logging.h" #include "mesa/util/macros.h" -std::string metrics_path = ""; -struct amdgpu_common_metrics amdgpu_common_metrics; -std::mutex amdgpu_common_metrics_m; -std::mutex amdgpu_m; -std::condition_variable amdgpu_c; -bool amdgpu_run_thread = true; -std::unique_ptr throttling; - -bool amdgpu_verify_metrics(const std::string& path) +bool AMDGPU::verify_metrics(const std::string& path) { metrics_table_header header {}; FILE *f; @@ -45,7 +37,8 @@ bool amdgpu_verify_metrics(const std::string& path) case 2: // v2_1, v2_2, v2_3, v2_4 if(header.content_revision<=0 || header.content_revision>4)// v2_0, not naturally aligned break; - cpuStats.cpu_type = "APU"; + + this->is_apu = true; return true; default: break; @@ -56,18 +49,18 @@ bool amdgpu_verify_metrics(const std::string& path) } #define IS_VALID_METRIC(FIELD) (FIELD != 0xffff) -void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) { +void AMDGPU::get_instant_metrics(struct amdgpu_common_metrics *metrics) { FILE *f; void *buf[MAX(sizeof(struct gpu_metrics_v1_3), sizeof(struct gpu_metrics_v2_4))/sizeof(void*)+1]; struct metrics_table_header* header = (metrics_table_header*)buf; - f = fopen(metrics_path.c_str(), "rb"); + f = fopen(gpu_metrics_path.c_str(), "rb"); if (!f) return; // Read the whole file if (fread(buf, sizeof(buf), 1, f) != 0) { - SPDLOG_DEBUG("amdgpu metrics file '{}' is larger than the buffer", metrics_path.c_str()); + SPDLOG_DEBUG("amdgpu metrics file '{}' is larger than the buffer", gpu_metrics_path.c_str()); fclose(f); return; } @@ -105,42 +98,7 @@ void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) { do metrics->average_cpu_power_w = metrics->average_cpu_power_w + amdgpu_metrics->average_core_power[i] / 1000.f; while (++i < ARRAY_SIZE(amdgpu_metrics->average_core_power) && IS_VALID_METRIC(amdgpu_metrics->average_core_power[i])); } else if( IS_VALID_METRIC(amdgpu_metrics->average_socket_power) && IS_VALID_METRIC(amdgpu_metrics->average_gfx_power) ) { - // fallback 2: estimate cpu power from total socket power - metrics->average_cpu_power_w = amdgpu_metrics->average_socket_power / 1000.f - amdgpu_metrics->average_gfx_power / 1000.f; - } else { - // giving up - metrics->average_cpu_power_w = 0; - } - - if( IS_VALID_METRIC(amdgpu_metrics->current_gfxclk) ) { - // prefered method - metrics->current_gfxclk_mhz = amdgpu_metrics->current_gfxclk; - } else if( IS_VALID_METRIC(amdgpu_metrics->average_gfxclk_frequency) ) { - // fallback 1 - metrics->current_gfxclk_mhz = amdgpu_metrics->average_gfxclk_frequency; - } else { - // giving up - metrics->current_gfxclk_mhz = 0; - } - if( IS_VALID_METRIC(amdgpu_metrics->current_uclk) ) { - // prefered method - metrics->current_uclk_mhz = amdgpu_metrics->current_uclk; - } else if( IS_VALID_METRIC(amdgpu_metrics->average_uclk_frequency) ) { - // fallback 1 - metrics->current_uclk_mhz = amdgpu_metrics->average_uclk_frequency; - } else { - // giving up - metrics->current_uclk_mhz = 0; - } - - if( IS_VALID_METRIC(amdgpu_metrics->temperature_soc) ) { - // prefered method - metrics->soc_temp_c = amdgpu_metrics->temperature_soc / 100; - } else if( header->content_revision >= 3 && IS_VALID_METRIC(amdgpu_metrics->average_temperature_soc) ) { - // fallback 1 - metrics->soc_temp_c = amdgpu_metrics->average_temperature_soc / 100; - } else { - // giving up + // fallback 2: estimate cpu power frostd::string pci_dev, uint32_t deviceID, uint32_t vendorID metrics->soc_temp_c = 0; } if( IS_VALID_METRIC(amdgpu_metrics->temperature_gfx) ) { @@ -167,9 +125,11 @@ void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) { do cpu_temp = MAX(cpu_temp, amdgpu_metrics->average_temperature_core[i]); while (++i < ARRAY_SIZE(amdgpu_metrics->average_temperature_core) && IS_VALID_METRIC(amdgpu_metrics->average_temperature_core[i])); metrics->apu_cpu_temp_c = cpu_temp / 100; +#ifdef DETECT_OS_UNIX } else if( cpuStats.ReadcpuTempFile(cpu_temp) ) { // fallback 2: Try temp from file 'm_cpuTempFile' of 'cpu.cpp' metrics->apu_cpu_temp_c = cpu_temp; +#endif } else { // giving up metrics->apu_cpu_temp_c = 0; @@ -189,10 +149,12 @@ void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) { throttling->indep_throttle_status = indep_throttle_status; } -void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing) { +void AMDGPU::get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing) { + while (!stop_thread) { // Get all the samples for (size_t cur_sample_id=0; cur_sample_id < METRICS_SAMPLE_COUNT; cur_sample_id++) { - amdgpu_get_instant_metrics(&metrics_buffer[cur_sample_id]); + if (gpu_metrics_is_valid) + get_instant_metrics(&metrics_buffer[cur_sample_id]); // Detect and fix if the gpu load is reported in centipercent if (gpu_load_needs_dividing || metrics_buffer[cur_sample_id].gpu_load_percent > 100){ @@ -203,34 +165,65 @@ void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[MET usleep(METRICS_POLLING_PERIOD_MS * 1000); } - // Copy the results from the different metrics to amdgpu_common_metrics - amdgpu_common_metrics_m.lock(); - UPDATE_METRIC_AVERAGE(gpu_load_percent); - UPDATE_METRIC_AVERAGE_FLOAT(average_gfx_power_w); - UPDATE_METRIC_AVERAGE_FLOAT(average_cpu_power_w); + if (stop_thread) break; + + std::unique_lock lock(metrics_mutex); + cond_var.wait(lock, [this]() { return !paused || stop_thread; }); + // do one pass of metrics from sysfs nodes + // then we replace with GPU metrics if it's available + get_sysfs_metrics(); + + if (gpu_metrics_is_valid) { + UPDATE_METRIC_AVERAGE(gpu_load_percent); + UPDATE_METRIC_AVERAGE_FLOAT(average_gfx_power_w); + UPDATE_METRIC_AVERAGE_FLOAT(average_cpu_power_w); + + UPDATE_METRIC_AVERAGE(current_gfxclk_mhz); + UPDATE_METRIC_AVERAGE(current_uclk_mhz); + + UPDATE_METRIC_AVERAGE(soc_temp_c); + UPDATE_METRIC_AVERAGE(gpu_temp_c); + UPDATE_METRIC_AVERAGE(apu_cpu_temp_c); - UPDATE_METRIC_AVERAGE(current_gfxclk_mhz); - UPDATE_METRIC_AVERAGE(current_uclk_mhz); + UPDATE_METRIC_MAX(is_power_throttled); + UPDATE_METRIC_MAX(is_current_throttled); + UPDATE_METRIC_MAX(is_temp_throttled); + UPDATE_METRIC_MAX(is_other_throttled); - UPDATE_METRIC_AVERAGE(soc_temp_c); - UPDATE_METRIC_AVERAGE(gpu_temp_c); - UPDATE_METRIC_AVERAGE(apu_cpu_temp_c); + UPDATE_METRIC_MAX(fan_speed); + metrics.fan_rpm = true; - UPDATE_METRIC_MAX(is_power_throttled); - UPDATE_METRIC_MAX(is_current_throttled); - UPDATE_METRIC_MAX(is_temp_throttled); - UPDATE_METRIC_MAX(is_other_throttled); + metrics.load = amdgpu_common_metrics.gpu_load_percent; + metrics.powerUsage = amdgpu_common_metrics.average_gfx_power_w; + metrics.MemClock = amdgpu_common_metrics.current_uclk_mhz; - UPDATE_METRIC_MAX(fan_speed); - amdgpu_common_metrics_m.unlock(); + // Use hwmon instead, see gpu.cpp + if ( deviceID == 0x1435 || deviceID == 0x163f ) + { + // If we are on VANGOGH (Steam Deck), then + // always use core clock from GPU metrics. + metrics.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz; + } + metrics.temp = amdgpu_common_metrics.gpu_temp_c; + metrics.apu_cpu_power = amdgpu_common_metrics.average_cpu_power_w; + metrics.apu_cpu_temp = amdgpu_common_metrics.apu_cpu_temp_c; + + metrics.is_power_throttled = amdgpu_common_metrics.is_power_throttled; + metrics.is_current_throttled = amdgpu_common_metrics.is_current_throttled; + metrics.is_temp_throttled = amdgpu_common_metrics.is_temp_throttled; + metrics.is_other_throttled = amdgpu_common_metrics.is_other_throttled; + + metrics.fan_speed = amdgpu_common_metrics.fan_speed; + } + } } -void amdgpu_metrics_polling_thread() { +void AMDGPU::metrics_polling_thread() { struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT]; bool gpu_load_needs_dividing = false; //some GPUs report load as centipercent // Initial poll of the metrics, so that we have values to display as fast as possible - amdgpu_get_instant_metrics(&amdgpu_common_metrics); + get_instant_metrics(&amdgpu_common_metrics); if (amdgpu_common_metrics.gpu_load_percent > 100){ gpu_load_needs_dividing = true; amdgpu_common_metrics.gpu_load_percent /= 100; @@ -240,48 +233,156 @@ void amdgpu_metrics_polling_thread() { memset(metrics_buffer, 0, sizeof(metrics_buffer)); while (1) { - std::unique_lock lock(amdgpu_m); - amdgpu_c.wait(lock, []{return amdgpu_run_thread;}); - lock.unlock(); #ifndef TEST_ONLY if (HUDElements.params->no_display && !logger->is_active()) usleep(100000); else #endif - amdgpu_get_samples_and_copy(metrics_buffer, gpu_load_needs_dividing); + get_samples_and_copy(metrics_buffer, gpu_load_needs_dividing); } } -void amdgpu_get_metrics(uint32_t deviceID){ - static bool init = false; - if (!init){ - std::thread(amdgpu_metrics_polling_thread).detach(); - init = true; +void AMDGPU::get_sysfs_metrics() { + int64_t value = 0; + if (sysfs_nodes.busy) { + rewind(sysfs_nodes.busy); + fflush(sysfs_nodes.busy); + int value = 0; + if (fscanf(sysfs_nodes.busy, "%d", &value) != 1) + value = 0; + metrics.load = value; } - amdgpu_common_metrics_m.lock(); - gpu_info.load = amdgpu_common_metrics.gpu_load_percent; + if (sysfs_nodes.memory_clock) { + rewind(sysfs_nodes.memory_clock); + fflush(sysfs_nodes.memory_clock); + if (fscanf(sysfs_nodes.memory_clock, "%" PRId64, &value) != 1) + value = 0; - gpu_info.powerUsage = amdgpu_common_metrics.average_gfx_power_w; - gpu_info.MemClock = amdgpu_common_metrics.current_uclk_mhz; + metrics.MemClock = value / 1000000; + } - // Use hwmon instead, see gpu.cpp - if ( deviceID == 0x1435 || deviceID == 0x163f ) - { - // If we are on VANGOGH (Steam Deck), then - // always use use core clock from GPU metrics. - gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz; + // TODO: on some gpus this will use the power1_input instead + // this value is instantaneous and should be averaged over time + // probably just average everything in this function to be safe + if (sysfs_nodes.power_usage) { + rewind(sysfs_nodes.power_usage); + fflush(sysfs_nodes.power_usage); + if (fscanf(sysfs_nodes.power_usage, "%" PRId64, &value) != 1) + value = 0; + + metrics.powerUsage = value / 1000000; + } + + if (sysfs_nodes.fan) { + rewind(sysfs_nodes.fan); + fflush(sysfs_nodes.fan); + if (fscanf(sysfs_nodes.fan, "%" PRId64, &value) != 1) + value = 0; + metrics.fan_speed = value; + metrics.fan_rpm = true; + } + + if (sysfs_nodes.vram_total) { + rewind(sysfs_nodes.vram_total); + fflush(sysfs_nodes.vram_total); + if (fscanf(sysfs_nodes.vram_total, "%" PRId64, &value) != 1) + value = 0; + metrics.memoryTotal = float(value) / (1024 * 1024 * 1024); + } + + if (sysfs_nodes.vram_used) { + rewind(sysfs_nodes.vram_used); + fflush(sysfs_nodes.vram_used); + if (fscanf(sysfs_nodes.vram_used, "%" PRId64, &value) != 1) + value = 0; + metrics.memoryUsed = float(value) / (1024 * 1024 * 1024); + } + // On some GPUs SMU can sometimes return the wrong temperature. + // As HWMON is way more visible than the SMU metrics, let's always trust it as it is the most likely to work + if (sysfs_nodes.core_clock) { + rewind(sysfs_nodes.core_clock); + fflush(sysfs_nodes.core_clock); + if (fscanf(sysfs_nodes.core_clock, "%" PRId64, &value) != 1) + value = 0; + + metrics.CoreClock = value / 1000000; } - // gpu_info.temp = amdgpu_common_metrics.gpu_temp_c; - gpu_info.apu_cpu_power = amdgpu_common_metrics.average_cpu_power_w; - gpu_info.apu_cpu_temp = amdgpu_common_metrics.apu_cpu_temp_c; - gpu_info.is_power_throttled = amdgpu_common_metrics.is_power_throttled; - gpu_info.is_current_throttled = amdgpu_common_metrics.is_current_throttled; - gpu_info.is_temp_throttled = amdgpu_common_metrics.is_temp_throttled; - gpu_info.is_other_throttled = amdgpu_common_metrics.is_other_throttled; + if (sysfs_nodes.temp){ + rewind(sysfs_nodes.temp); + fflush(sysfs_nodes.temp); + int value = 0; + if (fscanf(sysfs_nodes.temp, "%d", &value) != 1) + value = 0; + metrics.temp = value / 1000; + } - gpu_info.fan_speed = amdgpu_common_metrics.fan_speed; + if (sysfs_nodes.junction_temp){ + rewind(sysfs_nodes.junction_temp); + fflush(sysfs_nodes.junction_temp); + int value = 0; + if (fscanf(sysfs_nodes.junction_temp, "%d", &value) != 1) + value = 0; + metrics.junction_temp = value / 1000; + } - amdgpu_common_metrics_m.unlock(); + if (sysfs_nodes.memory_temp){ + rewind(sysfs_nodes.memory_temp); + fflush(sysfs_nodes.memory_temp); + int value = 0; + if (fscanf(sysfs_nodes.memory_temp, "%d", &value) != 1) + value = 0; + metrics.memory_temp = value / 1000; + } + + if (sysfs_nodes.gtt_used) { + rewind(sysfs_nodes.gtt_used); + fflush(sysfs_nodes.gtt_used); + if (fscanf(sysfs_nodes.gtt_used, "%" PRId64, &value) != 1) + value = 0; + metrics.gtt_used = float(value) / (1024 * 1024 * 1024); + } + + if (sysfs_nodes.gpu_voltage_soc) { + rewind(sysfs_nodes.gpu_voltage_soc); + fflush(sysfs_nodes.gpu_voltage_soc); + if (fscanf(sysfs_nodes.gpu_voltage_soc, "%" PRId64, &value) != 1) + value = 0; + metrics.voltage = value; + } } + +AMDGPU::AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id) { + this->pci_dev = pci_dev; + this->device_id = device_id; + this->vendor_id = vendor_id; + const std::string device_path = "/sys/bus/pci/devices/" + pci_dev; + gpu_metrics_path = device_path + "/gpu_metrics"; + gpu_metrics_is_valid = verify_metrics(gpu_metrics_path); + + sysfs_nodes.busy = fopen((device_path + "/gpu_busy_percent").c_str(), "r"); + sysfs_nodes.vram_total = fopen((device_path + "/mem_info_vram_total").c_str(), "r"); + sysfs_nodes.vram_used = fopen((device_path + "/mem_info_vram_used").c_str(), "r"); + sysfs_nodes.gtt_used = fopen((device_path + "/mem_info_gtt_used").c_str(), "r"); + + const std::string hwmon_path = device_path + "/hwmon/"; + if (fs::exists(hwmon_path)){ + const auto dirs = ls(hwmon_path.c_str(), "hwmon", LS_DIRS); + for (const auto& dir : dirs) { + sysfs_nodes.temp = fopen((hwmon_path + dir + "/temp1_input").c_str(), "r"); + sysfs_nodes.junction_temp = fopen((hwmon_path + dir + "/temp2_input").c_str(), "r"); + sysfs_nodes.memory_temp = fopen((hwmon_path + dir + "/temp3_input").c_str(), "r"); + sysfs_nodes.core_clock = fopen((hwmon_path + dir + "/freq1_input").c_str(), "r"); + sysfs_nodes.gpu_voltage_soc = fopen((hwmon_path + dir + "/in0_input").c_str(), "r"); + sysfs_nodes.memory_clock = fopen((hwmon_path + dir + "/freq2_input").c_str(), "r"); + sysfs_nodes.power_usage = fopen((hwmon_path + dir + "/power1_average").c_str(), "r"); + sysfs_nodes.power_usage = fopen((hwmon_path + dir + "/power1_input").c_str(), "r"); + sysfs_nodes.fan = fopen((hwmon_path + dir + "/fan1_input").c_str(), "r"); + } + } + + throttling = std::make_shared(0x1002); + std::thread thread(&AMDGPU::metrics_polling_thread, this); + thread.detach(); +} \ No newline at end of file diff --git a/src/amdgpu.h b/src/amdgpu.h index 7cfcead1dc..6a7c9827e0 100644 --- a/src/amdgpu.h +++ b/src/amdgpu.h @@ -1,6 +1,4 @@ #pragma once -// #include -// #include #include #include #include @@ -11,12 +9,9 @@ #include #include #include +#include "gpu_metrics_util.h" -#define METRICS_UPDATE_PERIOD_MS 500 -#define METRICS_POLLING_PERIOD_MS 25 -#define METRICS_SAMPLE_COUNT (METRICS_UPDATE_PERIOD_MS/METRICS_POLLING_PERIOD_MS) #define NUM_HBM_INSTANCES 4 - #define UPDATE_METRIC_AVERAGE(FIELD) do { int value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } amdgpu_common_metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0) #define UPDATE_METRIC_AVERAGE_FLOAT(FIELD) do { float value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } amdgpu_common_metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0) #define UPDATE_METRIC_MAX(FIELD) do { int cur_max = metrics_buffer[0].FIELD; for (size_t s=1; s < METRICS_SAMPLE_COUNT; s++) { cur_max = MAX(cur_max, metrics_buffer[s].FIELD); }; amdgpu_common_metrics.FIELD = cur_max; } while(0) @@ -232,6 +227,23 @@ struct gpu_metrics_v2_4 { uint16_t average_gfx_current; }; +struct amdgpu_files +{ + FILE *vram_total; + FILE *vram_used; + /* The following can be NULL, in that case we're using the gpu_metrics node */ + FILE *busy; + FILE *temp; + FILE *junction_temp; + FILE *memory_temp; + FILE *core_clock; + FILE *memory_clock; + FILE *power_usage; + FILE *gtt_used; + FILE *fan; + FILE *gpu_voltage_soc; +}; + /* This structure is used to communicate the latest values of the amdgpu metrics. * The direction of communication is amdgpu_polling_thread -> amdgpu_get_metrics(). */ @@ -262,49 +274,50 @@ struct amdgpu_common_metrics { uint16_t fan_speed; }; -bool amdgpu_verify_metrics(const std::string& path); -void amdgpu_get_metrics(uint32_t deviceID); extern std::string metrics_path; -extern std::condition_variable amdgpu_c; -extern bool amdgpu_run_thread; -void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics); -void amdgpu_metrics_polling_thread(); -void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing); -void amdgpu_trottling_thread(std::vector &power, std::vector &thermal); - -class Throttling { - public: - std::vector power; - std::vector thermal; - int64_t indep_throttle_status; - - Throttling() - : power(200, 0.0f), - thermal(200, 0.0f) {} - - void update(){ - if (((indep_throttle_status >> 0) & 0xFF) != 0) - power.push_back(0.1); - else - power.push_back(0); - - if (((indep_throttle_status >> 32) & 0xFFFF) != 0) - thermal.push_back(0.1); - else - thermal.push_back(0); - - power.erase(power.begin()); - thermal.erase(thermal.begin()); - } - - bool power_throttling(){ - return std::find(power.begin(), power.end(), 0.1f) != power.end(); - } - - bool thermal_throttling(){ - return std::find(thermal.begin(), thermal.end(), 0.1f) != thermal.end(); - } -}; - -extern std::unique_ptr throttling; +class AMDGPU { + public: + bool is_apu = false; + std::shared_ptr throttling; + + AMDGPU(std::string pci_dev, uint32_t device_id, uint32_t vendor_id); + + gpu_metrics copy_metrics() { + std::lock_guard lock(metrics_mutex); + return metrics; + }; + + void pause() { + paused = true; + cond_var.notify_one(); + }; + + void resume() { + paused = false; + cond_var.notify_one(); + } + + private: + std::string pci_dev; + std::string gpu_metrics_path; + uint32_t device_id; + uint32_t vendor_id; + std::condition_variable amdgpu_c; + std::thread thread; + struct amdgpu_files sysfs_nodes; + bool gpu_metrics_is_valid = false; + std::condition_variable cond_var; + std::atomic stop_thread = false; + std::atomic paused = false; + std::mutex metrics_mutex; + gpu_metrics metrics; + struct amdgpu_common_metrics amdgpu_common_metrics; + + void get_sysfs_metrics(); + bool verify_metrics(const std::string& path); + void get_instant_metrics(struct amdgpu_common_metrics *metrics); + void get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], + bool &gpu_load_needs_dividing); + void metrics_polling_thread(); +}; \ No newline at end of file diff --git a/src/app/main.cpp b/src/app/main.cpp index 51370ed6cf..808da909ca 100644 --- a/src/app/main.cpp +++ b/src/app/main.cpp @@ -345,7 +345,6 @@ int main(int, char**) } HUDElements.vendorID = vendorID; - init_gpu_stats(vendorID, 0, params); init_system_info(); sw_stats.engine = EngineTypes::GAMESCOPE; std::thread(msg_read_thread).detach(); @@ -364,10 +363,9 @@ int main(int, char**) XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); XSync(x11_display, 0); mangoapp_paused = false; - { - amdgpu_run_thread = true; - amdgpu_c.notify_one(); - } + // resume all GPU threads + for (auto gpu : HUDElements.gpus->available_gpus) + gpu->resume(); } { std::unique_lock lk(mangoapp_m); @@ -407,10 +405,10 @@ int main(int, char**) XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1); XSync(x11_display, 0); mangoapp_paused = true; - { - amdgpu_run_thread = false; - amdgpu_c.notify_one(); - } + // pause all GPUs threads + for (auto gpu : HUDElements.gpus->available_gpus) + gpu->pause(); + std::unique_lock lk(mangoapp_m); mangoapp_cv.wait(lk, []{return !params.no_display;}); } diff --git a/src/cpu.cpp b/src/cpu.cpp index 2798f5419f..6d23a5812f 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -281,16 +281,21 @@ bool CPUStats::ReadcpuTempFile(int& temp) { } bool CPUStats::UpdateCpuTemp() { - if (cpu_type == "APU"){ - m_cpuDataTotal.temp = gpu_info.apu_cpu_temp; - return true; + if (HUDElements.gpus){ + for (auto gpu : HUDElements.gpus->available_gpus) + if (gpu->is_apu()) { + m_cpuDataTotal.temp = gpu->metrics.apu_cpu_temp; + return true; + } } else { int temp = 0; - bool ret = ReadcpuTempFile(temp); - m_cpuDataTotal.temp = temp; + bool ret = ReadcpuTempFile(temp); + m_cpuDataTotal.temp = temp; return ret; } + + return false; } static bool get_cpu_power_k10temp(CPUPowerData* cpuPowerData, float& power) { @@ -419,8 +424,15 @@ static bool get_cpu_power_rapl(CPUPowerData* cpuPowerData, float& power) { } static bool get_cpu_power_amdgpu(float& power) { - power = gpu_info.apu_cpu_power; - return true; + if (HUDElements.gpus) + for (auto gpu : HUDElements.gpus->available_gpus) + if (gpu->is_apu()) { + power = gpu->metrics.apu_cpu_power; + return true; + } + + + return false; } bool CPUStats::UpdateCpuPower() { diff --git a/src/cpu.h b/src/cpu.h index 4f4999bf84..9d04887d4e 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -11,6 +11,7 @@ #include #endif #include "timing.hpp" +#include "hud_elements.h" typedef struct CPUData_ { unsigned long long int totalTime; diff --git a/src/gl/gl_hud.cpp b/src/gl/gl_hud.cpp index 83b962d6fc..2d03698314 100644 --- a/src/gl/gl_hud.cpp +++ b/src/gl/gl_hud.cpp @@ -152,7 +152,6 @@ void imgui_create(void *ctx, const gl_wsi plat) glx_mesa_queryInteger(GLX_RENDERER_DEVICE_ID_MESA, &device_id); SPDLOG_DEBUG("GL device id: {:04X}", device_id); - init_gpu_stats(vendorID, device_id, params); sw_stats.gpuName = gpu = remove_parentheses(deviceName); SPDLOG_DEBUG("gpu: {}", gpu); // Setup Dear ImGui context diff --git a/src/gpu.cpp b/src/gpu.cpp index 9a8c8f6750..0684828d4a 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -4,198 +4,143 @@ #include #include #include +#include +#include #include -#ifdef HAVE_XNVCTRL -#include "nvctrl.h" -#endif #include "timing.hpp" -#ifdef HAVE_NVML -#include "nvidia_info.h" -#endif - #include "amdgpu.h" +#include "file_utils.h" using namespace std::chrono_literals; -struct gpuInfo gpu_info {}; -amdgpu_files amdgpu {}; +#include +#include +#include +namespace fs = std::filesystem; -bool checkNvidia(const char *pci_dev){ - bool nvSuccess = false; -#ifdef HAVE_NVML - nvSuccess = checkNVML(pci_dev) && getNVMLInfo({}); -#endif -#ifdef HAVE_XNVCTRL - if (!nvSuccess) - nvSuccess = checkXNVCtrl(); -#endif -#ifdef _WIN32 - if (!nvSuccess) - nvSuccess = checkNVAPI(); -#endif - return nvSuccess; -} +GPUS::GPUS() { + std::vector gpu_entries; -void getNvidiaGpuInfo(const struct overlay_params& params){ -#ifdef HAVE_NVML - if (nvmlSuccess){ - getNVMLInfo(params); - gpu_info.load = nvidiaUtilization.gpu; - gpu_info.temp = nvidiaTemp; - gpu_info.memoryUsed = nvidiaMemory.used / (1024.f * 1024.f * 1024.f); - gpu_info.CoreClock = nvidiaCoreClock; - gpu_info.MemClock = nvidiaMemClock; - gpu_info.powerUsage = nvidiaPowerUsage / 1000; - gpu_info.fan_rpm = false; - gpu_info.memoryTotal = nvidiaMemory.total / (1024.f * 1024.f * 1024.f); - gpu_info.fan_speed = nvidiaFanSpeed; - if (params.enabled[OVERLAY_PARAM_ENABLED_throttling_status]){ - gpu_info.is_temp_throttled = (nvml_throttle_reasons & 0x0000000000000060LL) != 0; - gpu_info.is_power_throttled = (nvml_throttle_reasons & 0x000000000000008CLL) != 0; - gpu_info.is_other_throttled = (nvml_throttle_reasons & 0x0000000000000112LL) != 0; - } - #ifdef HAVE_XNVCTRL - static bool nvctrl_available = checkXNVCtrl(); - if (nvctrl_available) { - gpu_info.fan_rpm = true; - gpu_info.fan_speed = getNvctrlFanSpeed(); - } - #endif - - return; - } -#endif -#ifdef HAVE_XNVCTRL - if (nvctrlSuccess) { - getNvctrlInfo(); - gpu_info.load = nvctrl_info.load; - gpu_info.temp = nvctrl_info.temp; - gpu_info.memoryUsed = nvctrl_info.memoryUsed / (1024.f); - gpu_info.CoreClock = nvctrl_info.CoreClock; - gpu_info.MemClock = nvctrl_info.MemClock; - gpu_info.powerUsage = 0; - gpu_info.memoryTotal = nvctrl_info.memoryTotal; - gpu_info.fan_rpm = true; - gpu_info.fan_speed = nvctrl_info.fan_speed; - return; - } -#endif -#ifdef _WIN32 -nvapi_util(); -#endif -} + // Collect all relevant GPU entries (e.g., card0, card1, etc.) + for (const auto& entry : fs::directory_iterator("/sys/class/drm")) { + if (entry.is_directory()) { + std::string node_name = entry.path().filename().string(); -void getAmdGpuInfo(){ -#ifdef __linux__ - int64_t value = 0; - if (metrics_path.empty()){ - if (amdgpu.busy) { - rewind(amdgpu.busy); - fflush(amdgpu.busy); - int value = 0; - if (fscanf(amdgpu.busy, "%d", &value) != 1) - value = 0; - gpu_info.load = value; + // Check if the directory is a GPU card (e.g., card0, card1, etc.) + if (node_name.find("card") == 0 && node_name.length() == 5 && isdigit(node_name[4])) { + gpu_entries.push_back(node_name); // Store the card entry + } } + } - if (amdgpu.memory_clock) { - rewind(amdgpu.memory_clock); - fflush(amdgpu.memory_clock); - if (fscanf(amdgpu.memory_clock, "%" PRId64, &value) != 1) - value = 0; + // Sort the entries based on the numeric value of the card number + std::sort(gpu_entries.begin(), gpu_entries.end(), [](const std::string& a, const std::string& b) { + int num_a = std::stoi(a.substr(4)); + int num_b = std::stoi(b.substr(4)); + return num_a < num_b; + }); - gpu_info.MemClock = value / 1000000; - } + // Now process the sorted GPU entries + for (const auto& node_name : gpu_entries) { + uint32_t vendor_id = std::stoul(read_line("/sys/class/drm/" + node_name + "/device/vendor"), nullptr, 16); + uint32_t device_id = std::stoul(read_line("/sys/class/drm/" + node_name + "/device/device"), nullptr, 16); + const char* pci_dev = get_pci_device_address("/sys/class/drm/" + node_name).c_str(); - // TODO: on some gpus this will use the power1_input instead - // this value is instantaneous and should be averaged over time - // probably just average everything in this function to be safe - if (amdgpu.power_usage) { - rewind(amdgpu.power_usage); - fflush(amdgpu.power_usage); - if (fscanf(amdgpu.power_usage, "%" PRId64, &value) != 1) - value = 0; + std::shared_ptr ptr = std::make_shared(node_name, vendor_id, device_id, pci_dev); + available_gpus.emplace_back(ptr); - gpu_info.powerUsage = value / 1000000; - } + SPDLOG_DEBUG("GPU Found: node_name: {}, vendor_id: {:x} device_id: {:x} pci_dev: {}", node_name, vendor_id, device_id, pci_dev); } - if (amdgpu.fan) { - rewind(amdgpu.fan); - fflush(amdgpu.fan); - if (fscanf(amdgpu.fan, "%" PRId64, &value) != 1) - value = 0; - gpu_info.fan_speed = value; - gpu_info.fan_rpm = true; - } + find_active_gpu(); +} - if (amdgpu.vram_total) { - rewind(amdgpu.vram_total); - fflush(amdgpu.vram_total); - if (fscanf(amdgpu.vram_total, "%" PRId64, &value) != 1) - value = 0; - gpu_info.memoryTotal = float(value) / (1024 * 1024 * 1024); - } +std::string GPUS::get_pci_device_address(const std::string& drm_card_path) { + // Resolve the symbolic link to get the actual device path + fs::path device_path = fs::canonical(fs::path(drm_card_path) / "device"); - if (amdgpu.vram_used) { - rewind(amdgpu.vram_used); - fflush(amdgpu.vram_used); - if (fscanf(amdgpu.vram_used, "%" PRId64, &value) != 1) - value = 0; - gpu_info.memoryUsed = float(value) / (1024 * 1024 * 1024); - } - // On some GPUs SMU can sometimes return the wrong temperature. - // As HWMON is way more visible than the SMU metrics, let's always trust it as it is the most likely to work - if (amdgpu.core_clock) { - rewind(amdgpu.core_clock); - fflush(amdgpu.core_clock); - if (fscanf(amdgpu.core_clock, "%" PRId64, &value) != 1) - value = 0; - - gpu_info.CoreClock = value / 1000000; - } + // Convert the resolved device path to a string + std::string path_str = device_path.string(); - if (amdgpu.temp){ - rewind(amdgpu.temp); - fflush(amdgpu.temp); - int value = 0; - if (fscanf(amdgpu.temp, "%d", &value) != 1) - value = 0; - gpu_info.temp = value / 1000; - } + // Extract the last PCI address from the path using a regular expression + // This regex matches typical PCI addresses like 0000:03:00.0 + std::regex pci_address_regex(R"((\d{4}:\d{2}:\d{2}\.\d))"); + std::smatch match; + std::string pci_address; - if (amdgpu.junction_temp){ - rewind(amdgpu.junction_temp); - fflush(amdgpu.junction_temp); - int value = 0; - if (fscanf(amdgpu.junction_temp, "%d", &value) != 1) - value = 0; - gpu_info.junction_temp = value / 1000; + // Search for all matches and store the last one + auto it = std::sregex_iterator(path_str.begin(), path_str.end(), pci_address_regex); + auto end = std::sregex_iterator(); + for (std::sregex_iterator i = it; i != end; ++i) { + pci_address = (*i).str(); } - if (amdgpu.memory_temp){ - rewind(amdgpu.memory_temp); - fflush(amdgpu.memory_temp); - int value = 0; - if (fscanf(amdgpu.memory_temp, "%d", &value) != 1) - value = 0; - gpu_info.memory_temp = value / 1000; + if (!pci_address.empty()) { + return pci_address; // Return the last matched PCI address + } else { + SPDLOG_DEBUG("PCI address not found in the path: " + path_str); + return ""; } +} + +void GPUS::find_active_gpu() { + pid_t pid = getpid(); + std::string fdinfo_dir = "/proc/" + std::to_string(pid) + "/fdinfo/"; + bool active_gpu_found = false; + + for (const auto& entry : fs::directory_iterator(fdinfo_dir)) { + if (entry.is_regular_file()) { + std::ifstream file(entry.path()); + std::string line; + std::string drm_pdev; + bool has_drm_driver = false; + bool has_drm_engine_gfx = false; + + while (std::getline(file, line)) { + if (line.find("drm-driver:") != std::string::npos) { + has_drm_driver = true; + } + if (line.find("drm-pdev:") != std::string::npos) { + drm_pdev = line.substr(line.find(":") + 1); + drm_pdev.erase(0, drm_pdev.find_first_not_of(" \t")); + } + if (line.find("drm-engine-gfx:") != std::string::npos) { + uint64_t gfx_time = std::stoull(line.substr(line.find(":") + 1)); + if (gfx_time > 0) { + has_drm_engine_gfx = true; + } + } + } - if (amdgpu.gtt_used) { - rewind(amdgpu.gtt_used); - fflush(amdgpu.gtt_used); - if (fscanf(amdgpu.gtt_used, "%" PRId64, &value) != 1) - value = 0; - gpu_info.gtt_used = float(value) / (1024 * 1024 * 1024); + if (has_drm_driver && has_drm_engine_gfx) { + for (const auto& gpu : available_gpus) { + if (gpu->pci_dev == drm_pdev) { + gpu->is_active = true; + SPDLOG_DEBUG("Active GPU Found: node_name: {}, pci_dev: {}", gpu->name, gpu->pci_dev); + return; + } + } + } + } } - if (amdgpu.gpu_voltage_soc) { - rewind(amdgpu.gpu_voltage_soc); - fflush(amdgpu.gpu_voltage_soc); - if (fscanf(amdgpu.gpu_voltage_soc, "%" PRId64, &value) != 1) - value = 0; - gpu_info.voltage = value; + // NVIDIA GPUs will not show up in fdinfo so we use NVML instead to find the active GPU + // This will not work for older NVIDIA GPUs +#ifdef HAVE_NVML + if (!active_gpu_found) { + for (const auto& gpu : available_gpus) { + // NVIDIA vendor ID is 0x10de + if (gpu->vendor_id == 0x10de) { + for (auto& pid : gpu->nvidia_pids()) { + if (pid == getpid()) { + gpu->is_active = true; + SPDLOG_DEBUG("Active GPU Found: node_name: {}, pci_dev: {}", gpu->name, gpu->pci_dev); + return; + } + } + + } + } } #endif } diff --git a/src/gpu.h b/src/gpu.h index c27a36621c..0af6583050 100644 --- a/src/gpu.h +++ b/src/gpu.h @@ -5,49 +5,162 @@ #include #include #include "overlay_params.h" +#include +#include +#include +#include +#include +#include +#ifdef HAVE_NVML +#include "loaders/loader_nvml.h" +#endif +#ifdef HAVE_XNVCTRL +#include "loaders/loader_nvctrl.h" +#include "loaders/loader_x11.h" +#endif +#include "amdgpu.h" +#include "nvidia.h" +#include "gpu_metrics_util.h" +#include "gpu_fdinfo.h" -struct amdgpu_files -{ - FILE *vram_total; - FILE *vram_used; - /* The following can be NULL, in that case we're using the gpu_metrics node */ - FILE *busy; - FILE *temp; - FILE *junction_temp; - FILE *memory_temp; - FILE *core_clock; - FILE *memory_clock; - FILE *power_usage; - FILE *gtt_used; - FILE *fan; - FILE *gpu_voltage_soc; -}; +class GPU { + public: + gpu_metrics metrics; + std::string name; + std::unique_ptr nvidia = nullptr; + std::unique_ptr amdgpu = nullptr; + std::unique_ptr fdinfo = nullptr; + bool is_active; + std::string pci_dev; + uint32_t vendor_id; + + GPU(std::string name, uint32_t vendor_id, uint32_t device_id, const char* pci_dev) + : name(name), pci_dev(pci_dev), vendor_id(vendor_id), device_id(device_id) { + if (vendor_id == 0x10de) + nvidia = std::make_unique(pci_dev); + + if (vendor_id == 0x1002) + amdgpu = std::make_unique(pci_dev, device_id, vendor_id); + + // For now we're only accepting one of these modules at once + // Might be possible that multiple can exist on a system in the future? + if (vendor_id == 0x8086) + fdinfo = std::make_unique("i915"); + + if (vendor_id == 0x5143) + fdinfo = std::make_unique("msm"); + } + + gpu_metrics get_metrics() { + if (nvidia) + this->metrics = nvidia->copy_metrics(); + + if (amdgpu) + this->metrics = amdgpu->copy_metrics(); + + if (fdinfo) + this->metrics = fdinfo->copy_metrics(); + + return metrics; + }; + + std::vector nvidia_pids() { +#ifdef HAVE_NVML + if (nvidia) + return nvidia->pids(); +#endif + return std::vector(); + } + + void pause() { + if (nvidia) + nvidia->pause(); + + if (amdgpu) + amdgpu->pause(); + + if (fdinfo) + fdinfo->pause(); + } + + void resume() { + if (nvidia) + nvidia->resume(); + + if (amdgpu) + amdgpu->resume(); -extern amdgpu_files amdgpu; - -struct gpuInfo{ - int load; - int temp; - int junction_temp {-1}; - int memory_temp {-1}; - float memoryUsed; - float memoryTotal; - int MemClock; - int CoreClock; - float powerUsage; - float apu_cpu_power; - int apu_cpu_temp; - bool is_power_throttled; - bool is_current_throttled; - bool is_temp_throttled; - bool is_other_throttled; - float gtt_used; - int fan_speed; - int voltage; - bool fan_rpm; + if (fdinfo) + fdinfo->resume(); + } + + bool is_apu() { + if (amdgpu) + return amdgpu->is_apu; + else + return false; + } + + std::shared_ptr throttling() { + if (nvidia) + return nvidia->throttling; + + if (amdgpu) + return amdgpu->throttling; + + return nullptr; + } + + + private: + uint32_t device_id; + std::thread thread; }; -extern struct gpuInfo gpu_info; +class GPUS { + public: + std::vector> available_gpus; + std::mutex metrics_mutex; + + void find_active_gpu(); + GPUS(); + + void pause() { + for (auto& gpu : available_gpus) + gpu->pause(); + } + + void resume() { + for (auto& gpu : available_gpus) + gpu->resume(); + } + + std::shared_ptr active_gpu() { + if (!available_gpus.empty()){ + for (auto gpu : available_gpus) { + if (gpu->is_active) + return gpu; + } + } + + return nullptr; + } + + void update_throttling() { + for (auto gpu : available_gpus) + if (gpu->throttling()) + gpu->throttling()->update(); + } + + void get_metrics() { + std::lock_guard lock(metrics_mutex); + for (auto gpu : available_gpus) + gpu->get_metrics(); + } + + private: + std::string get_pci_device_address(const std::string& drm_card_path); +}; void getNvidiaGpuInfo(const struct overlay_params& params); void getAmdGpuInfo(void); diff --git a/src/msm.cpp b/src/gpu_fdinfo.cpp similarity index 51% rename from src/msm.cpp rename to src/gpu_fdinfo.cpp index c21127f5da..0bc37c2e0b 100644 --- a/src/msm.cpp +++ b/src/gpu_fdinfo.cpp @@ -1,30 +1,8 @@ -#include -#include -#include - -#include "msm.h" -std::unique_ptr msm; +#include "gpu_fdinfo.h" namespace fs = ghc::filesystem; -uint64_t MSM::get_gpu_time() { - char line[256]; - uint64_t total_val = 0; - for (auto fd : fdinfo) { - rewind(fd); - fflush(fd); - uint64_t val = 0; - while (fgets(line, sizeof(line), fd)){ - if (sscanf(line, "drm-engine-gpu: %" SCNu64 " ns", &val) == 1) { - total_val += val; - break; - } - } - } - - return total_val; -} - -void MSM::find_fd() { +void GPU_fdinfo::find_fd() { +#if DETECT_OS_UNIX DIR* dir = opendir("/proc/self/fdinfo"); if (!dir) { perror("Failed to open directory"); @@ -38,7 +16,7 @@ void MSM::find_fd() { char line[256]; bool found_driver = false; while (fgets(line, sizeof(line), file)) { - if (strstr(line, "msm") != NULL) + if (strstr(line, module) != NULL) found_driver = true; if (found_driver) { @@ -54,26 +32,49 @@ void MSM::find_fd() { } closedir(dir); +#endif +} + +uint64_t GPU_fdinfo::get_gpu_time() { + char line[256]; + uint64_t total_val = 0; + for (auto fd : fdinfo) { + rewind(fd); + fflush(fd); + uint64_t val = 0; + while (fgets(line, sizeof(line), fd)){ + if (sscanf(line, "drm-engine-gpu: %" SCNu64 " ns", &val) == 1) { + total_val += val; + break; + } + } + } + + return total_val; } -void MSM::get_fdinfo() { - static uint64_t previous_gpu_time, previous_time, now, gpu_time_now; - gpu_time_now = get_gpu_time(); - now = os_time_get_nano(); +void GPU_fdinfo::get_load() { + while (!stop_thread) { + std::unique_lock lock(metrics_mutex); + cond_var.wait(lock, [this]() { return !paused || stop_thread; }); + + static uint64_t previous_gpu_time, previous_time, now, gpu_time_now; + gpu_time_now = get_gpu_time(); + now = os_time_get_nano(); - if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){ - float time_since_last = now - previous_time; - float gpu_since_last = gpu_time_now - previous_gpu_time; - auto result = int((gpu_since_last / time_since_last) * 100); - if (result > 100) - result = 100; + if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){ + float time_since_last = now - previous_time; + float gpu_since_last = gpu_time_now - previous_gpu_time; + auto result = int((gpu_since_last / time_since_last) * 100); + if (result > 100) + result = 100; - gpu_info_msm.load = result; - previous_gpu_time = gpu_time_now; - previous_time = now; - } else { - previous_gpu_time = gpu_time_now; - previous_time = now; + metrics.load = result; + previous_gpu_time = gpu_time_now; + previous_time = now; + } else { + previous_gpu_time = gpu_time_now; + previous_time = now; + } } } - diff --git a/src/gpu_fdinfo.h b/src/gpu_fdinfo.h new file mode 100644 index 0000000000..9673a6818f --- /dev/null +++ b/src/gpu_fdinfo.h @@ -0,0 +1,54 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "gpu_metrics_util.h" +#include + +class GPU_fdinfo { + private: + bool init = false; + struct gpu_metrics metrics; + std::vector fdinfo; + const char* module; + void find_fd(); + std::thread thread; + std::condition_variable cond_var; + std::atomic stop_thread = false; + std::atomic paused = false; + mutable std::mutex metrics_mutex; + + uint64_t get_gpu_time(); + void get_load(); + + public: + GPU_fdinfo(const char* module) : module(module) { + find_fd(); + std::thread thread(&GPU_fdinfo::get_load, this); + thread.detach(); + } + + gpu_metrics copy_metrics() const { + return metrics; + }; + + void pause() { + paused = true; + cond_var.notify_one(); + } + + void resume() { + paused = false; + cond_var.notify_one(); + } + + ~GPU_fdinfo() { + for (size_t i = 0; i < fdinfo.size(); i++) { + fclose(fdinfo[i]); + } + fdinfo.clear(); + } +}; diff --git a/src/gpu_metrics_util.h b/src/gpu_metrics_util.h new file mode 100644 index 0000000000..0dad2a7bc6 --- /dev/null +++ b/src/gpu_metrics_util.h @@ -0,0 +1,89 @@ +#pragma once + +struct gpu_metrics { + int load; + int temp; + int junction_temp {-1}; + int memory_temp {-1}; + float memoryUsed; + float memoryTotal; + int MemClock; + int CoreClock; + float powerUsage; + float apu_cpu_power; + int apu_cpu_temp; + bool is_power_throttled; + bool is_current_throttled; + bool is_temp_throttled; + bool is_other_throttled; + float gtt_used; + int fan_speed; + int voltage; + bool fan_rpm; + + gpu_metrics() + : load(0), temp(0), junction_temp(0), memory_temp(0), + memoryUsed(0.0f), memoryTotal(0.0f), MemClock(0), CoreClock(0), + powerUsage(0.0f), apu_cpu_power(0.0f), apu_cpu_temp(0), + is_power_throttled(false), is_current_throttled(false), + is_temp_throttled(false), is_other_throttled(false), + gtt_used(0.0f), fan_speed(0), voltage(0), fan_rpm(false) {} +}; + +#define METRICS_UPDATE_PERIOD_MS 500 +#define METRICS_POLLING_PERIOD_MS 25 +#define METRICS_SAMPLE_COUNT (METRICS_UPDATE_PERIOD_MS/METRICS_POLLING_PERIOD_MS) + +#define GPU_UPDATE_METRIC_AVERAGE(FIELD) do { int value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0) +#define GPU_UPDATE_METRIC_AVERAGE_FLOAT(FIELD) do { float value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0) +#define GPU_UPDATE_METRIC_MAX(FIELD) do { int cur_max = metrics_buffer[0].FIELD; for (size_t s=1; s < METRICS_SAMPLE_COUNT; s++) { cur_max = MAX(cur_max, metrics_buffer[s].FIELD); }; metrics.FIELD = cur_max; } while(0) +#define GPU_UPDATE_METRIC_LAST(FIELD) do { metrics.FIELD = metrics_buffer[METRICS_SAMPLE_COUNT - 1].FIELD; } while(0) + +class Throttling { + public: + std::vector power; + std::vector thermal; + int64_t indep_throttle_status; + uint32_t vendor_id; + + Throttling(uint32_t vendor_id) + : power(200, 0.0f), + thermal(200, 0.0f), vendor_id(vendor_id) {} + + void update(){ + if (vendor_id == 0x1002) { + if (((indep_throttle_status >> 0) & 0xFF) != 0) + power.push_back(0.1); + else + power.push_back(0); + + + if (((indep_throttle_status >> 32) & 0xFFFF) != 0) + thermal.push_back(0.1); + else + thermal.push_back(0); + + } else if (vendor_id == 0x10de) { + if ((indep_throttle_status & 0x000000000000008CLL) != 0) + power.push_back(0.1); + else + power.push_back(0); + + if ((indep_throttle_status & 0x0000000000000060LL) != 0) + thermal.push_back(0.1); + else + thermal.push_back(0); + } + + power.erase(power.begin()); + thermal.erase(thermal.begin()); + } + + bool power_throttling(){ + return std::find(power.begin(), power.end(), 0.1f) != power.end(); + } + + bool thermal_throttling(){ + return std::find(thermal.begin(), thermal.end(), 0.1f) != thermal.end(); + } +}; \ No newline at end of file diff --git a/src/hud_elements.cpp b/src/hud_elements.cpp index dc35589ff8..8a478f1d38 100644 --- a/src/hud_elements.cpp +++ b/src/hud_elements.cpp @@ -184,118 +184,129 @@ void HudElements::version(){ } void HudElements::gpu_stats(){ - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){ - ImguiNextColumnFirstItem(); - const char* gpu_text; - if (HUDElements.params->gpu_text.empty()) - gpu_text = "GPU"; - else - gpu_text = HUDElements.params->gpu_text.c_str(); - HUDElements.TextColored(HUDElements.colors.gpu, "%s", gpu_text); ImguiNextColumnOrNewRow(); - auto text_color = HUDElements.colors.text; - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change]){ - struct LOAD_DATA gpu_data = { - HUDElements.colors.gpu_load_low, - HUDElements.colors.gpu_load_med, - HUDElements.colors.gpu_load_high, - HUDElements.params->gpu_load_value[0], - HUDElements.params->gpu_load_value[1] - }; + if (!HUDElements.gpus) + HUDElements.gpus = std::make_unique(); - auto load_color = change_on_load_temp(gpu_data, gpu_info.load); - right_aligned_text(load_color, HUDElements.ralign_width, "%i", gpu_info.load); - ImGui::SameLine(0, 1.0f); - HUDElements.TextColored(load_color,"%%"); - } - else { - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.load); - ImGui::SameLine(0, 1.0f); - HUDElements.TextColored(text_color,"%%"); - // ImGui::SameLine(150); - // ImGui::Text("%s", "%"); - } + std::lock_guard lock(HUDElements.gpus->metrics_mutex); + int i = 0; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){ + for (auto gpu : HUDElements.gpus->available_gpus) { + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_active_gpu] && !gpu->is_active) + continue; - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp]){ + ImguiNextColumnFirstItem(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_active_gpu]) { + HUDElements.TextColored(HUDElements.colors.gpu, "%s", "GPU"); + } else { + HUDElements.TextColored(HUDElements.colors.gpu, "%s", ("GPU" + to_string(i)).c_str()); + } ImguiNextColumnOrNewRow(); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) - right_aligned_text(text_color, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu_info.temp)); - else - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.temp); - ImGui::SameLine(0, 1.0f); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact]) - HUDElements.TextColored(HUDElements.colors.text, "°"); - else + auto text_color = HUDElements.colors.text; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_load_change]){ + struct LOAD_DATA gpu_data = { + HUDElements.colors.gpu_load_low, + HUDElements.colors.gpu_load_med, + HUDElements.colors.gpu_load_high, + HUDElements.params->gpu_load_value[0], + HUDElements.params->gpu_load_value[1] + }; + + auto load_color = change_on_load_temp(gpu_data, gpu->metrics.load); + right_aligned_text(load_color, HUDElements.ralign_width, "%i", gpu->metrics.load); + ImGui::SameLine(0, 1.0f); + HUDElements.TextColored(load_color,"%%"); + } + else { + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.load); + ImGui::SameLine(0, 1.0f); + HUDElements.TextColored(text_color,"%%"); + // ImGui::SameLine(150); + // ImGui::Text("%s", "%"); + } + + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp]){ + ImguiNextColumnOrNewRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) + right_aligned_text(text_color, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu->metrics.temp)); + else + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.temp); + ImGui::SameLine(0, 1.0f); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact]) + HUDElements.TextColored(HUDElements.colors.text, "°"); + else + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) + HUDElements.TextColored(HUDElements.colors.text, "°F"); + else + HUDElements.TextColored(HUDElements.colors.text, "°C"); + } + + if (gpu->metrics.junction_temp > -1 && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_junction_temp]) { + ImguiNextColumnOrNewRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) + right_aligned_text(text_color, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu->metrics.junction_temp)); + else + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.junction_temp); + ImGui::SameLine(0, 1.0f); if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) HUDElements.TextColored(HUDElements.colors.text, "°F"); else HUDElements.TextColored(HUDElements.colors.text, "°C"); - } + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "Jnc"); + ImGui::PopFont(); + } - if (gpu_info.junction_temp > -1 && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_junction_temp]) { - ImguiNextColumnOrNewRow(); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) - right_aligned_text(text_color, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu_info.junction_temp)); - else - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.junction_temp); - ImGui::SameLine(0, 1.0f); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) - HUDElements.TextColored(HUDElements.colors.text, "°F"); - else - HUDElements.TextColored(HUDElements.colors.text, "°C"); - ImGui::SameLine(0, 1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "Jnc"); - ImGui::PopFont(); - } + if (HUDElements.vendorID == 0x1002 || HUDElements.vendorID == 0x10de){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_fan] && cpuStats.cpu_type != "APU"){ + ImguiNextColumnOrNewRow(); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.fan_speed); + ImGui::SameLine(0, 1.0f); + if (gpu->metrics.fan_rpm) { + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "RPM"); + } else { + HUDElements.TextColored(HUDElements.colors.text, "%%"); + ImGui::PushFont(HUDElements.sw_stats->font1); + ImGui::SameLine(0, 1.0f); + HUDElements.TextColored(HUDElements.colors.text, "FAN"); + } + ImGui::PopFont(); + } + } - if (HUDElements.vendorID == 0x1002 || HUDElements.vendorID == 0x10de){ - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_fan] && cpuStats.cpu_type != "APU"){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){ ImguiNextColumnOrNewRow(); - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.fan_speed); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.CoreClock); ImGui::SameLine(0, 1.0f); - if (gpu_info.fan_rpm) { - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "RPM"); - } else { - HUDElements.TextColored(HUDElements.colors.text, "%%"); - ImGui::PushFont(HUDElements.sw_stats->font1); - ImGui::SameLine(0, 1.0f); - HUDElements.TextColored(HUDElements.colors.text, "FAN"); - } + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "MHz"); ImGui::PopFont(); } - } - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock]){ - ImguiNextColumnOrNewRow(); - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.CoreClock); - ImGui::SameLine(0, 1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "MHz"); - ImGui::PopFont(); - } - - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power]) { - ImguiNextColumnOrNewRow(); - char str[16]; - snprintf(str, sizeof(str), "%.1f", gpu_info.powerUsage); - if (strlen(str) > 4) - right_aligned_text(text_color, HUDElements.ralign_width, "%.0f", gpu_info.powerUsage); - else - right_aligned_text(text_color, HUDElements.ralign_width, "%.1f", gpu_info.powerUsage); - ImGui::SameLine(0, 1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "W"); - ImGui::PopFont(); - } + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_power]) { + ImguiNextColumnOrNewRow(); + char str[16]; + snprintf(str, sizeof(str), "%.1f", gpu->metrics.powerUsage); + if (strlen(str) > 4) + right_aligned_text(text_color, HUDElements.ralign_width, "%.0f", gpu->metrics.powerUsage); + else + right_aligned_text(text_color, HUDElements.ralign_width, "%.1f", gpu->metrics.powerUsage); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "W"); + ImGui::PopFont(); + } - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_voltage]) { - ImguiNextColumnOrNewRow(); - right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu_info.voltage); - ImGui::SameLine(0, 1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "mV"); - ImGui::PopFont(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_voltage]) { + ImguiNextColumnOrNewRow(); + right_aligned_text(text_color, HUDElements.ralign_width, "%i", gpu->metrics.voltage); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "mV"); + ImGui::PopFont(); + } + i++; } } } @@ -490,42 +501,62 @@ void HudElements::io_stats(){ } void HudElements::vram(){ + if (!HUDElements.gpus) + HUDElements.gpus = std::make_unique(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_vram]){ - ImguiNextColumnFirstItem(); - HUDElements.TextColored(HUDElements.colors.vram, "VRAM"); - ImguiNextColumnOrNewRow(); - // Add gtt_used to vram usage for APUs - if (cpuStats.cpu_type == "APU") - right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed + gpu_info.gtt_used); - else - right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu_info.memoryUsed); - if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact]){ - ImGui::SameLine(0,1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "GiB"); - ImGui::PopFont(); - } + std::lock_guard lock(HUDElements.gpus->metrics_mutex); + int i = 0; + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_stats]){ + for (auto gpu : HUDElements.gpus->available_gpus) { + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_active_gpu] && !gpu->is_active) + continue; + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_active_gpu]) { + HUDElements.TextColored(HUDElements.colors.vram, "VRAM"); + } else { + HUDElements.TextColored(HUDElements.colors.vram, ("VRAM" + to_string(i)).c_str()); + } - if (gpu_info.memory_temp > -1 && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_temp]) { - ImguiNextColumnOrNewRow(); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) - right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu_info.memory_temp)); - else - right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu_info.memory_temp); - ImGui::SameLine(0, 1.0f); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) - HUDElements.TextColored(HUDElements.colors.text, "°F"); - else - HUDElements.TextColored(HUDElements.colors.text, "°C"); - } + + ImguiNextColumnOrNewRow(); + // Add gtt_used to vram usage for APUs + if (cpuStats.cpu_type == "APU") + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu->metrics.memoryUsed + gpu->metrics.gtt_used); + else + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%.1f", gpu->metrics.memoryUsed); + if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_hud_compact]){ + ImGui::SameLine(0,1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "GiB"); + ImGui::PopFont(); + } - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock]){ - ImguiNextColumnOrNewRow(); - right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu_info.MemClock); - ImGui::SameLine(0, 1.0f); - ImGui::PushFont(HUDElements.sw_stats->font1); - HUDElements.TextColored(HUDElements.colors.text, "MHz"); - ImGui::PopFont(); + if (gpu->metrics.memory_temp > -1 && HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_temp]) { + ImguiNextColumnOrNewRow(); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", HUDElements.convert_to_fahrenheit(gpu->metrics.memory_temp)); + else + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu->metrics.memory_temp); + ImGui::SameLine(0, 1.0f); + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_temp_fahrenheit]) + HUDElements.TextColored(HUDElements.colors.text, "°F"); + else + HUDElements.TextColored(HUDElements.colors.text, "°C"); + } + + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock]){ + ImguiNextColumnOrNewRow(); + right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "%i", gpu->metrics.MemClock); + ImGui::SameLine(0, 1.0f); + ImGui::PushFont(HUDElements.sw_stats->font1); + HUDElements.TextColored(HUDElements.colors.text, "MHz"); + ImGui::PopFont(); + } + i++; + } } } } @@ -815,11 +846,12 @@ void HudElements::frame_timing(){ ImPlot::SetupAxesLimits(0, 200, min_time, max_time); ImPlot::SetNextLineStyle(HUDElements.colors.frametime, 1.5); ImPlot::PlotLine("frametime line", frametime_data.data(), frametime_data.size()); - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && throttling){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && + HUDElements.gpus->active_gpu() && HUDElements.gpus->active_gpu()->throttling()){ ImPlot::SetNextLineStyle(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), 1.5); - ImPlot::PlotLine("power line", throttling->power.data(), throttling->power.size()); + ImPlot::PlotLine("power line", HUDElements.gpus->active_gpu()->throttling()->power.data(), HUDElements.gpus->active_gpu()->throttling()->power.size()); ImPlot::SetNextLineStyle(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 1.5); - ImPlot::PlotLine("thermal line", throttling->thermal.data(), throttling->thermal.size()); + ImPlot::PlotLine("thermal line", HUDElements.gpus->active_gpu()->throttling()->thermal.data(), HUDElements.gpus->active_gpu()->throttling()->thermal.size()); } ImPlot::EndPlot(); } @@ -829,10 +861,10 @@ void HudElements::frame_timing(){ } ImGui::EndChild(); #ifdef __linux__ - if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && throttling){ + if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status_graph] && HUDElements.gpus->active_gpu()->throttling()){ ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2)); - if (throttling->power_throttling()) { + if (HUDElements.gpus->active_gpu()->throttling()->power_throttling()) { ImGui::TextColored(ImVec4(1.0f, 1.0f, 0.0f, 1.0f), "%s", ICON_FK_SQUARE); ImGui::SameLine(); ImGui::Text("Power throttling"); @@ -840,7 +872,7 @@ void HudElements::frame_timing(){ ImGui::Dummy(ImVec2(0.0f, real_font_size.y / 2)); - if (throttling->thermal_throttling()) { + if (HUDElements.gpus->active_gpu()->throttling()->thermal_throttling()) { ImGui::TextColored(ImVec4(1.0f, 0.0f, 0.0f, 1.0f), "%s", ICON_FK_SQUARE); ImGui::SameLine(); ImGui::Text("Thermal throttling"); @@ -1224,18 +1256,19 @@ void HudElements::fan(){ void HudElements::throttling_status(){ if (HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_throttling_status]){ - if (gpu_info.is_power_throttled || gpu_info.is_current_throttled || gpu_info.is_temp_throttled || gpu_info.is_other_throttled){ + auto gpu = HUDElements.gpus->active_gpu(); + if (gpu->metrics.is_power_throttled || gpu->metrics.is_current_throttled || gpu->metrics.is_temp_throttled || gpu->metrics.is_other_throttled){ ImguiNextColumnFirstItem(); HUDElements.TextColored(HUDElements.colors.engine, "%s", "Throttling"); ImguiNextColumnOrNewRow(); ImguiNextColumnOrNewRow(); - if (gpu_info.is_power_throttled) + if (gpu->metrics.is_power_throttled) right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "Power"); - if (gpu_info.is_current_throttled) + if (gpu->metrics.is_current_throttled) right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "Current"); - if (gpu_info.is_temp_throttled) + if (gpu->metrics.is_temp_throttled) right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "Temp"); - if (gpu_info.is_other_throttled) + if (gpu->metrics.is_other_throttled) right_aligned_text(HUDElements.colors.text, HUDElements.ralign_width, "Other"); } } @@ -1339,7 +1372,7 @@ void HudElements::graphs(){ arr.push_back(float(it.gpu_vram_used)); } - HUDElements.max = gpu_info.memoryTotal; + HUDElements.max = HUDElements.gpus->active_gpu()->metrics.memoryTotal; HUDElements.min = 0; HUDElements.TextColored(HUDElements.colors.engine, "%s", "VRAM"); } diff --git a/src/hud_elements.h b/src/hud_elements.h index 1b6622aded..e745ecf105 100644 --- a/src/hud_elements.h +++ b/src/hud_elements.h @@ -12,6 +12,7 @@ #include "net.h" #include "overlay_params.h" #include "shell.h" +#include "gpu.h" struct Function { std::function run; // Using std::function instead of a raw function pointer for more flexibility @@ -65,6 +66,7 @@ class HudElements{ display_servers display_server = UNKNOWN; std::unique_ptr winesync_ptr = nullptr; std::unique_ptr net = nullptr; + std::unique_ptr gpus = nullptr; #ifdef __linux__ std::unique_ptr shell = nullptr; #endif diff --git a/src/intel.cpp b/src/intel.cpp deleted file mode 100644 index 7e3cd9b925..0000000000 --- a/src/intel.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#include "intel.h" -std::unique_ptr intel; - -void Intel::intel_gpu_thread(){ - init = true; - static char stdout_buffer[1024]; - static FILE* intel_gpu_top; - if (runtime) - intel_gpu_top = popen("steam-runtime-launch-client --alongside-steam --host -- intel_gpu_top -J -s 500", "r"); - else - intel_gpu_top = popen("intel_gpu_top -J -s 500", "r"); - - int num_line = 0; - std::string buf; - int num_iterations = 0; - while (fgets(stdout_buffer, sizeof(stdout_buffer), intel_gpu_top)) { - if (num_line > 0) - buf += stdout_buffer; - - num_line++; - if (strlen(stdout_buffer) < 4 && !strchr(stdout_buffer, '{') && !strchr(stdout_buffer, ',')) { - if (buf[0] != '{') - buf = "{\n" + buf; - - if (num_iterations > 0){ - buf += "\n}"; - json j = json::parse(buf); - if (j.contains("engines")) - if (j["engines"].contains("Render/3D/0")) - if (j["engines"]["Render/3D/0"].contains("busy")) - gpu_info_intel.load = j["engines"]["Render/3D/0"]["busy"].get(); - - if (j.contains("engines")) - if (j["engines"].contains("Render/3D")) - if (j["engines"]["Render/3D"].contains("busy")) - gpu_info_intel.load = j["engines"]["Render/3D"]["busy"].get(); - - if (j.contains("frequency")) - if (j["frequency"].contains("actual")) - gpu_info_intel.CoreClock = j["frequency"]["actual"].get(); - if (j.contains("power")){ - if (j["power"].contains("GPU")) - gpu_info_intel.powerUsage = j["power"]["GPU"].get(); - if (j["power"].contains("Package")) - gpu_info_intel.apu_cpu_power = j["power"]["Package"].get(); - } - - } - buf = ""; - num_line = 0; - } - num_iterations++; - if (stop) - break; - } - - int exitcode = pclose(intel_gpu_top) / 256; - if (exitcode > 0){ - if (exitcode == 127) - SPDLOG_INFO("Failed to open '{}'", "intel_gpu_top"); - - if (exitcode == 1) - SPDLOG_INFO("Missing permissions for '{}'", "intel_gpu_top"); - - } -} - -uint64_t Intel::get_gpu_time() { - rewind(fdinfo); - fflush(fdinfo); - char line[256]; - uint64_t val; - while (fgets(line, sizeof(line), fdinfo)){ - if(strstr(line, "drm-engine-render")) - sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val); - } - - return val; -} - -FILE* Intel::find_fd() { - DIR* dir = opendir("/proc/self/fdinfo"); - if (!dir) { - perror("Failed to open directory"); - return NULL; - } - - static uint64_t val; - static bool found_driver; - - for (const auto& entry : fs::directory_iterator("/proc/self/fdinfo")){ - FILE* file = fopen(entry.path().string().c_str(), "r"); - if (file) { - char line[256]; - while (fgets(line, sizeof(line), file)) { - if (strstr(line, "i915") != NULL) - found_driver = true; - - if (found_driver){ - if(strstr(line, "drm-engine-render")){ - sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val); - return file; - } - } - } - } - fclose(file); - } - - return NULL; // Return NULL if no matching file is found -} - -void Intel::get_fdinfo(){ - static uint64_t previous_gpu_time, previous_time, now, gpu_time_now; - gpu_time_now = get_gpu_time(); - now = os_time_get_nano(); - - if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){ - float time_since_last = now - previous_time; - float gpu_since_last = gpu_time_now - previous_gpu_time; - auto result = int((gpu_since_last / time_since_last) * 100); - if (result > 100) - result = 100; - - gpu_info_intel.load = result; - previous_gpu_time = gpu_time_now; - previous_time = now; - } else { - previous_gpu_time = gpu_time_now; - previous_time = now; - } -} diff --git a/src/intel.h b/src/intel.h deleted file mode 100644 index 1fb4be6372..0000000000 --- a/src/intel.h +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "gpu.h" -#include "hud_elements.h" - -using json = nlohmann::json; -namespace fs = ghc::filesystem; - -class Intel { - private: - bool init = false; - bool runtime = false; - bool stop = false; - struct gpuInfo gpu_info_intel {}; - FILE* fdinfo; - struct stat stat_buffer; - std::thread thread; - - FILE* find_fd(); - void intel_gpu_thread(); - uint64_t get_gpu_time(); - void get_fdinfo(); - - public: - Intel() { - if (stat("/run/pressure-vessel", &stat_buffer) == 0) - runtime = true; - - fdinfo = find_fd(); - // thread = std::thread(&Intel::intel_gpu_thread, this); - } - - void update() { - if (fdinfo) - get_fdinfo(); - - gpu_info = gpu_info_intel; - } - - // ~Intel(){ - // stop = true; - // thread.join(); - // } -}; - -extern std::unique_ptr intel; diff --git a/src/loaders/loader_nvctrl.h b/src/loaders/loader_nvctrl.h index 81bc4359cd..61855c4be9 100644 --- a/src/loaders/loader_nvctrl.h +++ b/src/loaders/loader_nvctrl.h @@ -1,6 +1,7 @@ +#pragma once #ifndef LIBRARY_LOADER_NVCTRL_H #define LIBRARY_LOADER_NVCTRL_H -#define Bool bool +// #define Bool bool #include #include "NVCtrl/NVCtrlLib.h" #define LIBRARY_LOADER_NVCTRL_H_DLOPEN diff --git a/src/loaders/loader_nvml.cpp b/src/loaders/loader_nvml.cpp index 5b8501173c..694a43350a 100644 --- a/src/loaders/loader_nvml.cpp +++ b/src/loaders/loader_nvml.cpp @@ -253,6 +253,19 @@ bool libnvml_loader::Load(const std::string& library_name) { return false; } +#if defined(LIBRARY_LOADER_NVML_H_DLOPEN) + nvmlDeviceGetComputeRunningProcesses = + reinterpret_castnvmlDeviceGetComputeRunningProcesses)>( + dlsym(library_, "nvmlDeviceGetComputeRunningProcesses")); +#endif +#if defined(LIBRARY_LOADER_NVML_H_DT_NEEDED) + nvmlDeviceGetComputeRunningProcesses = &::nvmlDeviceGetComputeRunningProcesses; +#endif + if (!nvmlDeviceGetComputeRunningProcesses) { + CleanUp(true); + return false; + } + loaded_ = true; return true; } @@ -278,4 +291,5 @@ void libnvml_loader::CleanUp(bool unload) { nvmlUnitGetFanSpeedInfo = NULL; nvmlUnitGetHandleByIndex = NULL; nvmlDeviceGetFanSpeed = NULL; + nvmlDeviceGetComputeRunningProcesses = NULL; } diff --git a/src/loaders/loader_nvml.h b/src/loaders/loader_nvml.h index 4277ddf8c7..460873799a 100644 --- a/src/loaders/loader_nvml.h +++ b/src/loaders/loader_nvml.h @@ -41,6 +41,7 @@ class libnvml_loader { decltype(&::nvmlUnitGetFanSpeedInfo) nvmlUnitGetFanSpeedInfo; decltype(&::nvmlUnitGetHandleByIndex) nvmlUnitGetHandleByIndex; decltype(&::nvmlDeviceGetFanSpeed) nvmlDeviceGetFanSpeed; + decltype(&::nvmlDeviceGetComputeRunningProcesses) nvmlDeviceGetComputeRunningProcesses; private: void CleanUp(bool unload); diff --git a/src/meson.build b/src/meson.build index 1395678bed..4d961fefa2 100644 --- a/src/meson.build +++ b/src/meson.build @@ -59,7 +59,10 @@ vklayer_files = files( 'config.cpp', 'gpu.cpp', 'blacklist.cpp', - 'file_utils.cpp' + 'file_utils.cpp', + 'nvidia.cpp', + 'gpu_fdinfo.cpp', + 'amdgpu.cpp' ) opengl_files = [] @@ -89,9 +92,6 @@ if is_unixy 'battery.cpp', 'control.cpp', 'device.cpp', - 'amdgpu.cpp', - 'intel.cpp', - 'msm.cpp', 'net.cpp', 'shell.cpp' ) @@ -115,7 +115,6 @@ if is_unixy if nvml_h_found pre_args += '-DHAVE_NVML' vklayer_files += files( - 'nvml.cpp', 'loaders/loader_nvml.cpp', ) endif @@ -133,8 +132,7 @@ if is_unixy pre_args += '-DHAVE_XNVCTRL' vklayer_files += files( - 'loaders/loader_nvctrl.cpp', - 'nvctrl.cpp', + 'loaders/loader_nvctrl.cpp' ) endif diff --git a/src/msm.h b/src/msm.h deleted file mode 100644 index e8c2a1e173..0000000000 --- a/src/msm.h +++ /dev/null @@ -1,34 +0,0 @@ -#include -#include - -#include "gpu.h" - -class MSM { - private: - struct gpuInfo gpu_info_msm {}; - std::vector fdinfo; - void find_fd(); - uint64_t get_gpu_time(); - void get_fdinfo(); - - public: - MSM() { - find_fd(); - } - - ~MSM() { - for (size_t i = 0; i < fdinfo.size(); i++) { - fclose(fdinfo[i]); - } - fdinfo.clear(); - } - - void update() { - if (!fdinfo.empty()) - get_fdinfo(); - - gpu_info = gpu_info_msm; - } -}; - -extern std::unique_ptr msm; diff --git a/src/nvapi.cpp b/src/nvapi.cpp index 19e909e139..71952f2d5d 100644 --- a/src/nvapi.cpp +++ b/src/nvapi.cpp @@ -1,6 +1,5 @@ #include #include -#include "nvidia_info.h" #include "gpu.h" // magic numbers, do not change them @@ -62,6 +61,8 @@ void nvapi_util() gpuUsages[0] = (NVAPI_MAX_USAGES_PER_GPU * 4) | 0x10000; (*NvAPI_EnumPhysicalGPUs)(gpuHandles, &gpuCount); (*NvAPI_GPU_GetUsages)(gpuHandles[0], gpuUsages); - gpu_info.load = gpuUsages[3]; + // TODO: create a GPU class for nvapi + // otherwise we can't display information + // gpu_info.load = gpuUsages[3]; } \ No newline at end of file diff --git a/src/nvctrl.cpp b/src/nvctrl.cpp deleted file mode 100644 index 196ed2b4f3..0000000000 --- a/src/nvctrl.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "nvctrl.h" -#include "loaders/loader_nvctrl.h" -#include "loaders/loader_x11.h" -#include "string_utils.h" -#include "overlay.h" - -typedef std::unordered_map string_map; -static std::unique_ptr> display; - -struct nvctrlInfo nvctrl_info; -bool nvctrlSuccess = false; -int num_coolers = 0; - -static bool find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy) -{ - char buf[8] {}; - auto libx11 = get_libx11(); - for (int i = 0; i < 16; i++) { - snprintf(buf, sizeof(buf), ":%d", i); - Display *d = libx11->XOpenDisplay(buf); - if (d) { - int s = libx11->XDefaultScreen(d); - if (nvctrl.XNVCTRLIsNvScreen(d, s)) { - dpy = d; - SPDLOG_DEBUG("XNVCtrl is using display {}", buf); - return true; - } - libx11->XCloseDisplay(d); - } - } - return false; -} - -bool checkXNVCtrl() -{ - if (!get_libx11()->IsLoaded()) - return false; - - auto& nvctrl = get_libnvctrl_loader(); - if (!nvctrl.IsLoaded()) { - SPDLOG_DEBUG("XNVCtrl loader failed to load"); - return false; - } - - Display *dpy; - nvctrlSuccess = find_nv_x11(nvctrl, dpy); - - if (!nvctrlSuccess) { - SPDLOG_DEBUG("XNVCtrl didn't find the correct display"); - return false; - } - - auto local_x11 = get_libx11(); - display = { dpy, - [local_x11](Display *dpy) { - local_x11->XCloseDisplay(dpy); - } - }; - // get device id at init - int64_t pci_id; - nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), - NV_CTRL_TARGET_TYPE_GPU, - 0, - 0, - NV_CTRL_PCI_ID, - &pci_id); - deviceID = (pci_id & 0xFFFF); - - // get number of coolers at init - nvctrl.XNVCTRLQueryTargetCount(display.get(), - NV_CTRL_TARGET_TYPE_COOLER, - &num_coolers); - - return true; -} - -static void parse_token(std::string token, string_map& options) { - std::string param, value; - - size_t equal = token.find("="); - if (equal == std::string::npos) - return; - - value = token.substr(equal+1); - - param = token.substr(0, equal); - trim(param); - trim(value); - if (!param.empty()) - options[param] = value; -} - -static char* get_attr_target_string(libnvctrl_loader& nvctrl, int attr, int target_type, int target_id) { - char* c = nullptr; - if (!nvctrl.XNVCTRLQueryTargetStringAttribute(display.get(), target_type, target_id, 0, attr, &c)) { - SPDLOG_ERROR("Failed to query attribute '{}'", attr); - } - return c; -} - -void getNvctrlInfo(){ - string_map params; - std::string token; - auto& nvctrl = get_libnvctrl_loader(); - - if (!display) - return; - - int enums[] = { - NV_CTRL_STRING_GPU_UTILIZATION, - NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS, - 0 // keep null - }; - - for (size_t i=0; enums[i]; i++) { - char* str = get_attr_target_string(nvctrl, enums[i], NV_CTRL_TARGET_TYPE_GPU, 0); - if (!str) - continue; - - std::stringstream ss (str); - while (std::getline(ss, token, ',')) { - parse_token(token, params); - } - free(str); - } - - if (!try_stoi(nvctrl_info.load, params["graphics"])) - nvctrl_info.load = 0; - if (!try_stoi(nvctrl_info.CoreClock, params["nvclock"])) - nvctrl_info.CoreClock = 0; - if (!try_stoi(nvctrl_info.MemClock, params["memclock"])) - nvctrl_info.MemClock = 0; - - int64_t temp = 0; - nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), - NV_CTRL_TARGET_TYPE_GPU, - 0, - 0, - NV_CTRL_GPU_CORE_TEMPERATURE, - &temp); - nvctrl_info.temp = temp; - - int64_t memtotal = 0; - nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), - NV_CTRL_TARGET_TYPE_GPU, - 0, - 0, - NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY, - &memtotal); - nvctrl_info.memoryTotal = memtotal; - - int64_t memused = 0; - nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), - NV_CTRL_TARGET_TYPE_GPU, - 0, - 0, - NV_CTRL_USED_DEDICATED_GPU_MEMORY, - &memused); - nvctrl_info.memoryUsed = memused; - - nvctrl_info.fan_speed = getNvctrlFanSpeed(); -} - -int64_t getNvctrlFanSpeed(){ - int64_t fan_speed = 0; - if (num_coolers >= 1) { - auto& nvctrl = get_libnvctrl_loader(); - nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), - NV_CTRL_TARGET_TYPE_COOLER, - 0, - 0, - NV_CTRL_THERMAL_COOLER_SPEED, - &fan_speed); - } - return fan_speed; -} diff --git a/src/nvctrl.h b/src/nvctrl.h deleted file mode 100644 index 2335a5a085..0000000000 --- a/src/nvctrl.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once -#ifndef MANGOHUD_NVCTRL_H -#define MANGOHUD_NVCTRL_H - -struct nvctrlInfo{ - int load; - int temp; - float memoryUsed; - float memoryTotal; - int MemClock; - int CoreClock; - int fan_speed; -}; - -extern struct nvctrlInfo nvctrl_info; -extern bool nvctrlSuccess; -bool checkXNVCtrl(void); -void getNvctrlInfo(void); -int64_t getNvctrlFanSpeed(); - -#endif //MANGOHUD_NVCTRL_H diff --git a/src/nvidia.cpp b/src/nvidia.cpp new file mode 100644 index 0000000000..a839eb3a4f --- /dev/null +++ b/src/nvidia.cpp @@ -0,0 +1,297 @@ +#include "nvml.h" +#include "hud_elements.h" +#include "logging.h" +#include "gpu.h" +#include "string_utils.h" +#include +#include +#include "mesa/util/macros.h" + +#ifdef HAVE_XNVCTRL +void NVIDIA::parse_token(std::string token, std::unordered_map& options) { + std::string param, value; + + size_t equal = token.find("="); + if (equal == std::string::npos) + return; + + value = token.substr(equal+1); + + param = token.substr(0, equal); + trim(param); + trim(value); + if (!param.empty()) + options[param] = value; +} +#endif + +NVIDIA::NVIDIA(const char* pciBusId) { +#ifdef HAVE_NVML + auto& nvml = get_libnvml_loader(); + if (nvml.IsLoaded()){ + nvmlReturn_t result = nvml.nvmlInit(); + if (NVML_SUCCESS != result) { + SPDLOG_ERROR("Nvidia module not loaded"); + nvml_available = false; + } else { + result = NVML_ERROR_UNKNOWN; + if (pciBusId && ((result = nvml.nvmlDeviceGetHandleByPciBusId(pciBusId, &device)) != NVML_SUCCESS)) { + SPDLOG_ERROR("Getting device handle by PCI bus ID failed: {}", nvml.nvmlErrorString(result)); + if (result != NVML_SUCCESS) + SPDLOG_ERROR("Getting device handle failed: {}", nvml.nvmlErrorString(result)); + + nvml_available = false; + } + } + } +#endif + +#ifdef HAVE_XNVCTRL + if (!get_libx11()->IsLoaded()) + SPDLOG_DEBUG("XNVCtrl: X11 not loaded"); + + auto& nvctrl = get_libnvctrl_loader(); + if (!nvctrl.IsLoaded()) + SPDLOG_DEBUG("XNVCtrl loader failed to load"); + + Display *dpy; + nvctrl_available = find_nv_x11(nvctrl, dpy); + if (!nvctrl_available) + SPDLOG_DEBUG("XNVCtrl didn't find the correct display"); + else { + nvctrl.XNVCTRLQueryTargetCount(this->display.get(), + NV_CTRL_TARGET_TYPE_COOLER, + &num_coolers); + } + +#endif +#ifdef HAVE_NVML + nvml_available = true; + if (nvml_available || nvctrl_available){ + nvml.nvmlDeviceGetComputeRunningProcesses(device, &infoCount, process_info); + // first run is to get the proper infoCount + nvml.nvmlDeviceGetComputeRunningProcesses(device, &infoCount, process_info); + throttling = std::make_shared(0x10de); + std::thread thread(&NVIDIA::get_samples_and_copy, this); + thread.detach(); + } +#endif +} + +#ifdef HAVE_NVML +void NVIDIA::get_instant_metrics_nvml(struct gpu_metrics *metrics) { + auto params = HUDElements.params; + nvmlReturn_t response; + auto& nvml = get_libnvml_loader(); + if (nvml_available) { + struct nvmlUtilization_st nvml_utilization; + response = nvml.nvmlDeviceGetUtilizationRates(device, &nvml_utilization); + if (response == NVML_ERROR_NOT_SUPPORTED) { + if (nvml_available) + SPDLOG_ERROR("nvmlDeviceGetUtilizationRates failed, disabling nvml metrics"); + nvml_available = false; + } + metrics->load = nvml_utilization.gpu; + + if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_temp] || logger->is_active()) { + unsigned int temp; + nvml.nvmlDeviceGetTemperature(device, NVML_TEMPERATURE_GPU, &temp); + metrics->temp = temp; + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_vram] || logger->is_active()) { + struct nvmlMemory_st nvml_memory; + nvml.nvmlDeviceGetMemoryInfo(device, &nvml_memory); + metrics->memoryTotal = nvml_memory.total / (1024.f * 1024.f * 1024.f); + metrics->memoryUsed = nvml_memory.used / (1024.f * 1024.f * 1024.f); + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || logger->is_active()) { + unsigned int core_clock; + nvml.nvmlDeviceGetClockInfo(device, NVML_CLOCK_GRAPHICS, &core_clock); + metrics->CoreClock = core_clock; + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock] || logger->is_active()) { + unsigned int memory_clock; + nvml.nvmlDeviceGetClockInfo(device, NVML_CLOCK_MEM, &memory_clock); + metrics->MemClock = memory_clock; + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_power] || logger->is_active()) { + unsigned int power; + nvml.nvmlDeviceGetPowerUsage(device, &power); + metrics->powerUsage = power / 1000; + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_throttling_status]) { + unsigned long long nvml_throttle_reasons; + nvml.nvmlDeviceGetCurrentClocksThrottleReasons(device, &nvml_throttle_reasons); + metrics->is_temp_throttled = (nvml_throttle_reasons & 0x0000000000000060LL) != 0; + metrics->is_power_throttled = (nvml_throttle_reasons & 0x000000000000008CLL) != 0; + metrics->is_other_throttled = (nvml_throttle_reasons & 0x0000000000000112LL) != 0; + if (throttling) + throttling->indep_throttle_status = nvml_throttle_reasons; + } + + if (params->enabled[OVERLAY_PARAM_ENABLED_gpu_fan] || logger->is_active()){ + unsigned int fan_speed; + nvml.nvmlDeviceGetFanSpeed(device, &fan_speed); + metrics->fan_speed = fan_speed; + metrics->fan_rpm = false; + } + #ifdef HAVE_XNVCTRL + if (nvctrl_available) { + metrics->fan_rpm = true; + metrics->fan_speed = NVIDIA::get_nvctrl_fan_speed(); + } + #endif + } +} +#endif + +#ifdef HAVE_XNVCTRL +void NVIDIA::get_instant_metrics_xnvctrl(struct gpu_metrics *metrics) { + std::unordered_map xnvctrl_params; + std::string token; + auto& nvctrl = get_libnvctrl_loader(); + + if (!display) + nvctrl_available = false; + + if (nvctrl_available && !nvml_available) { + + int enums[] = { + NV_CTRL_STRING_GPU_UTILIZATION, + NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS, + 0 // keep null + }; + + for (size_t i=0; enums[i]; i++) { + char* str = get_attr_target_string(nvctrl, enums[i], NV_CTRL_TARGET_TYPE_GPU, 0); + if (!str) + continue; + + std::stringstream ss (str); + while (std::getline(ss, token, ',')) { + parse_token(token, xnvctrl_params); + } + free(str); + } + + if (!try_stoi(metrics->load, xnvctrl_params["graphics"])) + metrics->load = 0; + if (!try_stoi(metrics->CoreClock, xnvctrl_params["nvclock"])) + metrics->CoreClock = 0; + if (!try_stoi(metrics->MemClock, xnvctrl_params["memclock"])) + metrics->MemClock = 0; + + int64_t temp = 0; + nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), + NV_CTRL_TARGET_TYPE_GPU, + 0, + 0, + NV_CTRL_GPU_CORE_TEMPERATURE, + &temp); + metrics->temp = temp; + + int64_t memtotal = 0; + nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), + NV_CTRL_TARGET_TYPE_GPU, + 0, + 0, + NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY, + &memtotal); + metrics->memoryTotal = memtotal; + + int64_t memused = 0; + nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), + NV_CTRL_TARGET_TYPE_GPU, + 0, + 0, + NV_CTRL_USED_DEDICATED_GPU_MEMORY, + &memused); + metrics->memoryUsed = memused; + + metrics->fan_speed = NVIDIA::get_nvctrl_fan_speed(); + } +} +#endif + +void NVIDIA::get_samples_and_copy() { + struct gpu_metrics metrics_buffer[METRICS_SAMPLE_COUNT] {}; + while(!stop_thread) { + for (size_t cur_sample_id=0; cur_sample_id < METRICS_SAMPLE_COUNT; cur_sample_id++) { +#ifdef HAVE_NVML + NVIDIA::get_instant_metrics_nvml(&metrics_buffer[cur_sample_id]); +#endif +#ifdef HAVE_XNVCTRL + NVIDIA::get_instant_metrics_xnvctrl(&metrics_buffer[cur_sample_id]); +#endif + usleep(METRICS_POLLING_PERIOD_MS * 1000); + } + + if (stop_thread) break; + + std::unique_lock lock(metrics_mutex); + cond_var.wait(lock, [this]() { return !paused || stop_thread; }); + GPU_UPDATE_METRIC_AVERAGE(load); + GPU_UPDATE_METRIC_AVERAGE_FLOAT(powerUsage); + GPU_UPDATE_METRIC_AVERAGE(CoreClock); + GPU_UPDATE_METRIC_AVERAGE(MemClock); + + GPU_UPDATE_METRIC_AVERAGE(temp); + + GPU_UPDATE_METRIC_MAX(is_power_throttled); + GPU_UPDATE_METRIC_MAX(is_current_throttled); + GPU_UPDATE_METRIC_MAX(is_temp_throttled); + GPU_UPDATE_METRIC_MAX(is_other_throttled); + + GPU_UPDATE_METRIC_MAX(fan_speed); + } +} + +#ifdef HAVE_XNVCTRL +int64_t NVIDIA::get_nvctrl_fan_speed(){ + int64_t fan_speed = 0; + if (num_coolers >= 1) { + auto& nvctrl = get_libnvctrl_loader(); + nvctrl.XNVCTRLQueryTargetAttribute64(display.get(), + NV_CTRL_TARGET_TYPE_COOLER, + 0, + 0, + NV_CTRL_THERMAL_COOLER_SPEED, + &fan_speed); + } + return fan_speed; +} +#endif + +#ifdef HAVE_XNVCTRL +char* NVIDIA::get_attr_target_string(libnvctrl_loader& nvctrl, int attr, int target_type, int target_id) { + char* c = nullptr; + if (!nvctrl.XNVCTRLQueryTargetStringAttribute(NVIDIA::display.get(), target_type, target_id, 0, attr, &c)) { + SPDLOG_ERROR("Failed to query attribute '{}'", attr); + } + return c; +} +#endif + +#ifdef HAVE_X11 +bool NVIDIA::find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy) +{ + char buf[8] {}; + auto libx11 = get_libx11(); + Display *d = libx11->XOpenDisplay(getenv("DISPLAY")); + if (d) { + int s = libx11->XDefaultScreen(d); + if (nvctrl.XNVCTRLIsNvScreen(d, s)) { + dpy = d; + SPDLOG_DEBUG("XNVCtrl is using display {}", buf); + return true; + } + libx11->XCloseDisplay(d); + } + return false; +} +#endif \ No newline at end of file diff --git a/src/nvidia.h b/src/nvidia.h new file mode 100644 index 0000000000..7e8d8b9583 --- /dev/null +++ b/src/nvidia.h @@ -0,0 +1,69 @@ +#pragma once + +class NVIDIA { + public: + std::shared_ptr throttling; + + gpu_metrics copy_metrics() { + std::lock_guard lock(metrics_mutex); + return metrics; + }; + + void get_samples_and_copy(); + + NVIDIA(const char* pciBusId); + ~NVIDIA() { + stop_thread = true; + if (thread.joinable()) + thread.join(); + }; + +#ifdef HAVE_NVML + std::vector pids() { + std::vector vec; + for (size_t i = 0; i < infoCount; i++) + vec.push_back(static_cast (process_info[i].pid)); + + return vec; + }; +#endif + + void pause() { + paused = true; + cond_var.notify_one(); + }; + + void resume() { + paused = false; + cond_var.notify_one(); + } + + private: +#ifdef HAVE_XNVCTRL + std::unique_ptr> display; + int num_coolers; + unsigned int infoCount = 0; + int64_t get_nvctrl_fan_speed(); +#endif +#ifdef HAVE_NVML + nvmlProcessInfo_t *process_info = new nvmlProcessInfo_t[infoCount]; + nvmlDevice_t device; + void get_instant_metrics_nvml(struct gpu_metrics *metrics); +#endif + bool nvml_available; + bool nvctrl_available; + bool failed; + + std::mutex metrics_mutex; + gpu_metrics metrics; + std::thread thread; + std::condition_variable cond_var; + std::atomic stop_thread = false; + std::atomic paused = false; +#ifdef HAVE_XNVCTRL + void get_instant_metrics_xnvctrl(struct gpu_metrics *metrics); + void parse_token(std::string token, std::unordered_map& options); + bool find_nv_x11(libnvctrl_loader& nvctrl, Display*& dpy); + char* get_attr_target_string(libnvctrl_loader& nvctrl, int attr, int target_type, int target_id); +#endif +}; \ No newline at end of file diff --git a/src/nvidia_info.h b/src/nvidia_info.h deleted file mode 100644 index e74943c9cb..0000000000 --- a/src/nvidia_info.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#ifndef MANGOHUD_NVIDIA_INFO_H -#define MANGOHUD_NVIDIA_INFO_H - -#include -#include "overlay_params.h" - -extern nvmlReturn_t result; -extern unsigned int nvidiaTemp, processSamplesCount, *vgpuInstanceSamplesCount, nvidiaCoreClock, nvidiaMemClock, nvidiaPowerUsage, nvidiaFanSpeed; -extern nvmlDevice_t nvidiaDevice; -extern struct nvmlUtilization_st nvidiaUtilization; -extern struct nvmlMemory_st nvidiaMemory; -extern bool nvmlSuccess; -extern unsigned long long nvml_throttle_reasons; - -bool checkNVML(const char* pciBusId); -bool getNVMLInfo(const struct overlay_params& params); - -#endif //MANGOHUD_NVIDIA_INFO_H diff --git a/src/nvml.cpp b/src/nvml.cpp deleted file mode 100644 index 4a21aec619..0000000000 --- a/src/nvml.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include -#include "loaders/loader_nvml.h" -#include "nvidia_info.h" -#include -#include "overlay.h" -#include "overlay_params.h" -#include "nvctrl.h" -#include "logging.h" - -nvmlReturn_t result; -nvmlDevice_t nvidiaDevice; -nvmlPciInfo_t nvidiaPciInfo; -bool nvmlSuccess = false; -unsigned int nvidiaTemp = 0, nvidiaCoreClock = 0, nvidiaMemClock = 0, nvidiaPowerUsage = 0, nvidiaFanSpeed = 0; -unsigned long long nvml_throttle_reasons; -struct nvmlUtilization_st nvidiaUtilization; -struct nvmlMemory_st nvidiaMemory {}; -struct nvmlUnit_st* nvidiaUnit {}; - -bool checkNVML(const char* pciBusId){ - auto& nvml = get_libnvml_loader(); - if (nvml.IsLoaded()){ - result = nvml.nvmlInit(); - if (NVML_SUCCESS != result) { - SPDLOG_ERROR("Nvidia module not loaded"); - } else { - nvmlReturn_t ret = NVML_ERROR_UNKNOWN; - if (pciBusId && ((ret = nvml.nvmlDeviceGetHandleByPciBusId(pciBusId, &nvidiaDevice)) != NVML_SUCCESS)) { - SPDLOG_ERROR("Getting device handle by PCI bus ID failed: {}", nvml.nvmlErrorString(ret)); - SPDLOG_ERROR("Using index 0."); - } - - if (ret != NVML_SUCCESS) - ret = nvml.nvmlDeviceGetHandleByIndex(0, &nvidiaDevice); - - if (ret != NVML_SUCCESS) - SPDLOG_ERROR("Getting device handle failed: {}", nvml.nvmlErrorString(ret)); - - nvmlSuccess = (ret == NVML_SUCCESS); - if (ret == NVML_SUCCESS) - nvml.nvmlDeviceGetPciInfo_v3(nvidiaDevice, &nvidiaPciInfo); - - return nvmlSuccess; - } - } else { - SPDLOG_ERROR("Failed to load NVML"); - } - - return false; -} - -bool getNVMLInfo(const struct overlay_params& params){ - nvmlReturn_t response; - auto& nvml = get_libnvml_loader(); - response = nvml.nvmlDeviceGetUtilizationRates(nvidiaDevice, &nvidiaUtilization); - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_temp] || logger->is_active()) - nvml.nvmlDeviceGetTemperature(nvidiaDevice, NVML_TEMPERATURE_GPU, &nvidiaTemp); - if (params.enabled[OVERLAY_PARAM_ENABLED_vram] || logger->is_active()) - nvml.nvmlDeviceGetMemoryInfo(nvidiaDevice, &nvidiaMemory); - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_core_clock] || logger->is_active()) - nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_GRAPHICS, &nvidiaCoreClock); - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_mem_clock] || logger->is_active()) - nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_MEM, &nvidiaMemClock); - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_power] || logger->is_active()) - nvml.nvmlDeviceGetPowerUsage(nvidiaDevice, &nvidiaPowerUsage); - deviceID = nvidiaPciInfo.pciDeviceId >> 16; - if (params.enabled[OVERLAY_PARAM_ENABLED_throttling_status]) - nvml.nvmlDeviceGetCurrentClocksThrottleReasons(nvidiaDevice, &nvml_throttle_reasons); - - if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_fan] || logger->is_active()) - nvml.nvmlDeviceGetFanSpeed(nvidiaDevice, &nvidiaFanSpeed); - - if (response == NVML_ERROR_NOT_SUPPORTED) { - if (nvmlSuccess) - SPDLOG_ERROR("nvmlDeviceGetUtilizationRates failed"); - nvmlSuccess = false; - } - return nvmlSuccess; -} diff --git a/src/overlay.cpp b/src/overlay.cpp index 6f21dd6493..4976c02cc6 100644 --- a/src/overlay.cpp +++ b/src/overlay.cpp @@ -24,8 +24,6 @@ #include "iostats.h" #include "amdgpu.h" #include "fps_metrics.h" -#include "intel.h" -#include "msm.h" #include "net.h" #ifdef __linux__ @@ -128,20 +126,8 @@ void update_hw_info(const struct overlay_params& params, uint32_t vendorID) #endif } if (params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] || logger->is_active()) { - if (vendorID == 0x1002) - getAmdGpuInfo(); -#ifdef __linux__ - if (gpu_metrics_exists) - amdgpu_get_metrics(deviceID); -#endif - if (vendorID == 0x10de) - getNvidiaGpuInfo(params); -#ifdef __linux__ - if (vendorID== 0x8086) - if (intel) intel->update(); - if (vendorID == 0x5143) - if (msm) msm->update(); -#endif + if (HUDElements.gpus) + HUDElements.gpus->get_metrics(); } #ifdef __linux__ @@ -160,13 +146,14 @@ void update_hw_info(const struct overlay_params& params, uint32_t vendorID) if (params.enabled[OVERLAY_PARAM_ENABLED_io_read] || params.enabled[OVERLAY_PARAM_ENABLED_io_write]) getIoStats(g_io_stats); #endif - - currentLogData.gpu_load = gpu_info.load; - currentLogData.gpu_temp = gpu_info.temp; - currentLogData.gpu_core_clock = gpu_info.CoreClock; - currentLogData.gpu_mem_clock = gpu_info.MemClock; - currentLogData.gpu_vram_used = gpu_info.memoryUsed; - currentLogData.gpu_power = gpu_info.powerUsage; + if (HUDElements.gpus && HUDElements.gpus->active_gpu()) { + currentLogData.gpu_load = HUDElements.gpus->active_gpu()->metrics.load; + currentLogData.gpu_temp = HUDElements.gpus->active_gpu()->metrics.temp; + currentLogData.gpu_core_clock = HUDElements.gpus->active_gpu()->metrics.CoreClock; + currentLogData.gpu_mem_clock = HUDElements.gpus->active_gpu()->metrics.MemClock; + currentLogData.gpu_vram_used = HUDElements.gpus->active_gpu()->metrics.memoryUsed; + currentLogData.gpu_power = HUDElements.gpus->active_gpu()->metrics.powerUsage; + } #ifdef __linux__ currentLogData.ram_used = memused; currentLogData.swap_used = swapused; @@ -257,8 +244,8 @@ void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const stru frametime_data.erase(frametime_data.begin()); } #ifdef __linux__ - if (throttling) - throttling->update(); + if (HUDElements.gpus) + HUDElements.gpus->update_throttling(); #endif frametime = frametime_ms; fps = double(1000 / frametime_ms); @@ -765,216 +752,6 @@ struct pci_bus { int func; }; -void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_params& params) -{ - //if (!params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats]) - // return; - - pci_bus pci; - bool pci_bus_parsed = false; - const char *pci_dev = nullptr; - if (!params.pci_dev.empty()) - pci_dev = params.pci_dev.c_str(); - - // for now just checks if pci bus parses correctly, if at all necessary - if (pci_dev) { - if (sscanf(pci_dev, "%04x:%02x:%02x.%x", - &pci.domain, &pci.bus, - &pci.slot, &pci.func) == 4) { - pci_bus_parsed = true; - // reformat back to sysfs file name's and nvml's expected format - // so config file param's value format doesn't have to be as strict - std::stringstream ss; - ss << std::hex - << std::setw(4) << std::setfill('0') << pci.domain << ":" - << std::setw(2) << pci.bus << ":" - << std::setw(2) << pci.slot << "." - << std::setw(1) << pci.func; - params.pci_dev = ss.str(); - pci_dev = params.pci_dev.c_str(); - SPDLOG_DEBUG("PCI device ID: '{}'", pci_dev); - } else { - SPDLOG_ERROR("Failed to parse PCI device ID: '{}'", pci_dev); - SPDLOG_ERROR("Specify it as 'domain:bus:slot.func'"); - } - } - -#ifdef __linux__ - // NVIDIA - if (vendorID == 0x10de) - if(checkNvidia(pci_dev)) - vendorID = 0x10de; - - string path; - string drm = "/sys/class/drm/"; - - if (vendorID==0x8086){ - auto dirs = ls(drm.c_str(), "card"); - for (auto& dir : dirs) { - if (dir.find("-") != std::string::npos) - continue; // filter display adapters - - FILE *fp; - string device = path + "/device/device"; - if ((fp = fopen(device.c_str(), "r"))){ - uint32_t temp = 0; - if (fscanf(fp, "%x", &temp) == 1) { - if (temp != reported_deviceID){ - fclose(fp); - SPDLOG_DEBUG("DeviceID does not match vulkan report {:X}", reported_deviceID); - continue; - } - deviceID = temp; - } - fclose(fp); - } - - string vendor = path + "/device/vendor"; - if ((fp = fopen(vendor.c_str(), "r"))){ - uint32_t temp = 0; - if (fscanf(fp, "%x", &temp) != 1 || temp != 0x8086) { - fclose(fp); - continue; - } - fclose(fp); - } - path = drm + dir; - drm_dev = dir; - SPDLOG_DEBUG("Intel: using drm device {}", drm_dev); - intel = std::make_unique(); - break; - } - } - - if (vendorID == 0x5143) { - auto dirs = ls(drm.c_str(), "card"); - for (auto& dir : dirs) { - if (dir.find("-") != std::string::npos) { - continue; // filter display adapters - } - path = drm + dir; - drm_dev = dir; - SPDLOG_DEBUG("msm: using drm device {}", drm_dev); - msm = std::make_unique(); - } - } - - if (vendorID == 0x1002 - || gpu.find("Radeon") != std::string::npos - || gpu.find("AMD") != std::string::npos) { - string path; - string drm = "/sys/class/drm/"; - - auto dirs = ls(drm.c_str(), "card"); - for (auto& dir : dirs) { - if (dir.find("-") != std::string::npos) { - continue; // filter display adapters - } - path = drm + dir; - - SPDLOG_DEBUG("drm path check: {}", path); - if (pci_bus_parsed && pci_dev) { - string pci_device = read_symlink((path + "/device").c_str()); - SPDLOG_DEBUG("PCI device symlink: '{}'", pci_device); - if (!ends_with(pci_device, pci_dev)) { - SPDLOG_DEBUG("skipping GPU, no PCI ID match"); - continue; - } - } - - FILE *fp; - string device = path + "/device/device"; - if ((fp = fopen(device.c_str(), "r"))){ - uint32_t temp = 0; - if (fscanf(fp, "%x", &temp) == 1) { - if (!pci_bus_parsed && reported_deviceID && temp != reported_deviceID){ - fclose(fp); - SPDLOG_DEBUG("DeviceID does not match vulkan report {:X}", reported_deviceID); - continue; - } - deviceID = temp; - } - fclose(fp); - } - - string vendor = path + "/device/vendor"; - if ((fp = fopen(vendor.c_str(), "r"))){ - uint32_t temp = 0; - if (fscanf(fp, "%x", &temp) != 1 || temp != 0x1002) { - fclose(fp); - continue; - } - fclose(fp); - } - - const std::string device_path = path + "/device"; - const std::string gpu_metrics_path = device_path + "/gpu_metrics"; - if (amdgpu_verify_metrics(gpu_metrics_path)) { - gpu_info.fan_rpm = true; - gpu_metrics_exists = true; - metrics_path = gpu_metrics_path; - throttling = std::make_unique(); - SPDLOG_DEBUG("Using gpu_metrics of {}", gpu_metrics_path); - } - - if (!amdgpu.vram_total) - amdgpu.vram_total = fopen((device_path + "/mem_info_vram_total").c_str(), "r"); - if (!amdgpu.vram_used) - amdgpu.vram_used = fopen((device_path + "/mem_info_vram_used").c_str(), "r"); - if (!amdgpu.gtt_used) - amdgpu.gtt_used = fopen((device_path + "/mem_info_gtt_used").c_str(), "r"); - - const std::string hwmon_path = device_path + "/hwmon/"; - if (fs::exists(hwmon_path)){ - const auto dirs = ls(hwmon_path.c_str(), "hwmon", LS_DIRS); - for (const auto& dir : dirs) { - if (!amdgpu.temp) - amdgpu.temp = fopen((hwmon_path + dir + "/temp1_input").c_str(), "r"); - if (!amdgpu.junction_temp) - amdgpu.junction_temp = fopen((hwmon_path + dir + "/temp2_input").c_str(), "r"); - if (!amdgpu.memory_temp) - amdgpu.memory_temp = fopen((hwmon_path + dir + "/temp3_input").c_str(), "r"); - if (!amdgpu.core_clock) - amdgpu.core_clock = fopen((hwmon_path + dir + "/freq1_input").c_str(), "r"); - if (!amdgpu.gpu_voltage_soc) - amdgpu.gpu_voltage_soc = fopen((hwmon_path + dir + "/in0_input").c_str(), "r"); - } - - if (!metrics_path.empty()) - break; - - // The card output nodes - cardX-output, will point to the card node - // As such the actual metrics nodes will be missing. - amdgpu.busy = fopen((device_path + "/gpu_busy_percent").c_str(), "r"); - if (!amdgpu.busy) - continue; - - SPDLOG_DEBUG("using amdgpu path: {}", device_path); - - for (const auto& dir : dirs) { - if (!amdgpu.memory_clock) - amdgpu.memory_clock = fopen((hwmon_path + dir + "/freq2_input").c_str(), "r"); - if (!amdgpu.power_usage) - amdgpu.power_usage = fopen((hwmon_path + dir + "/power1_average").c_str(), "r"); - if (!amdgpu.power_usage) - amdgpu.power_usage = fopen((hwmon_path + dir + "/power1_input").c_str(), "r"); - if (!amdgpu.fan) - amdgpu.fan = fopen((hwmon_path + dir + "/fan1_input").c_str(), "r"); - } - } - break; - } - - // don't bother then - if (metrics_path.empty() && !amdgpu.busy && vendorID != 0x8086) { - params.enabled[OVERLAY_PARAM_ENABLED_gpu_stats] = false; - } - } -#endif - if (!params.permit_upload) - SPDLOG_DEBUG("Uploading is disabled (permit_upload = 0)"); -} - void init_system_info(){ #ifdef __linux__ const char* ld_preload = getenv("LD_PRELOAD"); diff --git a/src/overlay.h b/src/overlay.h index 8b53285c09..f97a6c8bf2 100644 --- a/src/overlay.h +++ b/src/overlay.h @@ -101,7 +101,6 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& void update_hud_info(struct swapchain_stats& sw_stats, const struct overlay_params& params, uint32_t vendorID); void update_hud_info_with_frametime(struct swapchain_stats& sw_stats, const struct overlay_params& params, uint32_t vendorID, uint64_t frametime_ns); void update_hw_info(const struct overlay_params& params, uint32_t vendorID); -void init_gpu_stats(uint32_t& vendorID, uint32_t reported_deviceID, overlay_params& params); void init_cpu_stats(overlay_params& params); void check_keybinds(overlay_params& params, uint32_t vendorID); void init_system_info(void); diff --git a/src/overlay_params.h b/src/overlay_params.h index 1ed33ed046..8378b4238b 100644 --- a/src/overlay_params.h +++ b/src/overlay_params.h @@ -115,6 +115,7 @@ typedef unsigned long KeySym; OVERLAY_PARAM_BOOL(present_mode) \ OVERLAY_PARAM_BOOL(time_no_label) \ OVERLAY_PARAM_BOOL(display_server) \ + OVERLAY_PARAM_BOOL(active_gpu) \ OVERLAY_PARAM_CUSTOM(fps_sampling_period) \ OVERLAY_PARAM_CUSTOM(output_folder) \ OVERLAY_PARAM_CUSTOM(output_file) \ diff --git a/src/vulkan.cpp b/src/vulkan.cpp index 2db5310029..e9f896cd03 100644 --- a/src/vulkan.cpp +++ b/src/vulkan.cpp @@ -1859,7 +1859,6 @@ static VkResult overlay_CreateDevice( gpu = device_data->properties.deviceName; SPDLOG_DEBUG("gpu: {}", gpu); #endif - init_gpu_stats(device_data->properties.vendorID, device_data->properties.deviceID, device_data->instance->params); } return result;