1use std::time::Instant;
2
3#[derive(Debug, Clone)]
4pub struct GpuInfo {
5 pub id: usize,
6 pub backend_index: u32,
7 pub name: String,
8 pub uuid: Option<String>,
9}
10
11#[derive(Debug, Clone)]
12pub struct GpuSample {
13 pub gpu_id: usize,
14 pub at: Instant,
15 pub gpu_util_percent: Option<f64>,
16 pub mem_util_percent: Option<f64>,
17 pub vram_used_bytes: Option<u64>,
18 pub vram_total_bytes: Option<u64>,
19 pub power_watts: Option<f64>,
20 pub power_limit_watts: Option<f64>,
21 pub temperature_celsius: Option<f64>,
22 pub fan_percent: Option<f64>,
23 pub graphics_clock_mhz: Option<f64>,
24 pub memory_clock_mhz: Option<f64>,
25 pub compute_processes: Option<u32>,
26 pub processes: Vec<GpuProcess>,
27}
28
29impl GpuSample {
30 pub fn vram_used_percent(&self) -> Option<f64> {
31 let used = self.vram_used_bytes? as f64;
32 let total = self.vram_total_bytes? as f64;
33 (total > 0.0).then_some((used / total) * 100.0)
34 }
35}
36
37#[derive(Debug, Clone, Copy, Eq, PartialEq)]
38pub enum GpuProcessKind {
39 Compute,
40 Graphics,
41 Mps,
42}
43
44impl GpuProcessKind {
45 pub fn label(self) -> &'static str {
46 match self {
47 Self::Compute => "C",
48 Self::Graphics => "G",
49 Self::Mps => "MPS",
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
55pub struct GpuProcess {
56 pub pid: u32,
57 pub user: Option<String>,
58 pub command: Option<String>,
59 pub kinds: Vec<GpuProcessKind>,
60 pub used_gpu_memory_bytes: Option<u64>,
61 pub gpu_instance_id: Option<u32>,
62 pub compute_instance_id: Option<u32>,
63}
64
65impl GpuProcess {
66 pub fn kind_label(&self) -> String {
67 self.kinds
68 .iter()
69 .map(|kind| kind.label())
70 .collect::<Vec<_>>()
71 .join("+")
72 }
73}
74
75#[derive(Debug, Clone, Copy, Eq, PartialEq)]
76pub enum MetricKind {
77 GpuUtil,
78 MemUtil,
79 VramUsed,
80 Power,
81 Temperature,
82 Fan,
83}
84
85impl MetricKind {
86 pub const ALL: [Self; 6] = [
87 Self::GpuUtil,
88 Self::MemUtil,
89 Self::VramUsed,
90 Self::Power,
91 Self::Temperature,
92 Self::Fan,
93 ];
94
95 pub fn title(self) -> &'static str {
96 match self {
97 Self::GpuUtil => "GPU",
98 Self::MemUtil => "MEM",
99 Self::VramUsed => "VRAM",
100 Self::Power => "POWER",
101 Self::Temperature => "TEMP",
102 Self::Fan => "FAN",
103 }
104 }
105
106 pub fn unit(self) -> &'static str {
107 match self {
108 Self::GpuUtil | Self::MemUtil | Self::VramUsed | Self::Fan => "%",
109 Self::Power => "W",
110 Self::Temperature => "C",
111 }
112 }
113
114 pub fn value(self, sample: &GpuSample) -> Option<f64> {
115 match self {
116 Self::GpuUtil => sample.gpu_util_percent,
117 Self::MemUtil => sample.mem_util_percent,
118 Self::VramUsed => sample.vram_used_percent(),
119 Self::Power => sample.power_watts,
120 Self::Temperature => sample.temperature_celsius,
121 Self::Fan => sample.fan_percent,
122 }
123 }
124
125 pub fn fixed_range(self, latest: Option<&GpuSample>) -> Option<(f64, f64)> {
126 match self {
127 Self::GpuUtil | Self::MemUtil | Self::VramUsed | Self::Fan => Some((0.0, 100.0)),
128 Self::Temperature => Some((0.0, 100.0)),
129 Self::Power => latest
130 .and_then(|sample| sample.power_limit_watts)
131 .map(|limit| (0.0, limit.max(1.0))),
132 }
133 }
134}