use sdl2::Sdl;
#[derive(Clone, Copy, Debug)]
pub struct Tick {
pub total_time: f64,
pub dt: f32,
pub frame: u64,
}
pub struct Timer {
dt: f64,
freq: f64,
max_frame: f64,
total_time: f64,
accumulator: f64,
current_time: f64,
frame: u64,
subsystem: sdl2::TimerSubsystem,
}
impl Timer {
pub fn new(context: &Sdl, dt: f64, max_frame: f64) -> Result<Self, String> {
let subsystem = context.timer()?;
let freq = subsystem.performance_frequency() as f64;
let current_time = subsystem.performance_counter() as f64 / freq;
Ok(Self {
dt,
freq,
max_frame,
total_time: 0.,
accumulator: 0.,
current_time,
frame: 0,
subsystem,
})
}
fn get_time(&self) -> f64 {
(self.subsystem.performance_counter() as f64) / self.freq
}
pub fn begin_frame<'a>(&'a mut self) -> TimeIter<'a> {
let new_time = self.get_time();
let frame_time = (new_time - self.current_time).min(self.max_frame);
self.current_time = new_time;
self.accumulator += frame_time;
TimeIter { timer: self }
}
pub const fn get_tick(&self) -> Tick {
Tick {
total_time: self.total_time,
dt: self.dt as f32,
frame: self.frame,
}
}
fn has_accumulator_remaining(&self) -> bool {
self.accumulator >= self.dt
}
fn reduce_accumulator(&mut self) {
self.accumulator -= self.dt;
self.total_time += self.dt;
}
#[allow(dead_code)]
pub const fn dt(&self) -> f32 {
self.dt as f32
}
#[allow(dead_code)]
pub const fn total_time(&self) -> f32 {
self.total_time as f32
}
}
pub struct TimeIter<'a> {
timer: &'a mut Timer,
}
impl<'a> Iterator for TimeIter<'a> {
type Item = Tick;
fn next(&mut self) -> Option<Self::Item> {
let timer = &mut self.timer;
if timer.has_accumulator_remaining() {
timer.frame += 1;
let tick = timer.get_tick();
timer.reduce_accumulator();
return Some(tick);
}
None
}
}