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