ass_renderer/debug/benchmarking/
animation.rs1use crate::utils::RenderError;
4use ass_core::parser::Script;
5
6#[cfg(feature = "nostd")]
7use alloc::{format, vec::Vec};
8#[cfg(not(feature = "nostd"))]
9use std::{format, time::Instant, vec::Vec};
10
11use super::{BenchmarkResult, PerformanceBenchmark, PerformanceMetrics};
12
13impl PerformanceBenchmark {
14 pub fn benchmark_animation_framerate(
16 &mut self,
17 script: &Script,
18 test_name: &str,
19 ) -> Result<BenchmarkResult, RenderError> {
20 if !self.config.measure_frame_rate {
21 return Err(RenderError::UnsupportedOperation(
22 "Frame rate benchmarking disabled".to_string(),
23 ));
24 }
25
26 let (start_time, end_time) = self.find_animation_timerange(script);
27 let total_frames = ((end_time - start_time) / 4) as usize; let mut frame_times = Vec::new();
29
30 eprintln!("Benchmarking animation frame rate: {total_frames} frames");
31
32 #[cfg(not(feature = "nostd"))]
33 let start_benchmark = Instant::now();
34
35 for frame_idx in 0..total_frames {
37 let time_cs = start_time + (frame_idx as u32 * 4); #[cfg(not(feature = "nostd"))]
40 let frame_start = Instant::now();
41
42 let _frame = self.our_renderer.render_frame(script, time_cs)?;
43
44 #[cfg(not(feature = "nostd"))]
45 let frame_time = frame_start.elapsed();
46 #[cfg(feature = "nostd")]
47 let frame_time = std::time::Duration::from_millis(1);
48
49 frame_times.push(frame_time.as_secs_f64() * 1000.0);
50 }
51
52 #[cfg(not(feature = "nostd"))]
53 let total_time = start_benchmark.elapsed().as_secs_f64();
54 #[cfg(feature = "nostd")]
55 let total_time = 1.0;
56
57 let avg_frame_time = frame_times.iter().sum::<f64>() / frame_times.len() as f64;
58 let fps = 1000.0 / avg_frame_time;
59 let realtime_fps = total_frames as f64 / total_time;
60
61 eprintln!("Animation benchmark results:");
62 eprintln!(" Average frame time: {avg_frame_time:.2}ms");
63 eprintln!(" Theoretical FPS: {fps:.1}");
64 eprintln!(" Actual FPS: {realtime_fps:.1}");
65
66 let our_performance = PerformanceMetrics {
67 avg_render_time_ms: avg_frame_time,
68 min_render_time_ms: frame_times.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
69 max_render_time_ms: frame_times.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
70 render_time_std_dev: {
71 let variance = frame_times
72 .iter()
73 .map(|&t| (t - avg_frame_time).powi(2))
74 .sum::<f64>()
75 / frame_times.len() as f64;
76 variance.sqrt()
77 },
78 fps: Some(fps),
79 peak_memory_bytes: None,
80 avg_memory_bytes: None,
81 cache_hit_rate: None,
82 };
83
84 Ok(BenchmarkResult {
85 test_name: format!("{test_name}_animation_fps"),
86 resolution: (
87 self.our_renderer.context().width(),
88 self.our_renderer.context().height(),
89 ),
90 our_performance,
91 performance_ratio: None,
92 memory_ratio: None,
93 compatibility_score: 1.0, })
95 }
96
97 fn find_animation_timerange(&self, script: &Script) -> (u32, u32) {
99 let mut min_time = u32::MAX;
100 let mut max_time = 0;
101
102 for section in script.sections() {
103 if let ass_core::parser::Section::Events(events) = section {
104 for event in events {
105 let start = event.start_time_cs().unwrap_or(0);
106 let end = event.end_time_cs().unwrap_or(0);
107 min_time = min_time.min(start);
108 max_time = max_time.max(end);
109 }
110 }
111 }
112
113 if min_time == u32::MAX {
114 (0, self.config.animation_duration_cs)
115 } else {
116 let duration = (max_time - min_time).min(self.config.animation_duration_cs);
117 (min_time, min_time + duration)
118 }
119 }
120}