use std::future::Future;
use std::rc::Rc;
use std::task::{Context, Waker};
use oxframe_protocol::Meta;
use crate::flow::{Task, TaskState};
use crate::runtime::{self, Runtime};
pub struct Executor {
rt: Rc<Runtime>,
root: Option<Task>,
tasks: Vec<Task>,
next_frame: u32,
}
impl Executor {
pub fn new(fps: f64, meta: Meta, seed: u64, root: impl Future<Output = ()> + 'static) -> Self {
Self {
rt: Runtime::new(fps, meta, seed),
root: Some(Task {
fut: Box::pin(root),
time: 0.0,
state: Rc::new(TaskState {
finished: Default::default(),
cancelled: Default::default(),
}),
}),
tasks: Vec::new(),
next_frame: 0,
}
}
pub fn enter<R>(&self, f: impl FnOnce() -> R) -> R {
let _guard = runtime::install(&self.rt);
f()
}
pub fn step(&mut self) -> bool {
let Some(root) = self.root.as_mut() else {
return true;
};
let _guard = runtime::install(&self.rt);
self.rt.frame.set(self.next_frame);
let mut cx = Context::from_waker(Waker::noop());
let root_done = root.poll(&mut cx);
self.tasks.retain_mut(|t| !t.poll(&mut cx));
loop {
let new: Vec<Task> = std::mem::take(&mut *self.rt.spawned.borrow_mut());
if new.is_empty() {
break;
}
for mut t in new {
if !t.poll(&mut cx) {
self.tasks.push(t);
}
}
}
self.next_frame += 1;
if root_done {
self.root = None;
self.tasks.clear();
}
root_done
}
pub fn is_finished(&self) -> bool {
self.root.is_none()
}
pub fn frames_done(&self) -> u32 {
self.next_frame
}
pub fn fps(&self) -> f64 {
self.rt.fps
}
pub fn meta(&self) -> Meta {
self.rt.meta.borrow().clone()
}
pub fn take_meta_changed(&self) -> bool {
self.rt.meta_changed.replace(false)
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use super::*;
use crate::{
Signal, all, any, loop_while, next_frame, presets, repeat, sequence, spawn, stagger,
wait_for, wait_until,
};
fn run<T>(fps: f64, fut: impl Future<Output = ()> + 'static, f: impl Fn() -> T) -> Vec<T> {
let mut ex = Executor::new(fps, Meta::default(), 0, fut);
let mut out = Vec::new();
for _ in 0..10_000 {
let done = ex.step();
out.push(f());
if done {
return out;
}
}
panic!("scene did not finish");
}
#[test]
fn tween_takes_exact_frames_and_hits_target() {
let s = Signal::new(0.0f64);
let s2 = s.clone();
let values = run(
60.0,
async move { s2.to(1.0, 1.0).ease(presets::LINEAR).await },
|| s.get(),
);
assert_eq!(values.len(), 61);
assert_eq!(values[0], 0.0);
assert!((values[30] - 0.5).abs() < 1e-12);
assert_eq!(values[60], 1.0);
}
#[test]
fn chained_waits_do_not_drift() {
let frames = run(
24.0,
async {
for _ in 0..30 {
wait_for(0.1).await;
}
},
|| (),
);
assert_eq!(frames.len(), 3 * 24 + 1);
}
#[test]
fn all_waits_for_longest_and_continues_from_its_end() {
let log = Rc::new(RefCell::new(Vec::new()));
let l = log.clone();
run(
10.0,
async move {
all((wait_for(0.5), wait_for(1.0), async {})).await;
l.borrow_mut().push((crate::frame(), crate::time()));
},
|| (),
);
assert_eq!(*log.borrow(), vec![(10, 1.0)]);
}
#[test]
fn sequence_staggers_starts() {
let a = Signal::new(0.0f64);
let b = Signal::new(0.0f64);
let (a2, b2) = (a.clone(), b.clone());
let values = run(
10.0,
async move {
sequence(
0.5,
[
a2.to(1.0, 1.0).ease(presets::LINEAR),
b2.to(1.0, 1.0).ease(presets::LINEAR),
],
)
.await;
},
|| (a.get(), b.get()),
);
assert_eq!(values.len(), 16);
assert!((values[5].0 - 0.5).abs() < 1e-12);
assert_eq!(values[5].1, 0.0);
assert!((values[10].1 - 0.5).abs() < 1e-12);
}
#[test]
fn spawn_runs_in_background_and_join_waits() {
let s = Signal::new(0.0f64);
let s2 = s.clone();
let values = run(
10.0,
async move {
let h = spawn(async move { s2.to(1.0, 0.5).ease(presets::LINEAR).await });
next_frame().await;
h.await;
},
|| s.get(),
);
assert!((values[2] - 0.4).abs() < 1e-12);
assert_eq!(*values.last().unwrap(), 1.0);
}
#[test]
fn wait_until_records_default_and_reads_meta() {
let mut ex = Executor::new(10.0, Meta::default(), 0, async {
wait_for(0.3).await;
wait_until("beat").await;
});
while !ex.step() {}
assert!((ex.meta().markers["beat"] - 0.3).abs() < 1e-12);
assert!(ex.take_meta_changed());
let mut meta = Meta::default();
meta.markers.insert("beat".into(), 1.0);
let mut ex = Executor::new(10.0, meta, 0, async {
wait_for(0.3).await;
wait_until("beat").await;
});
while !ex.step() {}
assert_eq!(ex.frames_done(), 11);
assert!(!ex.take_meta_changed());
}
#[test]
fn random_is_seeded() {
let draw = |seed| {
let out = Rc::new(RefCell::new(Vec::new()));
let o = out.clone();
let mut ex = Executor::new(10.0, Meta::default(), seed, async move {
for _ in 0..4 {
o.borrow_mut().push(crate::random());
}
});
ex.step();
out.take()
};
assert_eq!(draw(1), draw(1));
assert_ne!(draw(1), draw(2));
assert!(draw(3).iter().all(|v| (0.0..1.0).contains(v)));
}
fn end_of(fps: f64, fut: impl Future<Output = ()> + 'static) -> (u32, f64) {
let log = Rc::new(RefCell::new(None));
let l = log.clone();
run(
fps,
async move {
fut.await;
*l.borrow_mut() = Some((crate::frame(), crate::time()));
},
|| (),
);
log.borrow().expect("logged")
}
#[test]
fn all_takes_vecs_arrays_and_iterators() {
let durations = [0.5, 1.0, 0.2];
assert_eq!(
end_of(10.0, async move {
all(durations.map(wait_for)).await;
})
.1,
1.0
);
assert_eq!(
end_of(10.0, async move {
all(durations.iter().map(|&d| wait_for(d))).await;
})
.1,
1.0
);
assert_eq!(
end_of(10.0, async move {
all(vec![wait_for(0.3), wait_for(0.7)]).await;
})
.1,
0.7
);
let out = Rc::new(RefCell::new(Vec::new()));
let o = out.clone();
end_of(10.0, async move {
*o.borrow_mut() = all((1..=3).map(|i| async move { i * 10 })).await;
});
assert_eq!(*out.borrow(), vec![10, 20, 30]);
assert_eq!(
end_of(10.0, async {
all(Vec::<crate::flow::WaitFor>::new()).await;
})
.1,
0.0
);
}
#[test]
fn any_completes_with_the_first_and_continues_from_its_end() {
let s = Signal::new(0.0f64);
let s2 = s.clone();
let first = Rc::new(RefCell::new(None));
let f = first.clone();
let (frame, time) = end_of(10.0, async move {
let i = any((
wait_for(2.0),
s2.to(1.0, 2.0).ease(presets::LINEAR),
wait_for(0.5),
))
.await;
*f.borrow_mut() = Some(i);
});
assert_eq!(*first.borrow(), Some(2));
assert_eq!((frame, time), (5, 0.5));
assert!((s.get() - 0.25).abs() < 1e-12, "{}", s.get());
}
#[test]
fn stagger_starts_each_item_later() {
let signals: Vec<Signal<f64>> = (0..3).map(|_| Signal::new(0.0)).collect();
let watch = signals.clone();
let values = run(
10.0,
async move {
stagger(0.2, &signals, |s| s.to(1.0, 1.0).ease(presets::LINEAR)).await;
},
move || watch.iter().map(Signal::get).collect::<Vec<_>>(),
);
assert_eq!(values.len(), 15);
let at_half = &values[5];
assert!((at_half[0] - 0.5).abs() < 1e-12);
assert!((at_half[1] - 0.3).abs() < 1e-12);
assert!((at_half[2] - 0.1).abs() < 1e-12);
}
#[test]
fn repeat_and_loop_while() {
assert_eq!(end_of(10.0, repeat(3, || wait_for(0.5))).1, 1.5);
let left = Rc::new(std::cell::Cell::new(4));
let l = left.clone();
let end = end_of(
10.0,
loop_while(
move || {
l.set(l.get() - 1);
l.get() >= 0
},
|| wait_for(0.25),
),
);
assert_eq!(end.1, 1.0);
assert_eq!(left.get(), -1);
}
}