khora_infra/telemetry/
gpu_monitor.rs1use std::borrow::Cow;
18use std::sync::Mutex;
19
20use khora_core::renderer::api::RenderStats;
21use khora_core::renderer::GpuHook;
22use khora_core::telemetry::monitoring::{
23 GpuReport, MonitoredResourceType, ResourceMonitor, ResourceUsageReport,
24};
25
26#[derive(Debug)]
28pub struct GpuMonitor {
29 system_name: String,
30 last_frame_stats: Mutex<Option<GpuReport>>,
31}
32
33impl GpuMonitor {
34 pub fn new(system_name: String) -> Self {
36 Self {
37 system_name,
38 last_frame_stats: Mutex::new(None),
39 }
40 }
41
42 pub fn get_gpu_report(&self) -> Option<GpuReport> {
44 *self.last_frame_stats.lock().unwrap()
45 }
46
47 pub fn update_from_frame_stats(&self, render_stats: &RenderStats) {
49 let frame_start_us = 0u32; let frame_end_us = (render_stats.gpu_frame_total_time_ms * 1000.0) as u32;
53 let main_pass_duration_us = (render_stats.gpu_main_pass_time_ms * 1000.0) as u32;
54
55 let main_pass_begin_us = (frame_end_us - main_pass_duration_us) / 2;
57 let main_pass_end_us = main_pass_begin_us + main_pass_duration_us;
58
59 let mut hook_timings = [None; 4];
60 hook_timings[GpuHook::FrameStart as usize] = Some(frame_start_us);
61 hook_timings[GpuHook::MainPassBegin as usize] = Some(main_pass_begin_us);
62 hook_timings[GpuHook::MainPassEnd as usize] = Some(main_pass_end_us);
63 hook_timings[GpuHook::FrameEnd as usize] = Some(frame_end_us);
64
65 let report = GpuReport {
66 frame_number: render_stats.frame_number,
67 hook_timings_us: hook_timings,
68 cpu_preparation_time_us: Some((render_stats.cpu_preparation_time_ms * 1000.0) as u32),
70 cpu_submission_time_us: Some(
71 (render_stats.cpu_render_submission_time_ms * 1000.0) as u32,
72 ),
73 };
74
75 let mut last_stats = self.last_frame_stats.lock().unwrap();
76 *last_stats = Some(report);
77 }
78}
79
80impl ResourceMonitor for GpuMonitor {
81 fn monitor_id(&self) -> Cow<'static, str> {
82 Cow::Owned(format!("Gpu_{}", self.system_name))
83 }
84
85 fn resource_type(&self) -> MonitoredResourceType {
86 MonitoredResourceType::Gpu
87 }
88
89 fn get_usage_report(&self) -> ResourceUsageReport {
90 ResourceUsageReport::default()
92 }
93
94 fn as_any(&self) -> &dyn std::any::Any {
95 self
96 }
97
98 fn update(&self) {
99 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn gpu_monitor_creation() {
110 let monitor = GpuMonitor::new("TestGPU".to_string());
111 assert_eq!(monitor.monitor_id(), "Gpu_TestGPU");
112 assert_eq!(monitor.resource_type(), MonitoredResourceType::Gpu);
113 }
114
115 #[test]
116 fn gpu_monitor_update_stats() {
117 let monitor = GpuMonitor::new("TestGPU".to_string());
118
119 assert!(monitor.get_gpu_report().is_none());
121
122 let render_stats = RenderStats {
124 frame_number: 42,
125 cpu_preparation_time_ms: 1.0,
126 cpu_render_submission_time_ms: 0.5,
127 gpu_main_pass_time_ms: 16.67,
128 gpu_frame_total_time_ms: 16.67,
129 draw_calls: 100,
130 triangles_rendered: 1000,
131 vram_usage_estimate_mb: 256.0,
132 };
133
134 monitor.update_from_frame_stats(&render_stats);
136
137 let report = monitor.get_gpu_report();
139 assert!(report.is_some());
140
141 let report = report.unwrap();
142 assert_eq!(report.frame_number, 42);
143 assert_eq!(report.cpu_preparation_time_us, Some(1000)); assert_eq!(report.cpu_submission_time_us, Some(500)); }
146
147 #[test]
148 fn gpu_report_hook_methods() {
149 let monitor = GpuMonitor::new("TestGPU".to_string());
150
151 let render_stats = RenderStats {
152 frame_number: 1,
153 cpu_preparation_time_ms: 0.1,
154 cpu_render_submission_time_ms: 0.05,
155 gpu_main_pass_time_ms: 16.67,
156 gpu_frame_total_time_ms: 17.0,
157 draw_calls: 50,
158 triangles_rendered: 500,
159 vram_usage_estimate_mb: 128.0,
160 };
161
162 monitor.update_from_frame_stats(&render_stats);
163 let report = monitor.get_gpu_report().unwrap();
164
165 assert_eq!(report.frame_number, 1);
167
168 assert_eq!(report.frame_total_duration_us(), Some(17000)); assert_eq!(report.main_pass_duration_us(), Some(16670)); }
172
173 #[test]
174 fn gpu_report_missing_data() {
175 let monitor = GpuMonitor::new("TestGPU".to_string());
176
177 let render_stats = RenderStats {
178 frame_number: 1,
179 cpu_preparation_time_ms: 0.0,
180 cpu_render_submission_time_ms: 0.0,
181 gpu_main_pass_time_ms: 0.0,
182 gpu_frame_total_time_ms: 0.0,
183 draw_calls: 0,
184 triangles_rendered: 0,
185 vram_usage_estimate_mb: 0.0,
186 };
187
188 monitor.update_from_frame_stats(&render_stats);
189 let report = monitor.get_gpu_report().unwrap();
190
191 assert_eq!(report.frame_total_duration_us(), Some(0)); assert_eq!(report.main_pass_duration_us(), Some(0)); }
195}