use context::Context;
use std::cmp;
use std::time;
use std::thread;
#[derive(Debug, Clone)]
struct LogBuffer<T>
where T: Clone
{
head: usize,
size: usize,
contents: Vec<T>,
}
impl<T> LogBuffer<T>
where T: Clone + Copy
{
fn new(size: usize, init_val: T) -> LogBuffer<T> {
let mut v = Vec::with_capacity(size);
v.resize(size, init_val);
LogBuffer {
head: 0,
size: size,
contents: v,
}
}
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());
}
fn contents(&self) -> &[T] {
&self.contents
}
fn latest(&self) -> T {
self.contents[self.head]
}
}
#[derive(Debug)]
pub struct TimeContext {
init_instant: time::Instant,
last_instant: time::Instant,
frame_durations: LogBuffer<time::Duration>,
residual_update_dt: time::Duration,
frame_count: usize,
}
const TIME_LOG_FRAMES: usize = 200;
impl TimeContext {
pub fn new() -> TimeContext {
TimeContext {
init_instant: time::Instant::now(),
last_instant: time::Instant::now(),
frame_durations: LogBuffer::new(TIME_LOG_FRAMES, time::Duration::new(0, 0)),
residual_update_dt: time::Duration::from_secs(0),
frame_count: 0,
}
}
pub fn tick(&mut self) {
let now = time::Instant::now();
let time_since_last = now - self.last_instant;
self.frame_durations.push(time_since_last);
self.last_instant = now;
self.frame_count += 1;
self.residual_update_dt += time_since_last;
}
}
impl Default for TimeContext {
fn default() -> Self {
Self::new()
}
}
pub fn get_delta(ctx: &Context) -> time::Duration {
let tc = &ctx.timer_context;
tc.frame_durations.latest()
}
pub fn get_average_delta(ctx: &Context) -> time::Duration {
let tc = &ctx.timer_context;
let init = time::Duration::new(0, 0);
let sum = tc.frame_durations
.contents()
.iter()
.fold(init, |d1, d2| d1 + *d2);
sum / (tc.frame_durations.size as u32)
}
pub fn duration_to_f64(d: time::Duration) -> f64 {
let seconds = d.as_secs() as f64;
let nanos = d.subsec_nanos() as f64;
seconds + (nanos * 1e-9)
}
pub fn f64_to_duration(t: f64) -> time::Duration {
debug_assert!(t >= 0.0, "f64_to_duration passed a negative number!");
let seconds = t.trunc();
let nanos = t.fract() * 1e9;
time::Duration::new(seconds as u64, nanos as u32)
}
fn fps_as_duration(fps: u32) -> time::Duration {
let target_dt_seconds = 1.0 / (fps as f64);
f64_to_duration(target_dt_seconds)
}
pub fn get_fps(ctx: &Context) -> f64 {
let duration_per_frame = get_average_delta(ctx);
let seconds_per_frame = duration_to_f64(duration_per_frame);
1.0 / seconds_per_frame
}
pub fn get_time_since_start(ctx: &Context) -> time::Duration {
let tc = &ctx.timer_context;
time::Instant::now() - 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 sleep(duration: time::Duration) {
thread::sleep(duration);
}
pub fn yield_now() {
thread::yield_now();
}
pub fn get_ticks(ctx: &Context) -> usize {
ctx.timer_context.frame_count
}