fix: avoid padding jitter with constant name padding, minor timing calculation fixes

This commit is contained in:
Ryan Walters
2025-09-03 19:00:45 -05:00
parent 23f43288e1
commit ebc3f909a3
5 changed files with 32 additions and 45 deletions

View File

@@ -89,7 +89,7 @@ impl App {
let start = Instant::now(); let start = Instant::now();
let dt = self.last_tick.elapsed().as_secs_f32(); let dt = self.last_tick.elapsed().as_secs_f32();
self.last_tick = Instant::now(); self.last_tick = start;
let exit = self.game.tick(dt); let exit = self.game.tick(dt);

View File

@@ -8,8 +8,8 @@ use glam::UVec2;
/// ///
/// Calculated as 1/60th of a second (≈16.67ms). /// Calculated as 1/60th of a second (≈16.67ms).
/// ///
/// Written out explicitly to satisfy const-eval constraints. /// Uses integer arithmetic to avoid floating-point precision loss.
pub const LOOP_TIME: Duration = Duration::from_nanos((1_000_000_000.0 / 60.0) as u64); pub const LOOP_TIME: Duration = Duration::from_nanos(1_000_000_000 / 60);
/// The size of each cell, in pixels. /// The size of each cell, in pixels.
pub const CELL_SIZE: u32 = 8; pub const CELL_SIZE: u32 = 8;

View File

@@ -138,14 +138,12 @@ pub fn input_system(
// Warn if the smallvec was heap allocated due to exceeding stack capacity // Warn if the smallvec was heap allocated due to exceeding stack capacity
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
{ if frame_events.len() > frame_events.capacity() {
if frame_events.len() > frame_events.capacity() { tracing::warn!(
tracing::warn!( "More than {} events in a frame, consider adjusting stack capacity: {:?}",
"More than {} events in a frame, consider adjusting stack capacity: {:?}", frame_events.capacity(),
frame_events.capacity(), frame_events
frame_events );
);
}
} }
// Handle non-keyboard events inline and build a simplified keyboard event stream. // Handle non-keyboard events inline and build a simplified keyboard event stream.

View File

@@ -7,8 +7,8 @@ use parking_lot::{Mutex, RwLock};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::fmt::Display; use std::fmt::Display;
use std::time::Duration; use std::time::Duration;
use strum::EnumCount; use strum::{EnumCount, IntoEnumIterator};
use strum_macros::{EnumCount, IntoStaticStr}; use strum_macros::{EnumCount, EnumIter, IntoStaticStr};
use thousands::Separable; use thousands::Separable;
/// The maximum number of systems that can be profiled. Must not be exceeded, or it will panic. /// 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. /// The number of durations to keep in the circular buffer.
const TIMING_WINDOW_SIZE: usize = 30; 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 { pub enum SystemId {
Input, Input,
PlayerControls, PlayerControls,
@@ -96,7 +96,7 @@ impl SystemTimings {
let sum: f64 = durations.iter().sum(); let sum: f64 = durations.iter().sum();
let mean = sum / count; let mean = sum / count;
let variance = durations.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / count; let variance = durations.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / (count - 1.0).max(1.0);
let std_dev = variance.sqrt(); let std_dev = variance.sqrt();
stats.insert( stats.insert(
@@ -128,7 +128,7 @@ impl SystemTimings {
diff_secs * diff_secs diff_secs * diff_secs
}) })
.sum::<f64>() .sum::<f64>()
/ duration_sums.len() as f64; / (duration_sums.len() - 1).max(1) as f64;
let std_dev_secs = variance.sqrt(); let std_dev_secs = variance.sqrt();
(mean, Duration::from_secs_f64(std_dev_secs)) (mean, Duration::from_secs_f64(std_dev_secs))
@@ -250,17 +250,22 @@ pub fn format_timing_display(
}) })
.collect::<SmallVec<[Entry; 12]>>(); .collect::<SmallVec<[Entry; 12]>>();
let (max_name_width, max_avg_int_width, max_avg_decimal_width, max_std_int_width, max_std_decimal_width) = entries let (max_avg_int_width, max_avg_decimal_width, max_std_int_width, max_std_decimal_width) =
.iter() entries
.fold((0, 0, 3, 0, 3), |(name_w, avg_int_w, avg_dec_w, std_int_w, std_dec_w), e| { .iter()
( .fold((0, 3, 0, 3), |(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_int_w.max(e.avg_int.width() as usize),
avg_dec_w.max(e.avg_decimal.width() as usize), avg_dec_w.max(e.avg_decimal.width() as usize),
std_int_w.max(e.std_int.width() as usize), std_int_w.max(e.std_int.width() as usize),
std_dec_w.max(e.std_decimal.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| { entries.iter().map(|e| {
format!( format!(

View File

@@ -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(3));
timings.add_timing(SystemId::Blinking, Duration::from_millis(2)); timings.add_timing(SystemId::Blinking, Duration::from_millis(2));
timings.add_timing(SystemId::Blinking, Duration::from_millis(1)); timings.add_timing(SystemId::Blinking, Duration::from_millis(1));
fn close_enough(a: Duration, b: Duration) -> bool { fn close_enough(a: Duration, b: Duration) -> bool {
if a > b { if a > b {
a - b < Duration::from_micros(500) // 0.1ms a - b < Duration::from_micros(500) // 0.1ms
@@ -36,25 +37,8 @@ fn test_timing_statistics() {
total_avg total_avg
); );
assert!( assert!(
close_enough(total_std, Duration::from_millis(12)), close_enough(total_std, Duration::from_millis(17)),
"total_std: {:?}", "total_std: {:?}",
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);
// }