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_mut(pub(crate))]
62 #[set(pub(crate))]
63 pub(crate) state: Rc<EngineCell<SchedulerState>>,
64 /// The shared closure cell keeping the RAF callback alive. Held
65 /// behind `MaybeEngineCell` because the cell is empty both
66 /// before `spawn` runs and after cleanup tears the closure down.
67 #[get_mut(pub(crate))]
68 #[set(pub(crate))]
69 pub(crate) closure_cell: RafClosureCell,
70}
71
72/// A registry of [`Updatable`] tasks driven by the fixed-timestep scheduler.
73///
74/// The scheduler itself only knows how to run a single [`TickHandler`]; every
75/// other simulation object in the engine — `Timer`, `Tween`,
76/// `ParticleEmitter`, `Entity`, `Animator`, `SceneManager`, and the
77/// `PhysicsWorld2D` / `PhysicsWorld3D` containers — exposes its advancement
78/// through the [`Updatable`] trait instead. This registry is the driver that
79/// gives those objects a heartbeat: [`SchedulerState::tick`] calls
80/// [`TaskRegistry::update_all`] once per fixed step, immediately *after* the
81/// handler's `on_update` callback returns, so gameplay logic registered in
82/// `on_update` sees task state that has already advanced this step.
83///
84/// Tasks are updated in registration order, which makes the relative ordering
85/// of independent tasks explicit and reproducible rather than dependent on
86/// container iteration order.
87#[derive(Data, Default, New)]
88pub struct TaskRegistry {
89 /// The registered tasks, in registration order. `Box<dyn Updatable>`
90 /// erases the concrete task type so heterogeneous tasks (a `Timer` next
91 /// to a `Tween<f64>` next to a `ParticleEmitter`) coexist in one list.
92 #[get_mut(pub(crate))]
93 pub(crate) tasks: Vec<Box<dyn Updatable>>,
94}
95
96/// A handle to a task registered with a [`TaskRegistry`].
97///
98/// The handle is returned by [`TaskRegistry::register`] and is the only way
99/// to remove that task later. It identifies the task by its index in the
100/// registry's insertion-ordered task list, which keeps registration and
101/// removal O(1) for the common append-then-remove-last pattern.
102#[derive(Clone, Copy, Data, Debug, New, PartialEq, PartialOrd)]
103pub struct TaskHandle {
104 /// The zero-based index of the task in the registry's task list.
105 #[get(type(copy))]
106 pub(crate) id: u64,
107}