Skip to main content

euv_engine/scheduler/
impl.rs

1use crate::*;
2
3/// Implements default configuration and state initialization for scheduler types.
4impl Default for SchedulerConfig {
5    fn default() -> SchedulerConfig {
6        SchedulerConfig::new(DEFAULT_FIXED_TIMESTEP, DEFAULT_MAX_FRAME_TIME)
7    }
8}
9
10/// Implements `Default` for `SchedulerState` as a freshly created stopped state.
11impl Default for SchedulerState {
12    fn default() -> SchedulerState {
13        SchedulerState::new(UNINITIALIZED_TIME)
14    }
15}
16
17/// Implements time retrieval and tick execution for `SchedulerState`.
18impl SchedulerState {
19    /// Returns the current high-resolution timestamp in seconds from `performance.now()`.
20    ///
21    /// # Returns
22    ///
23    /// - `f64` - The current time in seconds.
24    pub fn current_time() -> f64 {
25        let window_value: Window = window().expect("no global window exists");
26        let performance: JsValue = Reflect::get(
27            window_value.as_ref(),
28            &JsValue::from_str(PERFORMANCE_OBJECT),
29        )
30        .expect("performance object should exist");
31        let now_value: JsValue =
32            Reflect::get(&performance, &JsValue::from_str(PERFORMANCE_NOW_METHOD))
33                .expect("performance.now should exist");
34        let now_millis: f64 = now_value
35            .as_f64()
36            .expect("performance.now should return a number");
37        now_millis / 1000.0
38    }
39
40    /// Performs one tick of the fixed-timestep scheduler.
41    ///
42    /// Calculates the elapsed frame time, clamps it to `max_frame_time`, accumulates it,
43    /// then runs as many fixed updates as needed. Finally, computes the interpolation
44    /// factor and calls the render callback.
45    ///
46    /// # Arguments
47    ///
48    /// - `&SchedulerConfig` - The scheduler configuration.
49    /// - `&TickHandlerRc` - The handler receiving update and render callbacks.
50    pub fn tick(&mut self, config: &SchedulerConfig, handler: &TickHandlerRc) {
51        let current_time: f64 = Self::current_time();
52        let frame_time: f64 = if self.get_last_time() == UNINITIALIZED_TIME {
53            config.get_fixed_timestep()
54        } else {
55            current_time - self.get_last_time()
56        };
57        self.set_last_time(current_time);
58        let clamped_frame_time: f64 = frame_time.min(config.get_max_frame_time());
59        *self.get_mut_accumulator() += clamped_frame_time;
60        while self.get_accumulator() >= config.get_fixed_timestep() {
61            handler.borrow_mut().on_update(config.get_fixed_timestep());
62            *self.get_mut_accumulator() -= config.get_fixed_timestep();
63            *self.get_mut_update_count() += 1;
64        }
65        let interpolation: f64 = self.get_accumulator() / config.get_fixed_timestep();
66        handler.borrow_mut().on_render(interpolation);
67        *self.get_mut_frame_count() += 1;
68    }
69}
70
71/// Implements lifecycle management for `SchedulerHandle`.
72impl SchedulerHandle {
73    /// Stops the scheduler and cancels any pending animation frame request.
74    pub fn stop(&self) {
75        let state_rc: Rc<RefCell<SchedulerState>> = self.get_state().clone();
76        let mut state = state_rc.borrow_mut();
77        state.set_running(false);
78        if let Some(id) = state.get_mut_raf_id().take() {
79            let window_value: Window = window().expect("no global window exists");
80            let _ = window_value.cancel_animation_frame(id);
81        }
82        drop(state);
83        self.get_closure_cell().borrow_mut().take();
84    }
85
86    /// Returns whether the scheduler is currently running.
87    ///
88    /// # Returns
89    ///
90    /// - `bool` - True if the scheduler is running.
91    pub fn is_running(&self) -> bool {
92        self.get_state().borrow().get_running()
93    }
94
95    /// Returns the total number of fixed update steps executed.
96    ///
97    /// # Returns
98    ///
99    /// - `u64` - The update count.
100    pub fn update_count(&self) -> u64 {
101        self.get_state().borrow().get_update_count()
102    }
103
104    /// Returns the total number of render frames executed.
105    ///
106    /// # Returns
107    ///
108    /// - `u64` - The frame count.
109    pub fn frame_count(&self) -> u64 {
110        self.get_state().borrow().get_frame_count()
111    }
112
113    /// Starts the scheduler with the given configuration and handler.
114    ///
115    /// Creates a `requestAnimationFrame`-driven loop that calls `tick`
116    /// on each animation frame. The returned `SchedulerHandle` can be used to stop the scheduler.
117    ///
118    /// # Arguments
119    ///
120    /// - `SchedulerConfig` - The scheduler configuration.
121    /// - `TickHandlerRc` - The handler receiving update and render callbacks.
122    ///
123    /// # Returns
124    ///
125    /// - `SchedulerHandle` - A handle to control the running scheduler.
126    pub fn start(config: SchedulerConfig, handler: TickHandlerRc) -> SchedulerHandle {
127        let state: Rc<RefCell<SchedulerState>> =
128            Rc::new(RefCell::new(SchedulerState::new(UNINITIALIZED_TIME)));
129        let closure_cell: RafClosureCell = Rc::new(RefCell::new(None));
130        state.borrow_mut().set_running(true);
131        let state_clone: Rc<RefCell<SchedulerState>> = state.clone();
132        let closure_cell_clone: RafClosureCell = closure_cell.clone();
133        let handler_clone: TickHandlerRc = handler.clone();
134        let raf_closure: Closure<dyn FnMut()> = Closure::wrap(Box::new(move || {
135            {
136                let mut state_ref = state_clone.borrow_mut();
137                if !state_ref.get_running() {
138                    return;
139                }
140                state_ref.tick(&config, &handler_clone);
141            }
142            if state_clone.borrow().get_running() {
143                let window_value: Window = window().expect("no global window exists");
144                let cell: RafClosureCell = closure_cell_clone.clone();
145                let id: i32 = window_value
146                    .request_animation_frame(
147                        cell.borrow()
148                            .as_ref()
149                            .expect("raf closure should exist")
150                            .as_ref()
151                            .unchecked_ref(),
152                    )
153                    .unwrap_or(0);
154                state_clone.borrow_mut().set_raf_id(Some(id));
155            }
156        }) as Box<dyn FnMut()>);
157        let window_value: Window = window().expect("no global window exists");
158        let id: i32 = window_value
159            .request_animation_frame(raf_closure.as_ref().unchecked_ref())
160            .unwrap_or(0);
161        state.borrow_mut().set_raf_id(Some(id));
162        *closure_cell.borrow_mut() = Some(raf_closure);
163        SchedulerHandle::new(state, closure_cell)
164    }
165}