pub mod builder;
mod clipboard;
mod editor;
#[cfg(feature = "window")]
pub mod gpu;
pub mod shell;
pub mod ui;
mod watch;
pub mod webexport;
use std::{
path::{Path, PathBuf},
time::{Duration, Instant},
};
pub fn frame_duration(fps: u32) -> Duration {
Duration::from_nanos(1_000_000_000 / fps.max(1) as u64)
}
pub struct TickPacer {
next: Instant,
}
impl TickPacer {
pub fn new(now: Instant) -> Self {
Self { next: now }
}
pub fn tick_due(&mut self, now: Instant, frame: Duration) -> bool {
if now < self.next {
return false;
}
self.next += frame;
if now > self.next + frame * 10 {
self.next = now + frame;
}
true
}
pub fn next_tick(&self) -> Instant {
self.next
}
}
pub fn sdk_path() -> PathBuf {
if let Ok(p) = std::env::var("PIXEL8_SDK") {
return PathBuf::from(p);
}
Path::new(env!("CARGO_MANIFEST_DIR")).join("../pixel8")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_pass_runs_only_the_ticks_already_due() {
let t0 = Instant::now();
let frame = Duration::from_millis(10);
let mut pacer = TickPacer::new(t0);
let now = t0 + frame * 2 + frame / 2;
let mut ticks = 0;
while pacer.tick_due(now, frame) {
ticks += 1;
}
assert_eq!(ticks, 3);
assert!(!pacer.tick_due(now, frame));
}
#[test]
fn a_long_stall_skips_ahead_with_a_single_tick() {
let t0 = Instant::now();
let frame = Duration::from_millis(10);
let mut pacer = TickPacer::new(t0);
let now = t0 + frame * 15;
assert!(pacer.tick_due(now, frame));
assert_eq!(pacer.next_tick(), now + frame);
assert!(!pacer.tick_due(now, frame));
}
#[test]
fn next_tick_reports_the_scheduled_instant() {
let t0 = Instant::now();
let frame = Duration::from_millis(10);
let mut pacer = TickPacer::new(t0);
assert_eq!(pacer.next_tick(), t0);
assert!(pacer.tick_due(t0, frame));
assert_eq!(pacer.next_tick(), t0 + frame);
}
}