diff --git a/src/app.rs b/src/app.rs index d576de5..9e4c1fd 100644 --- a/src/app.rs +++ b/src/app.rs @@ -89,7 +89,7 @@ impl App { let start = Instant::now(); let dt = self.last_tick.elapsed().as_secs_f32(); - self.last_tick = Instant::now(); + self.last_tick = start; let exit = self.game.tick(dt); diff --git a/src/constants.rs b/src/constants.rs index 034cd1f..185a980 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -8,8 +8,8 @@ use glam::UVec2; /// /// Calculated as 1/60th of a second (≈16.67ms). /// -/// Written out explicitly to satisfy const-eval constraints. -pub const LOOP_TIME: Duration = Duration::from_nanos((1_000_000_000.0 / 60.0) as u64); +/// Uses integer arithmetic to avoid floating-point precision loss. +pub const LOOP_TIME: Duration = Duration::from_nanos(1_000_000_000 / 60); /// The size of each cell, in pixels. pub const CELL_SIZE: u32 = 8; diff --git a/src/systems/input.rs b/src/systems/input.rs index 29994f3..8ee5010 100644 --- a/src/systems/input.rs +++ b/src/systems/input.rs @@ -138,14 +138,12 @@ pub fn input_system( // Warn if the smallvec was heap allocated due to exceeding stack capacity #[cfg(debug_assertions)] - { - if frame_events.len() > frame_events.capacity() { - tracing::warn!( - "More than {} events in a frame, consider adjusting stack capacity: {:?}", - frame_events.capacity(), - frame_events - ); - } + if frame_events.len() > frame_events.capacity() { + tracing::warn!( + "More than {} events in a frame, consider adjusting stack capacity: {:?}", + frame_events.capacity(), + frame_events + ); } // Handle non-keyboard events inline and build a simplified keyboard event stream. diff --git a/src/systems/profiling.rs b/src/systems/profiling.rs index 7a62ef4..7c68f80 100644 --- a/src/systems/profiling.rs +++ b/src/systems/profiling.rs @@ -7,8 +7,8 @@ use parking_lot::{Mutex, RwLock}; use smallvec::SmallVec; use std::fmt::Display; use std::time::Duration; -use strum::EnumCount; -use strum_macros::{EnumCount, IntoStaticStr}; +use strum::{EnumCount, IntoEnumIterator}; +use strum_macros::{EnumCount, EnumIter, IntoStaticStr}; use thousands::Separable; /// The maximum number of systems that can be profiled. Must not be exceeded, or it will panic. @@ -16,7 +16,7 @@ const MAX_SYSTEMS: usize = SystemId::COUNT; /// The number of durations to keep in the circular buffer. const TIMING_WINDOW_SIZE: usize = 30; -#[derive(EnumCount, IntoStaticStr, Debug, PartialEq, Eq, Hash, Copy, Clone)] +#[derive(EnumCount, EnumIter, IntoStaticStr, Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum SystemId { Input, PlayerControls, @@ -96,7 +96,7 @@ impl SystemTimings { let sum: f64 = durations.iter().sum(); let mean = sum / count; - let variance = durations.iter().map(|x| (x - mean).powi(2)).sum::() / count; + let variance = durations.iter().map(|x| (x - mean).powi(2)).sum::() / (count - 1.0).max(1.0); let std_dev = variance.sqrt(); stats.insert( @@ -128,7 +128,7 @@ impl SystemTimings { diff_secs * diff_secs }) .sum::() - / duration_sums.len() as f64; + / (duration_sums.len() - 1).max(1) as f64; let std_dev_secs = variance.sqrt(); (mean, Duration::from_secs_f64(std_dev_secs)) @@ -250,17 +250,22 @@ pub fn format_timing_display( }) .collect::>(); - let (max_name_width, max_avg_int_width, max_avg_decimal_width, max_std_int_width, max_std_decimal_width) = entries - .iter() - .fold((0, 0, 3, 0, 3), |(name_w, avg_int_w, avg_dec_w, std_int_w, std_dec_w), e| { - ( - name_w.max(e.name.len()), - avg_int_w.max(e.avg_int.width() as usize), - avg_dec_w.max(e.avg_decimal.width() as usize), - std_int_w.max(e.std_int.width() as usize), - std_dec_w.max(e.std_decimal.width() as usize), - ) - }); + let (max_avg_int_width, max_avg_decimal_width, max_std_int_width, max_std_decimal_width) = + entries + .iter() + .fold((0, 3, 0, 3), |(avg_int_w, avg_dec_w, std_int_w, std_dec_w), e| { + ( + avg_int_w.max(e.avg_int.width() as usize), + avg_dec_w.max(e.avg_decimal.width() as usize), + std_int_w.max(e.std_int.width() as usize), + std_dec_w.max(e.std_decimal.width() as usize), + ) + }); + + let max_name_width = SystemId::iter() + .map(|id| id.to_string().len()) + .max() + .expect("SystemId::iter() returned an empty iterator"); entries.iter().map(|e| { format!( diff --git a/tests/profiling.rs b/tests/profiling.rs index 97fa136..4a06dd2 100644 --- a/tests/profiling.rs +++ b/tests/profiling.rs @@ -14,6 +14,7 @@ fn test_timing_statistics() { timings.add_timing(SystemId::Blinking, Duration::from_millis(3)); timings.add_timing(SystemId::Blinking, Duration::from_millis(2)); timings.add_timing(SystemId::Blinking, Duration::from_millis(1)); + fn close_enough(a: Duration, b: Duration) -> bool { if a > b { a - b < Duration::from_micros(500) // 0.1ms @@ -36,25 +37,8 @@ fn test_timing_statistics() { total_avg ); assert!( - close_enough(total_std, Duration::from_millis(12)), + close_enough(total_std, Duration::from_millis(17)), "total_std: {:?}", total_std ); } - -// #[test] -// fn test_window_size_limit() { -// let timings = SystemTimings::default(); - -// // Add more than 90 timings to test window size limit -// for i in 0..100 { -// timings.add_timing("test_system", Duration::from_millis(i)); -// } - -// let stats = timings.get_stats(); -// let (avg, _) = stats.get("test_system").unwrap(); - -// // Should only keep the last 90 values, so average should be around 55ms -// // (average of 10-99) -// assert!((avg.as_millis() as f64 - 55.0).abs() < 5.0); -// }