euv_engine/scheduler/struct.rs
1use super::*;
2
3/// Configuration parameters for the fixed-timestep scheduler.
4#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
5pub struct SchedulerConfig {
6 /// The fixed simulation timestep in seconds (e.g., 1/60 for 60 Hz updates).
7 #[get(type(copy))]
8 pub(crate) fixed_timestep: f64,
9 /// The maximum allowed frame time in seconds before the scheduler starts dropping updates.
10 #[get(type(copy))]
11 pub(crate) max_frame_time: f64,
12}
13
14/// The runtime state of a scheduler instance.
15#[derive(Clone, Data, Debug, New, PartialEq)]
16pub struct SchedulerState {
17 /// The accumulated time waiting to be processed by fixed updates.
18 #[get(type(copy))]
19 #[get_mut(pub(crate))]
20 #[set(pub(crate))]
21 #[new(skip)]
22 pub(crate) accumulator: f64,
23 /// The timestamp of the previous frame in seconds, or `UNINITIALIZED_TIME` before the first frame.
24 #[get(type(copy))]
25 #[get_mut(pub(crate))]
26 #[set(pub(crate))]
27 pub(crate) last_time: f64,
28 /// Whether the scheduler is currently running and scheduling animation frames.
29 #[get(type(copy))]
30 #[get_mut(pub(crate))]
31 #[set(pub(crate))]
32 #[new(skip)]
33 pub(crate) running: bool,
34 /// The most recent `requestAnimationFrame` ID, used to cancel the next frame.
35 #[get(type(copy))]
36 #[get_mut(pub(crate))]
37 #[set(pub(crate))]
38 #[new(skip)]
39 pub(crate) raf_id: Option<i32>,
40 /// The total number of fixed update steps executed since the scheduler started.
41 #[get(type(copy))]
42 #[get_mut(pub(crate))]
43 #[set(pub(crate))]
44 #[new(skip)]
45 pub(crate) update_count: u64,
46 /// The total number of render frames executed since the scheduler started.
47 #[get(type(copy))]
48 #[get_mut(pub(crate))]
49 #[set(pub(crate))]
50 #[new(skip)]
51 pub(crate) frame_count: u64,
52}
53
54/// A handle to a running scheduler, allowing the caller to stop it later.
55#[derive(Clone, Data, New)]
56pub struct SchedulerHandle {
57 /// The shared scheduler state, held behind `EngineCell` (an
58 /// `UnsafeCell`-backed `Sync` newtype) so multiple closure
59 /// captures can mutate it without `RefCell`'s runtime borrow
60 /// check. Mirrors `core::reactive::schedule` shape.
61 #[get]
62 #[get_mut(pub(crate))]
63 #[set(pub(crate))]
64 pub(crate) state: Rc<EngineCell<SchedulerState>>,
65 /// The shared closure cell keeping the RAF callback alive. Held
66 /// behind `MaybeEngineCell` because the cell is empty both
67 /// before `spawn` runs and after cleanup tears the closure down.
68 #[get]
69 #[get_mut(pub(crate))]
70 #[set(pub(crate))]
71 pub(crate) closure_cell: RafClosureCell,
72}
73
74/// A registry of [`Updatable`] tasks driven by the fixed-timestep scheduler.
75///
76/// The scheduler itself only knows how to run a single [`TickHandler`]; every
77/// other simulation object in the engine — `Timer`, `Tween`,
78/// `ParticleEmitter`, `Entity`, `Animator`, `SceneManager`, and the
79/// `PhysicsWorld2D` / `PhysicsWorld3D` containers — exposes its advancement
80/// through the [`Updatable`] trait instead. This registry is the driver that
81/// gives those objects a heartbeat: [`SchedulerState::tick`] calls
82/// [`TaskRegistry::update_all`] once per fixed step, immediately *after* the
83/// handler's `on_update` callback returns, so gameplay logic registered in
84/// `on_update` sees task state that has already advanced this step.
85///
86/// Tasks are updated in registration order, which makes the relative ordering
87/// of independent tasks explicit and reproducible rather than dependent on
88/// container iteration order.
89#[derive(Data, Default, New)]
90pub struct TaskRegistry {
91 /// The registered tasks, in registration order. `Box<dyn Updatable>`
92 /// erases the concrete task type so heterogeneous tasks (a `Timer` next
93 /// to a `Tween<f64>` next to a `ParticleEmitter`) coexist in one list.
94 #[get]
95 #[get_mut(pub(crate))]
96 pub(crate) tasks: Vec<Box<dyn Updatable>>,
97}
98
99/// A handle to a task registered with a [`TaskRegistry`].
100///
101/// The handle is returned by [`TaskRegistry::register`] and is the only way
102/// to remove that task later. It identifies the task by its index in the
103/// registry's insertion-ordered task list, which keeps registration and
104/// removal O(1) for the common append-then-remove-last pattern.
105#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
106pub struct TaskHandle {
107 /// The zero-based index of the task in the registry's task list.
108 #[get(type(copy))]
109 pub(crate) id: u64,
110}