mod amd;
mod apple;
mod intel;
#[cfg(target_os = "linux")]
mod linux;
mod mock;
mod nvidia;
mod replay;
mod windows;
pub use mock::MockBackend;
use anyhow::Result;
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct GpuSnapshot {
pub name: String,
pub integrated: bool,
pub utilization_pct: f64,
pub mem_util_pct: Option<f64>,
pub video_util_pct: Option<f64>,
pub enc_util_pct: Option<f64>,
pub dec_util_pct: Option<f64>,
pub throttle: Option<String>,
pub vram_used_bytes: u64,
pub vram_total_bytes: u64,
pub temperature_c: Option<f64>,
pub temp_junction_c: Option<f64>,
pub temp_mem_c: Option<f64>,
pub power_w: Option<f64>,
pub power_limit_w: Option<f64>,
pub fan_pct: Option<f64>,
pub fan_rpm: Option<u64>,
pub clock_mhz: Option<u64>,
pub mem_clock_mhz: Option<u64>,
pub pcie_gen: Option<u8>,
pub pcie_width: Option<u32>,
pub pcie_max_gen: Option<u8>,
pub pcie_max_width: Option<u32>,
pub pcie_rx_kbs: Option<u64>,
pub pcie_tx_kbs: Option<u64>,
pub gtt_used_bytes: Option<u64>,
pub gtt_total_bytes: Option<u64>,
pub volt_mv: Option<u64>,
pub perf_level: Option<String>,
}
impl GpuSnapshot {
pub fn vram_pct(&self) -> f64 {
if self.vram_total_bytes == 0 {
return 0.0;
}
self.vram_used_bytes as f64 / self.vram_total_bytes as f64 * 100.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, serde::Serialize, serde::Deserialize)]
pub enum ProcKind {
Graphics,
#[default]
Compute,
}
impl ProcKind {
pub fn label(&self) -> &'static str {
match self {
ProcKind::Graphics => "Graphic",
ProcKind::Compute => "Compute",
}
}
}
#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
#[serde(default)]
pub struct GpuProcess {
pub pid: u32,
pub gpu_index: usize,
pub kind: ProcKind,
pub gpu_util_pct: Option<f64>,
pub gpu_mem_bytes: u64,
pub user: Option<String>,
pub command: Option<String>,
pub cpu_pct: Option<f32>,
pub host_mem_bytes: Option<u64>,
}
pub trait GpuBackend {
fn name(&self) -> &'static str;
fn poll(&mut self) -> Result<Vec<GpuSnapshot>>;
fn processes(&mut self) -> Vec<GpuProcess> {
Vec::new()
}
fn driver_info(&self) -> Option<String> {
None
}
}
pub fn detect(
mock: Option<usize>,
replay: Option<&std::path::Path>,
) -> Result<Box<dyn GpuBackend>> {
if let Some(path) = replay {
return replay::load(path);
}
if let Some(n) = mock {
return Ok(Box::new(MockBackend::new(n.clamp(1, 16))));
}
if let Some(b) = nvidia::probe() {
return Ok(b);
}
if let Some(b) = amd::probe() {
return Ok(b);
}
if let Some(b) = intel::probe() {
return Ok(b);
}
if let Some(b) = apple::probe() {
return Ok(b);
}
if let Some(b) = windows::probe() {
return Ok(b);
}
anyhow::bail!("no supported GPU backend found (run with --mock to demo the UI)")
}