use crate::context::Context;
use std::cmp;
use std::f64;
use std::time;
use std::time::Duration;
type Instant = f64;
pub fn time() -> f64 {
miniquad::date::now()
}
#[derive(Debug, Clone)]
struct LogBuffer<T>
where
T: Clone,
{
head: usize,
size: usize,
samples: usize,
contents: Vec<T>,
}
impl<T> LogBuffer<T>
where
T: Clone + Copy,
{
fn new(size: usize, init_val: T) -> LogBuffer<T> {
LogBuffer {
head: 0,
size,
contents: vec![init_val; size],
samples: 1,
}
}
fn push(&mut self, item: T) {
self.head = (self.head + 1) % self.contents.len();
self.contents[self.head] = item;
self.size = cmp::min(self.size + 1, self.contents.len());
self.samples += 1;
}
fn contents(&self) -> &[T] {
if self.samples > self.size {
&self.contents
} else {
&self.contents[..self.samples]
}
}
fn latest(&self) -> T {
self.contents[self.head]
}
}
#[derive(Debug)]
pub struct TimeContext {
init_instant: Instant,
last_instant: Instant,
frame_durations: LogBuffer<Duration>,
residual_update_dt: Duration,
frame_count: usize,
}
const TIME_LOG_FRAMES: usize = 200;
impl TimeContext {
pub fn new() -> TimeContext {
let initial_dt = time::Duration::from_millis(16);
TimeContext {
init_instant: time(),
last_instant: time(),
frame_durations: LogBuffer::new(TIME_LOG_FRAMES, initial_dt),
residual_update_dt: time::Duration::from_secs(0),
frame_count: 0,
}
}
pub fn tick(&mut self) {
let now = time();
let time_since_last = now - self.last_instant;
self.frame_durations.push(f64_to_duration(time_since_last));
self.last_instant = now;
self.frame_count += 1;
self.residual_update_dt += f64_to_duration(time_since_last);
}
}
impl Default for TimeContext {
fn default() -> Self {
Self::new()
}
}
pub fn delta(ctx: &Context) -> Duration {
let tc = &ctx.timer_context;
tc.frame_durations.latest()
}
pub fn average_delta(ctx: &Context) -> Duration {
let tc = &ctx.timer_context;
let sum: Duration = tc.frame_durations.contents().iter().sum();
if tc.frame_durations.samples > tc.frame_durations.size {
sum / (tc.frame_durations.size as u32)
} else {
sum / (tc.frame_durations.samples as u32)
}
}
pub fn duration_to_f64(d: Duration) -> f64 {
d.as_secs() as f64 + d.subsec_nanos() as f64 * 1e-9
}
pub fn f64_to_duration(t: f64) -> Duration {
let seconds = t.trunc();
let nanos = t.fract() * 1.0e9;
Duration::new(seconds as u64, nanos as u32)
}
fn fps_as_duration(fps: u32) -> Duration {
let target_dt_seconds = 1.0 / f64::from(fps);
f64_to_duration(target_dt_seconds)
}
pub fn fps(ctx: &Context) -> f64 {
let duration_per_frame = average_delta(ctx);
let seconds_per_frame = duration_to_f64(duration_per_frame);
1.0 / seconds_per_frame
}
pub fn time_since_start(ctx: &Context) -> Duration {
let tc = &ctx.timer_context;
f64_to_duration(time() - tc.init_instant)
}
pub fn time_since_start_f64(ctx: &Context) -> f64 {
let tc = &ctx.timer_context;
time() - tc.init_instant
}
pub fn check_update_time(ctx: &mut Context, target_fps: u32) -> bool {
let timedata = &mut ctx.timer_context;
let target_dt = fps_as_duration(target_fps);
if timedata.residual_update_dt > target_dt {
timedata.residual_update_dt -= target_dt;
true
} else {
false
}
}
pub fn remaining_update_time(ctx: &mut Context) -> Duration {
ctx.timer_context.residual_update_dt
}
pub fn ticks(ctx: &Context) -> usize {
ctx.timer_context.frame_count
}