Skip to main content

paint/
refresh_driver.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::{Cell, LazyCell, RefCell};
6use std::rc::Rc;
7use std::sync::Arc;
8use std::sync::atomic::{AtomicBool, Ordering};
9use std::thread::{self, JoinHandle};
10use std::time::Duration;
11
12use crossbeam_channel::{RecvTimeoutError, Sender};
13use embedder_traits::{EventLoopWaker, RefreshDriver};
14use log::warn;
15use servo_constellation_traits::EmbedderToConstellationMessage;
16use timers::{BoxedTimerCallback, TimerEventRequest, TimerScheduler};
17
18use crate::painter::Painter;
19use crate::webview_renderer::WebViewRenderer;
20
21/// The [`BaseRefreshDriver`] is a "base class" for [`RefreshDriver`] trait
22/// implementations. It encapsulates shared behavior so that it does not have to be
23/// implemented by all trait implementations. It is responsible for providing
24/// [`RefreshDriver`] implementations with a callback that is used to wake up the event
25/// loop and trigger frame readiness.
26pub(crate) struct BaseRefreshDriver {
27    /// Whether or not the [`BaseRefreshDriver`] is waiting for a frame. Once the [`RefreshDriver`]
28    /// informs the base that a frame start happened, this becomes false.
29    waiting_for_frame: Arc<AtomicBool>,
30    /// An [`EventLooperWaker`] which alerts the main UI event loop when a frame start occurs.
31    event_loop_waker: Box<dyn EventLoopWaker>,
32    /// A list of internal observers that watch for frame starts.
33    observers: RefCell<Vec<Rc<dyn RefreshDriverObserver>>>,
34    /// The implementation of the [`RefreshDriver`]. By default this is a simple timer, but the
35    /// embedder can install a custom driver, such as one that is run via the hardware vsync signal.
36    refresh_driver: Rc<dyn RefreshDriver>,
37}
38
39impl BaseRefreshDriver {
40    pub(crate) fn new(
41        event_loop_waker: Box<dyn EventLoopWaker>,
42        refresh_driver: Option<Rc<dyn RefreshDriver>>,
43        timer_refresh_driver: &LazyCell<Rc<TimerRefreshDriver>>,
44    ) -> Self {
45        let refresh_driver = refresh_driver.unwrap_or_else(|| (**timer_refresh_driver).clone());
46        Self {
47            waiting_for_frame: Arc::new(AtomicBool::new(false)),
48            event_loop_waker,
49            observers: Default::default(),
50            refresh_driver,
51        }
52    }
53
54    pub(crate) fn add_observer(&self, observer: Rc<dyn RefreshDriverObserver>) {
55        let mut observers = self.observers.borrow_mut();
56        observers.push(observer);
57
58        // If this is the first observer, make sure to observe the next frame.
59        if observers.len() == 1 {
60            self.observe_next_frame();
61        }
62    }
63
64    /// Notify the observers that a frame is about to be painted. Returns whether they were
65    /// notified, which they are not while waiting for the next frame to start. When they
66    /// are not notified, it is the caller's responsibility to ensure that a paint happens
67    /// once the next frame starts, so that the observers are notified then.
68    pub(crate) fn notify_will_paint(&self, painter: &mut Painter) -> bool {
69        // If we are still waiting for the frame to timeout this paint was caused for some
70        // non-animation related reason and we should wait until the frame start to notify the
71        // observers.
72        if self.wait_to_paint() {
73            return false;
74        }
75
76        // Limit the borrow of `self.observers` to the minimum here.
77        let still_has_observers = {
78            let mut observers = self.observers.borrow_mut();
79            observers.retain(|observer| observer.frame_started(painter));
80            !observers.is_empty()
81        };
82
83        if still_has_observers {
84            self.observe_next_frame();
85        }
86        true
87    }
88
89    fn observe_next_frame(&self) {
90        self.waiting_for_frame.store(true, Ordering::Relaxed);
91
92        let waiting_for_frame = self.waiting_for_frame.clone();
93        let event_loop_waker = self.event_loop_waker.clone_box();
94        self.refresh_driver.observe_next_frame(Box::new(move || {
95            waiting_for_frame.store(false, Ordering::Relaxed);
96            event_loop_waker.wake();
97        }));
98    }
99
100    /// Whether or not the renderer should trigger a message to the embedder to request a
101    /// repaint. This might be true if we are animating and we are still waiting for a new
102    /// frame from the `RefreshDriver`.
103    pub(crate) fn wait_to_paint(&self) -> bool {
104        !self.observers.borrow().is_empty() && self.waiting_for_frame.load(Ordering::Relaxed)
105    }
106}
107
108/// A [`RefreshDriverObserver`] is an internal subscriber to frame start signals from the
109/// [`RefreshDriver`]. Examples of these kind of observers would be one that triggers new
110/// animation frames right after vsync signals or one that handles touch interactions once
111/// per frame.
112pub(crate) trait RefreshDriverObserver {
113    /// Informs the observer that a new frame has started. The observer should return
114    /// `true` to keep observing or `false` if wants to stop observing and should be
115    /// removed by the [`BaseRefreshDriver`].
116    fn frame_started(&self, painter: &mut Painter) -> bool;
117}
118
119/// The [`AnimationRefreshDriverObserver`] is the default implementation of a
120/// [`RefreshDriver`] on systems without vsync hardware integration. It has a very basic
121/// way of triggering frames using a timer. It prevents new animation frames until the
122/// timer has fired.
123pub(crate) struct AnimationRefreshDriverObserver {
124    /// The channel on which messages can be sent to the Constellation.
125    pub(crate) constellation_sender: Sender<EmbedderToConstellationMessage>,
126
127    /// Whether or not we are currently animating via a timer.
128    pub(crate) animating: Cell<bool>,
129}
130
131impl AnimationRefreshDriverObserver {
132    pub(crate) fn new(constellation_sender: Sender<EmbedderToConstellationMessage>) -> Self {
133        Self {
134            constellation_sender,
135            animating: Default::default(),
136        }
137    }
138
139    pub(crate) fn notify_animation_state_changed(
140        &self,
141        webview_renderer: &WebViewRenderer,
142    ) -> bool {
143        if !webview_renderer.animating() {
144            // If no other WebView is animating we will officially stop animating once the
145            // next frame has been painted.
146            return false;
147        }
148
149        if let Err(error) =
150            self.constellation_sender
151                .send(EmbedderToConstellationMessage::TickAnimation(vec![
152                    webview_renderer.id,
153                ]))
154        {
155            warn!("Sending tick to constellation failed ({error:?}).");
156        }
157
158        if self.animating.get() {
159            return false;
160        }
161
162        self.animating.set(true);
163        true
164    }
165}
166
167impl RefreshDriverObserver for AnimationRefreshDriverObserver {
168    fn frame_started(&self, painter: &mut Painter) -> bool {
169        // If any WebViews are animating ask them to paint again for another animation tick.
170        let animating_webviews = painter.animating_webviews();
171
172        // If nothing is animating any longer, update our state and exit early without requesting
173        // any new frames.
174        if animating_webviews.is_empty() {
175            self.animating.set(false);
176            return false;
177        }
178
179        // Request new animation frames from all animating WebViews.
180        if let Err(error) =
181            self.constellation_sender
182                .send(EmbedderToConstellationMessage::TickAnimation(
183                    animating_webviews,
184                ))
185        {
186            warn!("Sending tick to constellation failed ({error:?}).");
187            return false;
188        }
189
190        self.animating.set(true);
191        true
192    }
193}
194
195enum TimerThreadMessage {
196    Request(TimerEventRequest),
197    Quit,
198}
199
200/// A thread that manages a [`TimerScheduler`] running in the background of the
201/// [`RefreshDriver`]. This is necessary because we need a reliable way of waking up the
202/// embedder's main thread, which may just be sleeping until the `EventLoopWaker` asks it
203/// to wake up.
204///
205/// It would be nice to integrate this somehow into the embedder thread, but it would
206/// require both some communication with the embedder and for all embedders to be well
207/// behave respecting wakeup timeouts -- a bit too much to ask at the moment.
208pub(crate) struct TimerRefreshDriver {
209    sender: Sender<TimerThreadMessage>,
210    join_handle: Option<JoinHandle<()>>,
211}
212
213impl Default for TimerRefreshDriver {
214    fn default() -> Self {
215        let (sender, receiver) = crossbeam_channel::unbounded::<TimerThreadMessage>();
216        let join_handle = thread::Builder::new()
217            .name(String::from("PaintTimerThread"))
218            .spawn(move || {
219                let mut scheduler = TimerScheduler::default();
220
221                loop {
222                    let recv_result = match scheduler.next_deadline() {
223                        Some(deadline) => receiver.recv_deadline(deadline),
224                        None => receiver.recv().map_err(|_| RecvTimeoutError::Disconnected),
225                    };
226                    match recv_result {
227                        Ok(TimerThreadMessage::Request(request)) => {
228                            scheduler.schedule_timer(request);
229                        },
230                        Err(RecvTimeoutError::Timeout) => scheduler.dispatch_completed_timers(),
231                        Ok(TimerThreadMessage::Quit) | Err(RecvTimeoutError::Disconnected) => {
232                            return;
233                        },
234                    }
235                }
236            })
237            .expect("Could not create RefreshDriver timer thread.");
238
239        Self {
240            sender,
241            join_handle: Some(join_handle),
242        }
243    }
244}
245
246impl TimerRefreshDriver {
247    pub(crate) fn queue_timer(&self, duration: Duration, callback: BoxedTimerCallback) {
248        let _ = self
249            .sender
250            .send(TimerThreadMessage::Request(TimerEventRequest {
251                callback,
252                duration,
253            }));
254    }
255}
256
257impl RefreshDriver for TimerRefreshDriver {
258    fn observe_next_frame(&self, new_start_frame_callback: Box<dyn Fn() + Send + 'static>) {
259        const FRAME_DURATION: Duration = Duration::from_millis(1000 / 120);
260        self.queue_timer(FRAME_DURATION, new_start_frame_callback);
261    }
262}
263
264impl Drop for TimerRefreshDriver {
265    fn drop(&mut self) {
266        let _ = self.sender.send(TimerThreadMessage::Quit);
267        if let Some(join_handle) = self.join_handle.take() {
268            let _ = join_handle.join();
269        }
270    }
271}