Skip to main content

cranpose_core/
frame_clock.rs

1use crate::runtime::RuntimeHandle;
2use crate::FrameCallbackId;
3use std::cell::RefCell;
4use std::future::Future;
5use std::pin::Pin;
6use std::rc::Rc;
7use std::task::{Context, Poll, Waker};
8
9#[derive(Clone)]
10pub struct FrameClock {
11    runtime: RuntimeHandle,
12}
13
14impl FrameClock {
15    pub fn new(runtime: RuntimeHandle) -> Self {
16        Self { runtime }
17    }
18
19    pub fn runtime_handle(&self) -> RuntimeHandle {
20        self.runtime.clone()
21    }
22
23    pub fn with_frame_nanos(
24        &self,
25        callback: impl FnOnce(u64) + 'static,
26    ) -> FrameCallbackRegistration {
27        let mut callback_opt = Some(callback);
28        let runtime = self.runtime.clone();
29        match runtime.register_frame_callback(move |time| {
30            if let Some(callback) = callback_opt.take() {
31                callback(time);
32            }
33        }) {
34            Some(id) => FrameCallbackRegistration::new(runtime, id),
35            None => FrameCallbackRegistration::inactive(runtime),
36        }
37    }
38
39    pub fn with_frame_millis(
40        &self,
41        callback: impl FnOnce(u64) + 'static,
42    ) -> FrameCallbackRegistration {
43        self.with_frame_nanos(move |nanos| {
44            let millis = nanos / 1_000_000;
45            callback(millis);
46        })
47    }
48
49    pub fn next_frame(&self) -> NextFrame {
50        NextFrame::new(self.clone())
51    }
52}
53
54pub struct FrameCallbackRegistration {
55    runtime: RuntimeHandle,
56    id: Option<FrameCallbackId>,
57}
58
59struct NextFrameState {
60    registration: Option<FrameCallbackRegistration>,
61    time: Option<u64>,
62    waker: Option<Waker>,
63}
64
65impl NextFrameState {
66    fn new() -> Self {
67        Self {
68            registration: None,
69            time: None,
70            waker: None,
71        }
72    }
73}
74
75pub struct NextFrame {
76    clock: FrameClock,
77    state: Rc<RefCell<NextFrameState>>,
78}
79
80impl NextFrame {
81    fn new(clock: FrameClock) -> Self {
82        Self {
83            clock,
84            state: Rc::new(RefCell::new(NextFrameState::new())),
85        }
86    }
87}
88
89impl Future for NextFrame {
90    type Output = u64;
91
92    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
93        if let Some(time) = self.state.borrow().time {
94            return Poll::Ready(time);
95        }
96
97        {
98            let mut state = self.state.borrow_mut();
99            state.waker = Some(cx.waker().clone());
100            if state.registration.is_none() {
101                drop(state);
102                let state = Rc::downgrade(&self.state);
103                let registration = self.clock.with_frame_nanos(move |time| {
104                    if let Some(state) = state.upgrade() {
105                        let mut state = state.borrow_mut();
106                        state.time = Some(time);
107                        state.registration.take();
108                        if let Some(waker) = state.waker.take() {
109                            waker.wake();
110                        }
111                    }
112                });
113                self.state.borrow_mut().registration = Some(registration);
114            }
115        }
116
117        if let Some(time) = self.state.borrow().time {
118            Poll::Ready(time)
119        } else {
120            Poll::Pending
121        }
122    }
123}
124
125impl Drop for NextFrame {
126    fn drop(&mut self) {
127        if let Some(registration) = self.state.borrow_mut().registration.take() {
128            drop(registration);
129        }
130    }
131}
132
133impl FrameCallbackRegistration {
134    fn new(runtime: RuntimeHandle, id: FrameCallbackId) -> Self {
135        Self {
136            runtime,
137            id: Some(id),
138        }
139    }
140
141    fn inactive(runtime: RuntimeHandle) -> Self {
142        Self { runtime, id: None }
143    }
144
145    pub fn cancel(mut self) {
146        if let Some(id) = self.id.take() {
147            self.runtime.cancel_frame_callback(id);
148        }
149    }
150}
151
152impl Drop for FrameCallbackRegistration {
153    fn drop(&mut self) {
154        if let Some(id) = self.id.take() {
155            self.runtime.cancel_frame_callback(id);
156        }
157    }
158}