#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
use crate::ported::crt::{ColorElements, ColorScheme};
use crate::ported::meter::Meter;
use crate::ported::richstring::{
RichString, RichString_appendAscii, RichString_appendnAscii, RichString_writeAscii,
};
pub struct GPUMeterEngineData {
pub key: Option<String>,
pub timeDiff: u64,
pub percentage: f64,
}
pub static GPUMeter_engineData: Mutex<[GPUMeterEngineData; 4]> = Mutex::new([
GPUMeterEngineData {
key: None,
timeDiff: 0,
percentage: 0.0,
},
GPUMeterEngineData {
key: None,
timeDiff: 0,
percentage: 0.0,
},
GPUMeterEngineData {
key: None,
timeDiff: 0,
percentage: 0.0,
},
GPUMeterEngineData {
key: None,
timeDiff: 0,
percentage: 0.0,
},
]);
static totalUsage: Mutex<f64> = Mutex::new(f64::NAN);
static totalGPUTimeDiff: Mutex<u64> = Mutex::new(u64::MAX);
static ACTIVE_METERS: AtomicUsize = AtomicUsize::new(0);
pub fn GPUMeter_active() -> bool {
ACTIVE_METERS.load(Ordering::Relaxed) > 0
}
pub fn humanTimeUnit(mut value: u64) -> String {
if value < 1000 {
return format!("{:3}ns", value);
}
if value < 10000 {
return format!("{:1}.{:1}us", value / 1000, (value % 1000) / 100);
}
value /= 1000;
if value < 1000 {
return format!("{:3}us", value);
}
if value < 10000 {
return format!("{:1}.{:1}ms", value / 1000, (value % 1000) / 100);
}
value /= 1000;
if value < 1000 {
return format!("{:3}ms", value);
}
if value < 10000 {
return format!("{:1}.{:1}s", value / 1000, (value % 1000) / 100);
}
value /= 1000;
if value < 600 {
return format!("{:3}s", value);
}
value /= 60;
if value < 600 {
return format!("{:3}m", value);
}
value /= 60;
if value < 96 {
return format!("{:3}h", value);
}
value /= 24;
format!("{:3}d", value)
}
pub fn GPUMeter_updateValues(this: &mut Meter) {
let mut tu = totalUsage.lock().unwrap();
let mut td = totalGPUTimeDiff.lock().unwrap();
crate::ported::linux::platform::Platform_setGPUValues(this, &mut tu, &mut td);
if !(*tu >= 0.0) {
this.txtBuffer = "N/A".to_string();
return;
}
this.txtBuffer = format!("{:.1}%", *tu);
}
pub fn GPUMeter_display(this: &Meter, out: &mut RichString) {
let scheme = ColorScheme::active();
let meter_text = ColorElements::METER_TEXT.packed(scheme);
let meter_value = ColorElements::METER_VALUE.packed(scheme);
RichString_writeAscii(out, meter_text, b":");
let total_usage = *totalUsage.lock().unwrap();
if !(total_usage >= 0.0) {
RichString_appendAscii(out, meter_value, b" N/A");
return;
}
let buffer = format!("{:5.1}%", total_usage);
RichString_appendnAscii(out, meter_value, buffer.as_bytes(), buffer.len());
let total_time_diff = *totalGPUTimeDiff.lock().unwrap();
if total_time_diff != u64::MAX {
RichString_appendAscii(out, meter_text, b"(");
let buffer = humanTimeUnit(total_time_diff);
RichString_appendnAscii(out, meter_value, buffer.as_bytes(), buffer.len());
RichString_appendAscii(out, meter_text, b")");
}
let engine_data = GPUMeter_engineData.lock().unwrap();
for i in 0..engine_data.len() {
let key = match &engine_data[i].key {
Some(k) => k,
None => break,
};
RichString_appendAscii(out, meter_text, b" ");
RichString_appendAscii(out, meter_text, key.as_bytes());
RichString_appendAscii(out, meter_text, b":");
if this.values[i] >= 0.0 {
let buffer = format!("{:5.1}%", this.values[i]);
RichString_appendnAscii(out, meter_value, buffer.as_bytes(), buffer.len());
} else {
RichString_appendAscii(out, meter_value, b" N/A");
}
if engine_data[i].timeDiff != u64::MAX {
RichString_appendAscii(out, meter_text, b"(");
let buffer = humanTimeUnit(engine_data[i].timeDiff);
RichString_appendnAscii(out, meter_value, buffer.as_bytes(), buffer.len());
RichString_appendAscii(out, meter_text, b")");
}
}
}
pub fn GPUMeter_init() {
ACTIVE_METERS.fetch_add(1, Ordering::Relaxed);
}
pub fn GPUMeter_done() {
assert!(ACTIVE_METERS.load(Ordering::Relaxed) > 0);
ACTIVE_METERS.fetch_sub(1, Ordering::Relaxed);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn human_time_unit_nanoseconds_width3() {
assert_eq!(humanTimeUnit(0), " 0ns");
assert_eq!(humanTimeUnit(7), " 7ns");
assert_eq!(humanTimeUnit(999), "999ns");
}
#[test]
fn human_time_unit_sub_microsecond_fraction() {
assert_eq!(humanTimeUnit(1000), "1.0us");
assert_eq!(humanTimeUnit(1500), "1.5us");
assert_eq!(humanTimeUnit(9999), "9.9us");
}
#[test]
fn human_time_unit_microseconds_width3() {
assert_eq!(humanTimeUnit(10_000), " 10us");
assert_eq!(humanTimeUnit(999_000), "999us");
}
#[test]
fn human_time_unit_millisecond_fraction_and_width() {
assert_eq!(humanTimeUnit(1_000_000), "1.0ms");
assert_eq!(humanTimeUnit(10_000_000), " 10ms");
}
#[test]
fn human_time_unit_second_fraction() {
assert_eq!(humanTimeUnit(1_000_000_000), "1.0s");
assert_eq!(humanTimeUnit(5_000_000_000), "5.0s");
}
#[test]
fn human_time_unit_seconds_minutes_hours_days() {
assert_eq!(humanTimeUnit(60_000_000_000), " 60s");
assert_eq!(humanTimeUnit(600_000_000_000), " 10m");
assert_eq!(humanTimeUnit(36_000_000_000_000), " 10h");
assert_eq!(humanTimeUnit(360_000_000_000_000), " 4d");
}
fn text(r: &RichString) -> String {
(0..r.chlen as usize).map(|i| r.chptr[i].chars).collect()
}
#[test]
fn display_na_when_total_usage_unavailable() {
let m = Meter {
host: core::ptr::null(),
values: vec![0.0; 5],
..Meter::empty()
};
let mut out = RichString::new();
GPUMeter_display(&m, &mut out);
assert_eq!(text(&out), ": N/A");
}
#[test]
fn active_meter_counter_lifecycle() {
assert!(!GPUMeter_active());
GPUMeter_init();
assert!(GPUMeter_active());
GPUMeter_init();
assert!(GPUMeter_active());
GPUMeter_done();
assert!(GPUMeter_active());
GPUMeter_done();
assert!(!GPUMeter_active());
}
#[test]
fn update_values_computes_usage_percentage() {
use crate::ported::linux::linuxmachine::LinuxMachine;
use crate::ported::machine::Machine;
let host = Box::leak(Box::new(LinuxMachine {
super_: Machine {
monotonicMs: 2000,
..Default::default()
},
curGpuTime: 1_000_000_000,
prevGpuTime: 0,
gpuEngineData: None,
..Default::default()
}));
let mut m = Meter {
values: vec![0.0; 5],
host: &host.super_ as *const crate::ported::machine::Machine,
..Meter::empty()
};
GPUMeter_updateValues(&mut m);
assert_eq!(m.txtBuffer, "50.0%");
assert_eq!(m.curItems, 5);
}
}