1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
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
    }
}