Skip to main content

ass_renderer/debug/benchmarking/
animation.rs

1//! Animation frame-rate benchmarking for the renderer
2
3use 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    /// Run frame rate benchmark for animations
15    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; // 25 FPS
28        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        // Render all frames
36        for frame_idx in 0..total_frames {
37            let time_cs = start_time + (frame_idx as u32 * 4); // 25 FPS
38
39            #[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, // Placeholder
94        })
95    }
96
97    /// Find animation time range
98    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}