use std::num;
use std::thread;
use std::time;
#[must_use = "iterators are lazy and do nothing unless consumed"]
#[derive(Debug)]
pub struct Clock {
time_per_update: time::Duration,
time_per_render: Option<time::Duration>,
max_updates_per_frame: Option<usize>,
updates: usize,
previous_update_time: time::Instant,
previous_render_time: time::Instant,
}
impl Clock {
#[inline]
pub fn new(updates_per_second: num::NonZeroU32) -> Self {
let time_per_update = time::Duration::from_secs(1) / updates_per_second.get();
let now = time::Instant::now();
Clock {
time_per_update,
time_per_render: None,
max_updates_per_frame: None,
previous_update_time: now - time_per_update,
previous_render_time: now,
updates: 0,
}
}
#[inline]
pub fn max_frame_rate(mut self, frames_per_second: num::NonZeroU32) -> Self {
let time_per_render = time::Duration::from_secs(1) / frames_per_second.get();
self.time_per_render = Some(time_per_render);
self.previous_render_time -= time_per_render;
self
}
#[deprecated(note = "use `Clock::max_frame_rate` instead")]
#[inline]
pub fn with_frame_limit(self, frames_per_second: num::NonZeroU32) -> Self {
self.max_frame_rate(frames_per_second)
}
#[inline]
pub fn max_updates_per_frame(mut self, max_updates_per_frame: usize) -> Self {
self.max_updates_per_frame = Some(max_updates_per_frame);
self
}
#[deprecated(note = "use `Clock::max_updates_per_frame` instead")]
#[inline]
pub fn with_frame_skip(self, frame_skip: usize) -> Self {
self.max_updates_per_frame(frame_skip)
}
}
impl Iterator for Clock {
type Item = Tick;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut now = time::Instant::now();
if let Some(time_per_render) = self.time_per_render {
if now - self.previous_render_time < time_per_render
&& now - self.previous_update_time < self.time_per_update
{
let sleep_time = std::cmp::min(
self.previous_render_time + time_per_render - now,
self.previous_update_time + self.time_per_update - now,
);
thread::sleep(sleep_time);
now += sleep_time;
}
}
if now - self.previous_update_time >= self.time_per_update
&& self.updates < self.max_updates_per_frame.unwrap_or(usize::MAX)
{
self.previous_update_time += self.time_per_update;
self.updates += 1;
Some(Tick::Update)
} else {
self.previous_render_time = now;
self.updates = 0;
let interpolation = (now - self.previous_update_time).as_secs_f32()
/ self.time_per_update.as_secs_f32();
Some(Tick::Render { interpolation })
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum Tick {
Update,
Render {
interpolation: f32,
},
}