use std::collections::VecDeque;
use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub struct FrameMetrics {
pub update_duration: Duration,
pub view_duration: Duration,
pub frame_duration: Duration,
pub events_processed: usize,
pub commands_executed: usize,
pub timestamp: Instant,
}
impl FrameMetrics {
pub fn fps(&self) -> f32 {
if self.frame_duration.as_secs_f32() > 0.0 {
1.0 / self.frame_duration.as_secs_f32()
} else {
0.0
}
}
}
#[derive(Debug, Clone)]
pub struct PerformanceMetrics {
frames: VecDeque<FrameMetrics>,
max_frames: usize,
total_events: usize,
total_commands: usize,
start_time: Instant,
current_frame: Option<FrameBuilder>,
}
#[derive(Debug, Clone)]
struct FrameBuilder {
update_start: Option<Instant>,
update_duration: Option<Duration>,
view_start: Option<Instant>,
view_duration: Option<Duration>,
frame_start: Instant,
events_processed: usize,
commands_executed: usize,
}
impl PerformanceMetrics {
pub fn new() -> Self {
Self::with_capacity(1000)
}
pub fn with_capacity(max_frames: usize) -> Self {
Self {
frames: VecDeque::with_capacity(max_frames),
max_frames,
total_events: 0,
total_commands: 0,
start_time: Instant::now(),
current_frame: None,
}
}
pub fn start_frame(&mut self) {
self.current_frame = Some(FrameBuilder {
update_start: None,
update_duration: None,
view_start: None,
view_duration: None,
frame_start: Instant::now(),
events_processed: 0,
commands_executed: 0,
});
}
pub fn start_update(&mut self) {
if let Some(frame) = &mut self.current_frame {
frame.update_start = Some(Instant::now());
}
}
pub fn end_update(&mut self) {
if let Some(frame) = &mut self.current_frame {
if let Some(start) = frame.update_start {
frame.update_duration = Some(start.elapsed());
}
}
}
pub fn start_view(&mut self) {
if let Some(frame) = &mut self.current_frame {
frame.view_start = Some(Instant::now());
}
}
pub fn end_view(&mut self) {
if let Some(frame) = &mut self.current_frame {
if let Some(start) = frame.view_start {
frame.view_duration = Some(start.elapsed());
}
}
}
pub fn record_event(&mut self) {
self.total_events += 1;
if let Some(frame) = &mut self.current_frame {
frame.events_processed += 1;
}
}
pub fn record_command(&mut self) {
self.total_commands += 1;
if let Some(frame) = &mut self.current_frame {
frame.commands_executed += 1;
}
}
pub fn end_frame(&mut self) {
if let Some(builder) = self.current_frame.take() {
let metrics = FrameMetrics {
update_duration: builder.update_duration.unwrap_or_default(),
view_duration: builder.view_duration.unwrap_or_default(),
frame_duration: builder.frame_start.elapsed(),
events_processed: builder.events_processed,
commands_executed: builder.commands_executed,
timestamp: builder.frame_start,
};
self.record_frame(metrics);
}
}
pub fn record_frame(&mut self, metrics: FrameMetrics) {
self.total_events += metrics.events_processed;
self.total_commands += metrics.commands_executed;
if self.frames.len() >= self.max_frames {
self.frames.pop_front();
}
self.frames.push_back(metrics);
}
pub fn average_fps(&self) -> f32 {
if self.frames.is_empty() {
return 0.0;
}
let total_duration: Duration = self.frames.iter().map(|f| f.frame_duration).sum();
if total_duration.as_secs_f32() > 0.0 {
self.frames.len() as f32 / total_duration.as_secs_f32()
} else {
0.0
}
}
pub fn current_fps(&self) -> f32 {
self.frames.back().map_or(0.0, FrameMetrics::fps)
}
pub fn average_frame_time(&self) -> Duration {
if self.frames.is_empty() {
return Duration::ZERO;
}
let total: Duration = self.frames.iter().map(|f| f.frame_duration).sum();
total / self.frames.len() as u32
}
pub fn summary(&self) -> MetricsSummary {
MetricsSummary {
average_fps: self.average_fps(),
current_fps: self.current_fps(),
average_frame_time: self.average_frame_time(),
total_events: self.total_events,
total_commands: self.total_commands,
uptime: self.start_time.elapsed(),
frame_count: self.frames.len(),
}
}
pub fn clear(&mut self) {
self.frames.clear();
self.total_events = 0;
self.total_commands = 0;
self.start_time = Instant::now();
}
}
impl Default for PerformanceMetrics {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct MetricsSummary {
pub average_fps: f32,
pub current_fps: f32,
pub average_frame_time: Duration,
pub total_events: usize,
pub total_commands: usize,
pub uptime: Duration,
pub frame_count: usize,
}
impl std::fmt::Display for MetricsSummary {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"FPS: {:.1} (avg: {:.1}) | Frame: {:?} | Events: {} | Commands: {} | Uptime: {:?}",
self.current_fps,
self.average_fps,
self.average_frame_time,
self.total_events,
self.total_commands,
self.uptime
)
}
}