From a489bff0d191e02828ccddb8d923a828a70a7edf Mon Sep 17 00:00:00 2001 From: Ryan Walters Date: Wed, 3 Sep 2025 17:23:20 -0500 Subject: [PATCH] chore: add timing demo bin --- Cargo.toml | 1 + src/bin/timing_demo.rs | 91 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/bin/timing_demo.rs diff --git a/Cargo.toml b/Cargo.toml index 7614b0d..fcaa510 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ keywords = ["game", "pacman", "arcade", "sdl2"] categories = ["games", "emulators"] publish = false exclude = ["/assets/unpacked/**", "/assets/site/**", "/bacon.toml", "/Justfile"] +default-run = "pacman" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/bin/timing_demo.rs b/src/bin/timing_demo.rs new file mode 100644 index 0000000..ad6101d --- /dev/null +++ b/src/bin/timing_demo.rs @@ -0,0 +1,91 @@ +use circular_buffer::CircularBuffer; +use pacman::constants::CANVAS_SIZE; +use sdl2::event::Event; +use sdl2::keyboard::Keycode; +use sdl2::pixels::Color; +use std::time::{Duration, Instant}; + +fn main() -> Result<(), String> { + let sdl_context = sdl2::init()?; + let video_subsystem = sdl_context.video()?; + + let window = video_subsystem + .window("SDL2 Timing Demo", CANVAS_SIZE.x, CANVAS_SIZE.y) + .opengl() + .position_centered() + .build() + .map_err(|e| e.to_string())?; + + let mut canvas = window.into_canvas().accelerated().build().map_err(|e| e.to_string())?; + canvas + .set_logical_size(CANVAS_SIZE.x, CANVAS_SIZE.y) + .map_err(|e| e.to_string())?; + + let mut event_pump = sdl_context.event_pump()?; + + // Store frame timings in milliseconds + let mut frame_timings = CircularBuffer::<20_000, f64>::new(); + let mut last_report_time = Instant::now(); + let report_interval = Duration::from_millis(500); + + 'running: loop { + let frame_start_time = Instant::now(); + + for event in event_pump.poll_iter() { + match event { + Event::Quit { .. } + | Event::KeyDown { + keycode: Some(Keycode::Escape), + .. + } => { + break 'running; + } + _ => {} + } + } + + // Clear the screen + canvas.set_draw_color(Color::RGB(0, 0, 0)); + canvas.clear(); + canvas.present(); + + // Record timing + let frame_duration = frame_start_time.elapsed(); + frame_timings.push_back(frame_duration.as_secs_f64()); + + // Report stats every `report_interval` + let elapsed = last_report_time.elapsed(); + if elapsed >= report_interval { + if !frame_timings.is_empty() { + let count = frame_timings.len() as f64; + let sum: f64 = frame_timings.iter().sum(); + let mean = sum / count; + + let variance = frame_timings + .iter() + .map(|value| { + let diff = mean - value; + diff * diff + }) + .sum::() + / count; + let std_dev = variance.sqrt(); + + println!( + "Rendered {count} frames at {fps:.1} fps (last {elapsed:.2?}): mean={mean:.3?}, std_dev={std_dev:.3?}", + count = frame_timings.len(), + fps = count / elapsed.as_secs_f64(), + elapsed = elapsed, + mean = Duration::from_secs_f64(mean), + std_dev = Duration::from_secs_f64(std_dev), + ); + } + + // Reset for next interval + frame_timings.clear(); + last_report_time = Instant::now(); + } + } + + Ok(()) +}