use std::time::{Duration, Instant}; /// Number of fps samples to store const MAX_SAMPLES: usize = 100; /// Keeps track of frame intervals and FPS pub struct Timer { previous: Instant, tick_index: usize, tick_sum: Duration, tick_list: Box<[Duration; MAX_SAMPLES]>, frame: u32, } impl Timer { pub fn new() -> Self { let previous = Instant::now(); let tick_index = 0usize; let tick_sum = Duration::ZERO; let tick_list = Box::new([tick_sum; MAX_SAMPLES]); let frame = 0u32; Self { previous, tick_index, tick_sum, tick_list, frame, } } pub fn update(&mut self) { let time = self.previous.elapsed(); self.previous = Instant::now(); // caculate average tick self.tick_index += 1; self.tick_index %= MAX_SAMPLES; self.tick_sum -= self.tick_list[self.tick_index]; self.tick_sum += time; self.tick_list[self.tick_index] = time; // update frame frame self.frame += 1; } pub const fn frame(&self) -> u32 { self.frame } #[expect(clippy::cast_possible_truncation)] pub const fn fps(&self) -> u32 { match self.frame_time().as_nanos() { 0 => u32::MAX, // fps can never be bigger than u32::MAX since we're // computing in nanoseconds nanos => 1_000_000_000u128.div_ceil(nanos) as u32, } } #[expect(clippy::cast_possible_truncation)] pub const fn frame_time(&self) -> Duration { match self.tick_sum.checked_div(MAX_SAMPLES as u32) { Some(duration) => duration, None => Duration::ZERO, } } pub const fn delta_time(&self) -> Duration { self.tick_list[self.tick_index] } }