use crate::MainFramebuffer;
use embassy_time::{Instant, Timer};
#[derive(PartialEq, Eq, Debug)]
pub enum LoopControlFlow {
Continue,
Break,
}
pub trait Updatable: Send {
fn update(&mut self, delta_time: std::time::Duration) -> LoopControlFlow;
fn run(&mut self, frames_per_second: u16) -> impl Future<Output = ()>
where
Self: Sized,
{
run(self, frames_per_second)
}
}
pub async fn run(updatable: &mut dyn Updatable, frames_per_second: u16) {
let delay = embassy_time::Duration::from_millis((1000.0 / frames_per_second as f64) as u64);
let mut update_start = Instant::now();
const REPORT_FPS: bool = false;
let mut elapseds = Vec::new();
let mut last_fps_report = update_start;
'update_loop: loop {
let now = Instant::now();
let elapsed = now.duration_since(update_start);
if REPORT_FPS {
elapseds.push(elapsed.as_millis());
let time_since_last_report = now - last_fps_report;
if time_since_last_report >= embassy_time::Duration::from_secs(5) {
let fps = elapseds.len() as f32
/ (time_since_last_report.as_micros() as f32 / 1_000_000.0);
log::info!("fps: {}", fps);
log::info!("processing times (ms): {:?}", elapseds);
last_fps_report = now;
elapseds.clear();
}
}
if elapsed <= delay {
let sleep_time = delay - elapsed;
Timer::after(sleep_time).await;
}
let _flush_guard = MainFramebuffer::get().pause_auto_flush();
let now = Instant::now();
let delta_time = now.duration_since(update_start);
update_start = now;
if updatable.update(delta_time.into()) == LoopControlFlow::Break {
break 'update_loop;
}
}
}
pub struct InSeries {
updatables: Vec<Box<dyn Updatable>>,
}
impl InSeries {
#[must_use]
pub fn new(mut updatables: Vec<Box<dyn Updatable>>) -> InSeries {
updatables.reverse();
InSeries { updatables }
}
}
impl Updatable for InSeries {
fn update(&mut self, delta_time: std::time::Duration) -> LoopControlFlow {
let Some(updatable) = &mut self.updatables.last_mut() else {
return LoopControlFlow::Break;
};
let res = updatable.update(delta_time);
match res {
LoopControlFlow::Continue => (),
LoopControlFlow::Break => {
self.updatables.pop();
}
}
LoopControlFlow::Continue
}
}
pub struct InParallel {
updatables: Vec<Box<dyn Updatable>>,
}
impl InParallel {
#[must_use]
pub fn new(updatables: Vec<Box<dyn Updatable>>) -> InParallel {
InParallel { updatables }
}
}
impl Updatable for InParallel {
fn update(&mut self, delta_time: std::time::Duration) -> LoopControlFlow {
self.updatables
.retain_mut(|up| up.update(delta_time) == LoopControlFlow::Continue);
if self.updatables.is_empty() {
LoopControlFlow::Break
} else {
LoopControlFlow::Continue
}
}
}
pub struct DoNothingFor {
remaining: std::time::Duration,
}
impl DoNothingFor {
#[must_use]
pub fn new(time: std::time::Duration) -> DoNothingFor {
DoNothingFor { remaining: time }
}
}
impl Updatable for DoNothingFor {
fn update(&mut self, delta_time: std::time::Duration) -> LoopControlFlow {
self.remaining = self.remaining.checked_sub(delta_time).unwrap_or_default();
if self.remaining == std::time::Duration::from_millis(0) {
LoopControlFlow::Break
} else {
LoopControlFlow::Continue
}
}
}
impl<F> Updatable for F
where
F: FnMut(std::time::Duration) -> LoopControlFlow + Send,
{
fn update(&mut self, delta_time: std::time::Duration) -> LoopControlFlow {
self(delta_time)
}
}