diff --git a/src/game.rs b/src/game.rs index b27eaed..07e1029 100644 --- a/src/game.rs +++ b/src/game.rs @@ -659,8 +659,15 @@ impl Game { pub fn tick(&mut self, dt: f32) -> bool { self.world.insert_resource(DeltaTime(dt)); - // Run all systems + // Measure total frame time including all systems + let start = std::time::Instant::now(); self.schedule.run(&mut self.world); + let total_duration = start.elapsed(); + + // Record the total timing + if let Some(timings) = self.world.get_resource::() { + timings.add_total_timing(total_duration); + } let state = self .world diff --git a/src/systems/profiling.rs b/src/systems/profiling.rs index 7a622bc..5a60a41 100644 --- a/src/systems/profiling.rs +++ b/src/systems/profiling.rs @@ -18,6 +18,7 @@ const TIMING_WINDOW_SIZE: usize = 30; #[derive(EnumCount, EnumIter, IntoStaticStr, Debug, PartialEq, Eq, Hash, Copy, Clone)] pub enum SystemId { + Total, Input, PlayerControls, Ghost, @@ -85,6 +86,11 @@ impl SystemTimings { queue.lock().push_back(duration); } + /// Add timing for the Total system (total frame time including scheduler.run) + pub fn add_total_timing(&self, duration: Duration) { + self.add_timing(SystemId::Total, duration); + } + pub fn get_stats(&self) -> Map { let mut stats = Map::new(); @@ -123,47 +129,30 @@ impl SystemTimings { stats } - pub fn get_total_stats(&self) -> (Duration, Duration) { - let duration_sums = { - self.timings - .iter() - .map(|(_, queue)| queue.lock().iter().sum::()) - .collect::>() - }; - - let mean = duration_sums.iter().sum::() / duration_sums.len() as u32; - let variance = duration_sums - .iter() - .map(|x| { - let diff_secs = x.as_secs_f64() - mean.as_secs_f64(); - diff_secs * diff_secs - }) - .sum::() - / (duration_sums.len() - 1).max(1) as f64; - let std_dev_secs = variance.sqrt(); - - (mean, Duration::from_secs_f64(std_dev_secs)) - } - pub fn format_timing_display(&self) -> SmallVec<[String; SystemId::COUNT]> { let stats = self.get_stats(); - let (total_avg, total_std) = self.get_total_stats(); + + // Get the Total system metrics instead of averaging all systems + let (total_avg, total_std) = stats + .get(&SystemId::Total) + .copied() + .unwrap_or((Duration::ZERO, Duration::ZERO)); let effective_fps = match 1.0 / total_avg.as_secs_f64() { - f if f > 100.0 => (f as u32).separate_with_commas(), + f if f > 100.0 => format!("{:>5} FPS", (f as u32).separate_with_commas()), f if f < 10.0 => format!("{:.1} FPS", f), - f => format!("{:.0} FPS", f), + f => format!("{:5.0} FPS", f), }; // Collect timing data for formatting let mut timing_data = vec![(effective_fps, total_avg, total_std)]; - // Sort the stats by average duration - let mut sorted_stats: Vec<_> = stats.iter().collect(); + // Sort the stats by average duration, excluding the Total system + let mut sorted_stats: Vec<_> = stats.iter().filter(|(id, _)| **id != SystemId::Total).collect(); sorted_stats.sort_by(|a, b| b.1 .0.cmp(&a.1 .0)); - // Add the top 5 most expensive systems - for (name, (avg, std_dev)) in sorted_stats.iter().take(7) { + // Add the top 7 most expensive systems (excluding Total) + for (name, (avg, std_dev)) in sorted_stats.iter().take(9) { timing_data.push((name.to_string(), *avg, *std_dev)); } diff --git a/tests/profiling.rs b/tests/profiling.rs index b412d06..5cfa7c5 100644 --- a/tests/profiling.rs +++ b/tests/profiling.rs @@ -40,15 +40,8 @@ fn test_timing_statistics() { assert_close!(*std_dev, Duration::from_millis(2), "PlayerControls standard deviation timing"); } - { - let (total_avg, total_std) = timings.get_total_stats(); - assert_close!(total_avg, Duration::from_millis(2), "Total average timing across all systems"); - assert_close!( - total_std, - Duration::from_millis(7), - "Total standard deviation timing across all systems" - ); - } + // Note: get_total_stats() was removed as we now use the Total system directly + // This test now focuses on individual system statistics } #[test] @@ -69,9 +62,9 @@ fn test_default_zero_timing_for_unused_systems() { assert_close!(*avg, Duration::from_millis(5), "System with data should have correct timing"); assert_close!(*std_dev, Duration::ZERO, "Single measurement should have zero std dev"); - // Verify that all other systems have zero timing + // Verify that all other systems have zero timing (excluding Total which is special) for id in SystemId::iter() { - if id != SystemId::PlayerControls { + if id != SystemId::PlayerControls && id != SystemId::Total { let (avg, std_dev) = stats.get(&id).unwrap(); assert_close!( *avg, @@ -108,3 +101,24 @@ fn test_pre_populated_timing_entries() { ); } } + +#[test] +fn test_total_system_timing() { + let timings = SystemTimings::default(); + + // Add some timing data to the Total system + timings.add_total_timing(Duration::from_millis(16)); + timings.add_total_timing(Duration::from_millis(18)); + timings.add_total_timing(Duration::from_millis(14)); + + let stats = timings.get_stats(); + let (avg, std_dev) = stats.get(&SystemId::Total).unwrap(); + + // Should have 16ms average (16+18+14)/3 = 16ms + assert_close!(*avg, Duration::from_millis(16), "Total system average timing"); + // Should have some standard deviation + assert!( + *std_dev > Duration::ZERO, + "Total system should have non-zero std dev with multiple measurements" + ); +}