|
|
|
@ -3,9 +3,38 @@
|
|
|
|
|
#include "amdgpu.h"
|
|
|
|
|
#include "gpu.h"
|
|
|
|
|
#include "cpu.h"
|
|
|
|
|
#include "overlay.h"
|
|
|
|
|
|
|
|
|
|
#define METRICS_UPDATE_PERIOD_MS 500
|
|
|
|
|
#define METRICS_POLLING_PERIOD_MS 5
|
|
|
|
|
#define METRICS_SAMPLE_COUNT (METRICS_UPDATE_PERIOD_MS/METRICS_POLLING_PERIOD_MS)
|
|
|
|
|
|
|
|
|
|
std::string metrics_path = "";
|
|
|
|
|
|
|
|
|
|
/* This structure is used to communicate the latest values of the amdgpu metrics.
|
|
|
|
|
* The direction of communication is amdgpu_polling_thread -> amdgpu_get_metrics().
|
|
|
|
|
*/
|
|
|
|
|
struct amdgpu_common_metrics {
|
|
|
|
|
/* Load level: averaged across the sampling period */
|
|
|
|
|
uint8_t gpu_load_percent;
|
|
|
|
|
// uint8_t mem_load_percent;
|
|
|
|
|
|
|
|
|
|
/* Power usage: averaged across the sampling period */
|
|
|
|
|
float average_gfx_power_w;
|
|
|
|
|
float average_cpu_power_w;
|
|
|
|
|
|
|
|
|
|
/* Clocks: latest value of the clock */
|
|
|
|
|
uint16_t current_gfxclk_mhz;
|
|
|
|
|
uint16_t current_uclk_mhz;
|
|
|
|
|
|
|
|
|
|
/* Temperatures: maximum values over the sampling period */
|
|
|
|
|
uint16_t soc_temp_c;
|
|
|
|
|
uint16_t gpu_temp_c;
|
|
|
|
|
uint16_t apu_cpu_temp_c;
|
|
|
|
|
} amdgpu_common_metrics;
|
|
|
|
|
|
|
|
|
|
std::mutex amdgpu_common_metrics_m;
|
|
|
|
|
|
|
|
|
|
bool amdgpu_check_metrics(const std::string& path)
|
|
|
|
|
{
|
|
|
|
|
metrics_table_header header {};
|
|
|
|
@ -33,83 +62,113 @@ bool amdgpu_check_metrics(const std::string& path)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void calculate_gpu_load(){
|
|
|
|
|
int gpu_load = 0;
|
|
|
|
|
int i = 0;
|
|
|
|
|
struct metrics_table_header header;
|
|
|
|
|
std::ifstream in(metrics_path, std::ios_base::in | std::ios_base::binary);
|
|
|
|
|
in.read((char*)&header, sizeof(header));
|
|
|
|
|
if (header.format_revision == 1){
|
|
|
|
|
struct gpu_metrics_v1_3 amdgpu_metrics;
|
|
|
|
|
while (i < 100){
|
|
|
|
|
in.clear();
|
|
|
|
|
in.seekg(0);
|
|
|
|
|
in.read((char*)&amdgpu_metrics, sizeof(amdgpu_metrics));
|
|
|
|
|
if (amdgpu_metrics.average_gfx_activity > 100)
|
|
|
|
|
gpu_load += amdgpu_metrics.average_gfx_activity / 100;
|
|
|
|
|
else
|
|
|
|
|
gpu_load += amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
sleep(0.005);
|
|
|
|
|
}
|
|
|
|
|
} else if (header.format_revision == 2){
|
|
|
|
|
struct gpu_metrics_v2_2 amdgpu_metrics;
|
|
|
|
|
while (i < 100){
|
|
|
|
|
in.clear();
|
|
|
|
|
in.seekg(0);
|
|
|
|
|
in.read((char*)&amdgpu_metrics, sizeof(amdgpu_metrics));
|
|
|
|
|
if (amdgpu_metrics.average_gfx_activity > 100)
|
|
|
|
|
gpu_load += amdgpu_metrics.average_gfx_activity / 100;
|
|
|
|
|
else
|
|
|
|
|
gpu_load += amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
|
|
|
|
|
i++;
|
|
|
|
|
sleep(0.005);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
gpu_info.load = gpu_load / 100;
|
|
|
|
|
}
|
|
|
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
|
|
|
void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) {
|
|
|
|
|
// Set all the fields to 0 by default
|
|
|
|
|
memset(metrics, 0, sizeof(struct amdgpu_common_metrics));
|
|
|
|
|
|
|
|
|
|
void amdgpu_get_metrics()
|
|
|
|
|
{
|
|
|
|
|
if (!metrics_path.empty()){
|
|
|
|
|
struct metrics_table_header header;
|
|
|
|
|
std::ifstream in(metrics_path, std::ios_base::in | std::ios_base::binary);
|
|
|
|
|
in.read((char*)&header, sizeof(header));
|
|
|
|
|
if (header.format_revision == 1){
|
|
|
|
|
cpuStats.cpu_type = "CPU";
|
|
|
|
|
if (header.format_revision == 1) {
|
|
|
|
|
// Desktop GPUs
|
|
|
|
|
struct gpu_metrics_v1_3 amdgpu_metrics;
|
|
|
|
|
in.clear();
|
|
|
|
|
in.seekg(0);
|
|
|
|
|
in.read((char*)&amdgpu_metrics, sizeof(amdgpu_metrics));
|
|
|
|
|
// gpu_info.load = amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
std::thread(calculate_gpu_load).detach();
|
|
|
|
|
gpu_info.CoreClock = amdgpu_metrics.average_gfxclk_frequency;
|
|
|
|
|
gpu_info.powerUsage = amdgpu_metrics.average_socket_power;
|
|
|
|
|
gpu_info.temp = amdgpu_metrics.temperature_edge;
|
|
|
|
|
gpu_info.MemClock = amdgpu_metrics.current_uclk;
|
|
|
|
|
} else if (header.format_revision == 2){
|
|
|
|
|
|
|
|
|
|
metrics->gpu_load_percent = amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
|
|
|
|
|
metrics->average_gfx_power_w = amdgpu_metrics.average_socket_power;
|
|
|
|
|
|
|
|
|
|
metrics->current_gfxclk_mhz = amdgpu_metrics.average_gfxclk_frequency;
|
|
|
|
|
metrics->current_uclk_mhz = amdgpu_metrics.current_uclk;
|
|
|
|
|
|
|
|
|
|
metrics->gpu_temp_c = amdgpu_metrics.temperature_edge;
|
|
|
|
|
} else if (header.format_revision == 2) {
|
|
|
|
|
// APUs
|
|
|
|
|
cpuStats.cpu_type = "APU";
|
|
|
|
|
struct gpu_metrics_v2_2 amdgpu_metrics;
|
|
|
|
|
in.clear();
|
|
|
|
|
in.seekg(0);
|
|
|
|
|
in.read((char*)&amdgpu_metrics, sizeof(amdgpu_metrics));
|
|
|
|
|
// gpu_info.load = amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
std::thread(calculate_gpu_load).detach();
|
|
|
|
|
gpu_info.CoreClock = amdgpu_metrics.current_gfxclk;
|
|
|
|
|
gpu_info.powerUsage = amdgpu_metrics.average_gfx_power / 1000.f;
|
|
|
|
|
gpu_info.temp = amdgpu_metrics.temperature_gfx / 100;
|
|
|
|
|
gpu_info.MemClock = amdgpu_metrics.current_uclk;
|
|
|
|
|
gpu_info.apu_cpu_power = amdgpu_metrics.average_cpu_power / 1000.f;
|
|
|
|
|
|
|
|
|
|
metrics->gpu_load_percent = amdgpu_metrics.average_gfx_activity;
|
|
|
|
|
|
|
|
|
|
metrics->average_gfx_power_w = amdgpu_metrics.average_gfx_power / 1000.f;
|
|
|
|
|
metrics->average_cpu_power_w = amdgpu_metrics.average_cpu_power / 1000.f;
|
|
|
|
|
|
|
|
|
|
metrics->current_gfxclk_mhz = amdgpu_metrics.current_gfxclk;
|
|
|
|
|
metrics->current_uclk_mhz = amdgpu_metrics.current_uclk;
|
|
|
|
|
|
|
|
|
|
metrics->soc_temp_c = amdgpu_metrics.temperature_soc / 100;
|
|
|
|
|
metrics->gpu_temp_c = amdgpu_metrics.temperature_gfx / 100;
|
|
|
|
|
int cpu_temp = 0;
|
|
|
|
|
for (unsigned i = 0; i < cpuStats.GetCPUData().size() / 2; i++)
|
|
|
|
|
if (amdgpu_metrics.temperature_core[i] > cpu_temp)
|
|
|
|
|
cpu_temp = amdgpu_metrics.temperature_core[i];
|
|
|
|
|
for (unsigned i = 0; i < 8; i++)
|
|
|
|
|
cpu_temp = MAX(cpu_temp, amdgpu_metrics.temperature_core[i]);
|
|
|
|
|
metrics->apu_cpu_temp_c = cpu_temp / 100;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#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_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)
|
|
|
|
|
#define UPDATE_METRIC_LAST(FIELD) do { amdgpu_common_metrics.FIELD = metrics_buffer[METRICS_SAMPLE_COUNT - 1].FIELD; } while(0)
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
gpu_info.apu_cpu_temp = cpu_temp / 100;
|
|
|
|
|
while (1) {
|
|
|
|
|
// 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]);
|
|
|
|
|
|
|
|
|
|
// 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){
|
|
|
|
|
gpu_load_needs_dividing = true;
|
|
|
|
|
metrics_buffer[cur_sample_id].gpu_load_percent /= 100;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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(average_gfx_power_w);
|
|
|
|
|
UPDATE_METRIC_AVERAGE(average_cpu_power_w);
|
|
|
|
|
|
|
|
|
|
UPDATE_METRIC_LAST(current_gfxclk_mhz);
|
|
|
|
|
UPDATE_METRIC_LAST(current_uclk_mhz);
|
|
|
|
|
|
|
|
|
|
UPDATE_METRIC_MAX(soc_temp_c);
|
|
|
|
|
UPDATE_METRIC_MAX(gpu_temp_c);
|
|
|
|
|
UPDATE_METRIC_MAX(apu_cpu_temp_c);
|
|
|
|
|
amdgpu_common_metrics_m.unlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void amdgpu_get_metrics(){
|
|
|
|
|
static bool init = false;
|
|
|
|
|
if (!init){
|
|
|
|
|
std::thread(amdgpu_metrics_polling_thread).detach();
|
|
|
|
|
init = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
amdgpu_common_metrics_m.lock();
|
|
|
|
|
gpu_info.load = amdgpu_common_metrics.gpu_load_percent;
|
|
|
|
|
|
|
|
|
|
gpu_info.powerUsage = amdgpu_common_metrics.average_gfx_power_w;
|
|
|
|
|
|
|
|
|
|
gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz;
|
|
|
|
|
gpu_info.MemClock = amdgpu_common_metrics.current_uclk_mhz;
|
|
|
|
|
|
|
|
|
|
gpu_info.temp = amdgpu_common_metrics.gpu_temp_c;
|
|
|
|
|
gpu_info.apu_cpu_power = amdgpu_common_metrics.apu_cpu_temp_c;
|
|
|
|
|
amdgpu_common_metrics_m.unlock();
|
|
|
|
|
}
|
|
|
|
|