use prometeu_drivers::hardware::Hardware; use prometeu_firmware::Firmware; use std::time::{Duration, Instant}; use winit::window::Window; pub struct HostStats { pub last_stats_update: Instant, pub frames_since_last_update: u64, pub current_fps: f64, pub audio_load_accum_us: u64, pub audio_load_samples: u64, recent_host_cpu_us: [u64; 5], recent_host_cpu_count: usize, recent_host_cpu_cursor: usize, } impl Default for HostStats { fn default() -> Self { Self::new() } } impl HostStats { pub fn new() -> Self { Self { last_stats_update: Instant::now(), frames_since_last_update: 0, current_fps: 0.0, audio_load_accum_us: 0, audio_load_samples: 0, recent_host_cpu_us: [0; 5], recent_host_cpu_count: 0, recent_host_cpu_cursor: 0, } } pub fn record_frame(&mut self) { self.frames_since_last_update += 1; } pub fn record_audio_perf(&mut self, us: u64) { self.audio_load_accum_us += us; self.audio_load_samples += 1; } pub fn record_host_cpu_time(&mut self, us: u64) { self.recent_host_cpu_us[self.recent_host_cpu_cursor] = us; self.recent_host_cpu_cursor = (self.recent_host_cpu_cursor + 1) % self.recent_host_cpu_us.len(); self.recent_host_cpu_count = self.recent_host_cpu_count.saturating_add(1).min(self.recent_host_cpu_us.len()); } pub fn average_host_cpu_ms(&self) -> f64 { if self.recent_host_cpu_count == 0 { 0.0 } else { let sum: u64 = self.recent_host_cpu_us.iter().take(self.recent_host_cpu_count).sum(); (sum as f64 / self.recent_host_cpu_count as f64) / 1000.0 } } pub fn update( &mut self, now: Instant, window: Option<&Window>, _hardware: &Hardware, firmware: &Firmware, ) { let stats_elapsed = now.duration_since(self.last_stats_update); if stats_elapsed >= Duration::from_secs(1) { self.current_fps = self.frames_since_last_update as f64 / stats_elapsed.as_secs_f64(); if let Some(window) = window { // Fixed comparison always against 60Hz, keep even when doing CPU stress tests let frame_budget_us = 16666.0; let cpu_load_core = (self.average_host_cpu_ms() * 1000.0 / frame_budget_us) * 100.0; let cpu_load_audio = if self.audio_load_samples > 0 { (self.audio_load_accum_us as f64 / stats_elapsed.as_micros() as f64) * 100.0 } else { 0.0 }; let title = format!( "PROMETEU | GFX: {:.1} KB | FPS: {:.1} | Load: {:.1}% (C) + {:.1}% (A) | Frame: tick {} logical {} done {}", 0, self.current_fps, cpu_load_core, cpu_load_audio, firmware.os.tick_index, firmware.os.logical_frame_index, firmware .os .atomic_telemetry .completed_logical_frames .load(std::sync::atomic::Ordering::Relaxed), ); window.set_title(&title); } self.last_stats_update = now; self.frames_since_last_update = 0; self.audio_load_accum_us = 0; self.audio_load_samples = 0; } } }