paint/
paint.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, Ref, RefCell, RefMut};
6use std::collections::HashMap;
7use std::env;
8use std::fs::create_dir_all;
9use std::rc::Rc;
10use std::time::{SystemTime, UNIX_EPOCH};
11
12use base::generic_channel::{self, GenericSender, RoutedReceiver};
13use base::id::{PainterId, PipelineId, WebViewId};
14use bitflags::bitflags;
15use canvas_traits::webgl::{WebGLContextId, WebGLThreads};
16use constellation_traits::EmbedderToConstellationMessage;
17use crossbeam_channel::Sender;
18use dpi::PhysicalSize;
19use embedder_traits::{
20    EventLoopWaker, InputEventAndId, InputEventId, InputEventResult, ScreenshotCaptureError,
21    Scroll, ShutdownState, ViewportDetails, WebViewPoint, WebViewRect,
22};
23use euclid::{Scale, Size2D};
24use image::RgbaImage;
25use ipc_channel::ipc::{self};
26use log::{debug, warn};
27use paint_api::rendering_context::RenderingContext;
28use paint_api::{
29    PaintMessage, PaintProxy, PainterSurfmanDetails, PainterSurfmanDetailsMap,
30    WebRenderExternalImageIdManager, WebViewTrait,
31};
32use profile_traits::mem::{
33    ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
34};
35use profile_traits::path;
36use profile_traits::time::{self as profile_time};
37use servo_config::pref;
38use servo_geometry::DeviceIndependentPixel;
39use style_traits::CSSPixel;
40use surfman::Device;
41use surfman::chains::SwapChains;
42use webgl::WebGLComm;
43use webgl::webgl_thread::WebGLContextBusyMap;
44#[cfg(feature = "webgpu")]
45use webgpu::canvas_context::WebGpuExternalImageMap;
46use webrender::{CaptureBits, MemoryReport};
47use webrender_api::units::{DevicePixel, DevicePoint};
48use webrender_api::{FontInstanceKey, FontKey, ImageKey};
49
50use crate::InitialPaintState;
51use crate::painter::Painter;
52use crate::webview_renderer::UnknownWebView;
53
54/// An option to control what kind of WebRender debugging is enabled while Servo is running.
55#[derive(Copy, Clone)]
56pub enum WebRenderDebugOption {
57    Profiler,
58    TextureCacheDebug,
59    RenderTargetDebug,
60}
61
62/// [`Paint`] is Servo's rendering subsystem. It has a few responsibilities:
63///
64/// 1. Maintain a WebRender instance for each [`RenderingContext`] that Servo knows about.
65///    [`RenderingContext`]s are per-`WebView`, but more than one `WebView` can use the same
66///    [`RenderingContext`]. This allows multiple `WebView`s to share the same WebRender
67///    instance which is more efficient. This is useful for tabbed web browsers.
68/// 2. Receive display lists from the layout of all of the currently active `Pipeline`s
69///    (frames). These display lists are sent to WebRender, and new frames are generated.
70///    Once the frame is ready the [`Painter`] for the WebRender instance will ask libservo
71///    to inform the embedder that a new frame is ready so that it can trigger a paint.
72/// 3. Drive animation and animation callback updates. Animation updates should ideally be
73///    coordinated with the system vsync signal, so the `RefreshDriver` is exposed in the
74///    API to allow the embedder to do this. The [`Painter`] then asks its `WebView`s to
75///    update their rendering, which triggers layouts.
76/// 4. Eagerly handle scrolling and touch events. In order to avoid latency when handling
77///    these kind of actions, each [`Painter`] will eagerly process touch events and
78///    perform panning and zooming operations on their WebRender contents -- informing the
79///    WebView contents asynchronously.
80///
81/// `Paint` and all of its contained structs should **never** block on the Constellation,
82/// because sometimes the Constellation blocks on us.
83pub struct Paint {
84    /// All of the [`Painters`] for this [`Paint`]. Each [`Painter`] handles painting to
85    /// a single [`RenderingContext`].
86    painters: Vec<Rc<RefCell<Painter>>>,
87
88    /// A [`PaintProxy`] which can be used to allow other parts of Servo to communicate
89    /// with this [`Paint`].
90    pub(crate) paint_proxy: PaintProxy,
91
92    /// An [`EventLoopWaker`] used to wake up the main embedder event loop when the renderer needs
93    /// to run.
94    pub(crate) event_loop_waker: Box<dyn EventLoopWaker>,
95
96    /// Tracks whether we are in the process of shutting down, or have shut down and
97    /// should shut down `Paint`. This is shared with the `Servo` instance.
98    shutdown_state: Rc<Cell<ShutdownState>>,
99
100    /// The port on which we receive messages.
101    paint_receiver: RoutedReceiver<PaintMessage>,
102
103    /// The channel on which messages can be sent to the constellation.
104    pub(crate) embedder_to_constellation_sender: Sender<EmbedderToConstellationMessage>,
105
106    /// The [`WebRenderExternalImageIdManager`] used to generate new `ExternalImageId`s.
107    webrender_external_image_id_manager: WebRenderExternalImageIdManager,
108
109    /// A [`HashMap`] of [`PainterId`] to the Surfaman types (`Device`, `Adapter`) that
110    /// are specific to a particular [`Painter`].
111    pub(crate) painter_surfman_details_map: PainterSurfmanDetailsMap,
112
113    /// A [`HashMap`] of `WebGLContextId` to a usage count. This count indicates when
114    /// WebRender is still rendering the context. This is used to ensure properly clean
115    /// up of all Surfman `Surface`s.
116    pub(crate) busy_webgl_contexts_map: WebGLContextBusyMap,
117
118    /// The [`WebGLThreads`] for this renderer.
119    webgl_threads: WebGLThreads,
120
121    /// The shared [`SwapChains`] used by [`WebGLThreads`] for this renderer.
122    pub(crate) swap_chains: SwapChains<WebGLContextId, Device>,
123
124    /// The channel on which messages can be sent to the time profiler.
125    time_profiler_chan: profile_time::ProfilerChan,
126
127    /// A handle to the memory profiler which will automatically unregister
128    /// when it's dropped.
129    _mem_profiler_registration: ProfilerRegistration,
130
131    /// Some XR devices want to run on the main thread.
132    #[cfg(feature = "webxr")]
133    webxr_main_thread: RefCell<webxr::MainThreadRegistry>,
134
135    /// An map of external images shared between all `WebGpuExternalImages`.
136    #[cfg(feature = "webgpu")]
137    webgpu_image_map: std::cell::OnceCell<WebGpuExternalImageMap>,
138}
139
140/// Why we need to be repainted. This is used for debugging.
141#[derive(Clone, Copy, Default, PartialEq)]
142pub(crate) struct RepaintReason(u8);
143
144bitflags! {
145    impl RepaintReason: u8 {
146        /// We're performing the single repaint in headless mode.
147        const ReadyForScreenshot = 1 << 0;
148        /// We're performing a repaint to run an animation.
149        const ChangedAnimationState = 1 << 1;
150        /// A new WebRender frame has arrived.
151        const NewWebRenderFrame = 1 << 2;
152        /// The window has been resized and will need to be synchronously repainted.
153        const Resize = 1 << 3;
154        /// A fling has started and a repaint needs to happen to process the animation.
155        const StartedFlinging = 1 << 4;
156    }
157}
158
159impl Paint {
160    pub fn new(state: InitialPaintState) -> Rc<RefCell<Self>> {
161        let registration = state.mem_profiler_chan.prepare_memory_reporting(
162            "paint".into(),
163            state.paint_proxy.clone(),
164            PaintMessage::CollectMemoryReport,
165        );
166
167        let webrender_external_image_id_manager = WebRenderExternalImageIdManager::default();
168        let painter_surfman_details_map = PainterSurfmanDetailsMap::default();
169        let WebGLComm {
170            webgl_threads,
171            swap_chains,
172            busy_webgl_context_map,
173            #[cfg(feature = "webxr")]
174            webxr_layer_grand_manager,
175        } = WebGLComm::new(
176            state.paint_proxy.cross_process_paint_api.clone(),
177            webrender_external_image_id_manager.clone(),
178            painter_surfman_details_map.clone(),
179        );
180
181        // Create the WebXR main thread
182        #[cfg(feature = "webxr")]
183        let webxr_main_thread = {
184            use servo_config::pref;
185
186            let mut webxr_main_thread = webxr::MainThreadRegistry::new(
187                state.event_loop_waker.clone(),
188                webxr_layer_grand_manager,
189            )
190            .expect("Failed to create WebXR device registry");
191            if pref!(dom_webxr_enabled) {
192                state.webxr_registry.register(&mut webxr_main_thread);
193            }
194            webxr_main_thread
195        };
196
197        Rc::new(RefCell::new(Paint {
198            painters: Default::default(),
199            paint_proxy: state.paint_proxy,
200            event_loop_waker: state.event_loop_waker,
201            shutdown_state: state.shutdown_state,
202            paint_receiver: state.receiver,
203            embedder_to_constellation_sender: state.embedder_to_constellation_sender.clone(),
204            webrender_external_image_id_manager,
205            webgl_threads,
206            swap_chains,
207            time_profiler_chan: state.time_profiler_chan,
208            _mem_profiler_registration: registration,
209            painter_surfman_details_map,
210            busy_webgl_contexts_map: busy_webgl_context_map,
211            #[cfg(feature = "webxr")]
212            webxr_main_thread: RefCell::new(webxr_main_thread),
213            #[cfg(feature = "webgpu")]
214            webgpu_image_map: Default::default(),
215        }))
216    }
217
218    pub fn register_rendering_context(
219        &mut self,
220        rendering_context: Rc<dyn RenderingContext>,
221    ) -> PainterId {
222        if let Some(painter_id) = self.painters.iter().find_map(|painter| {
223            let painter = painter.borrow();
224            if Rc::ptr_eq(&painter.rendering_context, &rendering_context) {
225                Some(painter.painter_id)
226            } else {
227                None
228            }
229        }) {
230            return painter_id;
231        }
232
233        let painter = Painter::new(rendering_context.clone(), self);
234        let connection = rendering_context
235            .connection()
236            .expect("Failed to get connection");
237        let adapter = connection
238            .create_adapter()
239            .expect("Failed to create adapter");
240
241        let painter_surfman_details = PainterSurfmanDetails {
242            connection,
243            adapter,
244        };
245        self.painter_surfman_details_map
246            .insert(painter.painter_id, painter_surfman_details);
247
248        let painter_id = painter.painter_id;
249        self.painters.push(Rc::new(RefCell::new(painter)));
250        painter_id
251    }
252
253    fn remove_painter(&mut self, painter_id: PainterId) {
254        self.painters
255            .retain(|painter| painter.borrow().painter_id != painter_id);
256        self.painter_surfman_details_map.remove(painter_id);
257    }
258
259    pub(crate) fn maybe_painter<'a>(&'a self, painter_id: PainterId) -> Option<Ref<'a, Painter>> {
260        self.painters
261            .iter()
262            .map(|painter| painter.borrow())
263            .find(|painter| painter.painter_id == painter_id)
264    }
265
266    pub(crate) fn painter<'a>(&'a self, painter_id: PainterId) -> Ref<'a, Painter> {
267        self.maybe_painter(painter_id)
268            .expect("painter_id not found")
269    }
270
271    pub(crate) fn maybe_painter_mut<'a>(
272        &'a self,
273        painter_id: PainterId,
274    ) -> Option<RefMut<'a, Painter>> {
275        self.painters
276            .iter()
277            .map(|painter| painter.borrow_mut())
278            .find(|painter| painter.painter_id == painter_id)
279    }
280
281    pub(crate) fn painter_mut<'a>(&'a self, painter_id: PainterId) -> RefMut<'a, Painter> {
282        self.maybe_painter_mut(painter_id)
283            .expect("painter_id not found")
284    }
285
286    pub fn painter_id(&self) -> PainterId {
287        self.painters[0].borrow().painter_id
288    }
289
290    pub fn rendering_context_size(&self, painter_id: PainterId) -> Size2D<u32, DevicePixel> {
291        self.painter(painter_id).rendering_context.size2d()
292    }
293
294    pub fn webgl_threads(&self) -> WebGLThreads {
295        self.webgl_threads.clone()
296    }
297
298    pub fn webrender_external_image_id_manager(&self) -> WebRenderExternalImageIdManager {
299        self.webrender_external_image_id_manager.clone()
300    }
301
302    pub fn webxr_running(&self) -> bool {
303        #[cfg(feature = "webxr")]
304        {
305            self.webxr_main_thread.borrow().running()
306        }
307        #[cfg(not(feature = "webxr"))]
308        {
309            false
310        }
311    }
312
313    #[cfg(feature = "webxr")]
314    pub fn webxr_main_thread_registry(&self) -> webxr_api::Registry {
315        self.webxr_main_thread.borrow().registry()
316    }
317
318    #[cfg(feature = "webgpu")]
319    pub fn webgpu_image_map(&self) -> WebGpuExternalImageMap {
320        self.webgpu_image_map.get_or_init(Default::default).clone()
321    }
322
323    pub fn webviews_needing_repaint(&self) -> Vec<WebViewId> {
324        self.painters
325            .iter()
326            .flat_map(|painter| painter.borrow().webviews_needing_repaint())
327            .collect()
328    }
329
330    pub fn finish_shutting_down(&self) {
331        // Drain paint port, sometimes messages contain channels that are blocking
332        // another thread from finishing (i.e. SetFrameTree).
333        while self.paint_receiver.try_recv().is_ok() {}
334
335        let (webgl_exit_sender, webgl_exit_receiver) =
336            generic_channel::channel().expect("Failed to create IPC channel!");
337        if !self
338            .webgl_threads
339            .exit(webgl_exit_sender)
340            .is_ok_and(|_| webgl_exit_receiver.recv().is_ok())
341        {
342            warn!("Could not exit WebGLThread.");
343        }
344
345        // Tell the profiler, memory profiler, and scrolling timer to shut down.
346        if let Ok((sender, receiver)) = ipc::channel() {
347            self.time_profiler_chan
348                .send(profile_time::ProfilerMsg::Exit(sender));
349            let _ = receiver.recv();
350        }
351    }
352
353    fn handle_browser_message(&self, msg: PaintMessage) {
354        trace_msg_from_constellation!(msg, "{msg:?}");
355
356        match self.shutdown_state() {
357            ShutdownState::NotShuttingDown => {},
358            ShutdownState::ShuttingDown => {
359                self.handle_browser_message_while_shutting_down(msg);
360                return;
361            },
362            ShutdownState::FinishedShuttingDown => {
363                // Messages to Paint are ignored after shutdown is complete.
364                return;
365            },
366        }
367
368        match msg {
369            PaintMessage::CollectMemoryReport(sender) => {
370                self.collect_memory_report(sender);
371            },
372            PaintMessage::ChangeRunningAnimationsState(
373                webview_id,
374                pipeline_id,
375                animation_state,
376            ) => {
377                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
378                    painter.change_running_animations_state(
379                        webview_id,
380                        pipeline_id,
381                        animation_state,
382                    );
383                }
384            },
385            PaintMessage::SetFrameTreeForWebView(webview_id, frame_tree) => {
386                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
387                    painter.set_frame_tree_for_webview(&frame_tree);
388                }
389            },
390            PaintMessage::SetThrottled(webview_id, pipeline_id, throttled) => {
391                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
392                    painter.set_throttled(webview_id, pipeline_id, throttled);
393                }
394            },
395            PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
396                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
397                    painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
398                }
399            },
400            PaintMessage::NewWebRenderFrameReady(..) => {
401                unreachable!("New WebRender frames should be handled in the caller.");
402            },
403            PaintMessage::SendInitialTransaction(webview_id, pipeline_id) => {
404                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
405                    painter.send_initial_pipeline_transaction(webview_id, pipeline_id);
406                }
407            },
408            PaintMessage::ScrollNodeByDelta(
409                webview_id,
410                pipeline_id,
411                offset,
412                external_scroll_id,
413            ) => {
414                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
415                    painter.scroll_node_by_delta(
416                        webview_id,
417                        pipeline_id,
418                        offset,
419                        external_scroll_id,
420                    );
421                }
422            },
423            PaintMessage::ScrollViewportByDelta(webview_id, delta) => {
424                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
425                    painter.scroll_viewport_by_delta(webview_id, delta);
426                }
427            },
428            PaintMessage::UpdateEpoch {
429                webview_id,
430                pipeline_id,
431                epoch,
432            } => {
433                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
434                    painter.update_epoch(webview_id, pipeline_id, epoch);
435                }
436            },
437            PaintMessage::SendDisplayList {
438                webview_id,
439                display_list_descriptor,
440                display_list_info_receiver,
441                display_list_data_receiver,
442            } => {
443                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
444                    painter.handle_new_display_list(
445                        webview_id,
446                        display_list_descriptor,
447                        display_list_info_receiver,
448                        display_list_data_receiver,
449                    );
450                }
451            },
452            PaintMessage::GenerateFrame(painter_ids) => {
453                for painter_id in painter_ids {
454                    if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
455                        painter.generate_frame_for_script();
456                    }
457                }
458            },
459            PaintMessage::GenerateImageKey(webview_id, result_sender) => {
460                self.handle_generate_image_key(webview_id, result_sender);
461            },
462            PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
463                self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
464            },
465            PaintMessage::UpdateImages(painter_id, updates) => {
466                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
467                    painter.update_images(updates);
468                }
469            },
470            PaintMessage::DelayNewFrameForCanvas(
471                webview_id,
472                pipeline_id,
473                canvas_epoch,
474                image_keys,
475            ) => {
476                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
477                    painter.delay_new_frames_for_canvas(pipeline_id, canvas_epoch, image_keys);
478                }
479            },
480            PaintMessage::AddFont(painter_id, font_key, data, index) => {
481                debug_assert!(painter_id == font_key.into());
482
483                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
484                    painter.add_font(font_key, data, index);
485                }
486            },
487            PaintMessage::AddSystemFont(painter_id, font_key, native_handle) => {
488                debug_assert!(painter_id == font_key.into());
489
490                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
491                    painter.add_system_font(font_key, native_handle);
492                }
493            },
494            PaintMessage::AddFontInstance(
495                painter_id,
496                font_instance_key,
497                font_key,
498                size,
499                flags,
500                variations,
501            ) => {
502                debug_assert!(painter_id == font_key.into());
503                debug_assert!(painter_id == font_instance_key.into());
504
505                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
506                    painter.add_font_instance(font_instance_key, font_key, size, flags, variations);
507                }
508            },
509            PaintMessage::RemoveFonts(painter_id, keys, instance_keys) => {
510                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
511                    painter.remove_fonts(keys, instance_keys);
512                }
513            },
514            PaintMessage::GenerateFontKeys(
515                number_of_font_keys,
516                number_of_font_instance_keys,
517                result_sender,
518                painter_id,
519            ) => {
520                self.handle_generate_font_keys(
521                    number_of_font_keys,
522                    number_of_font_instance_keys,
523                    result_sender,
524                    painter_id,
525                );
526            },
527            PaintMessage::Viewport(webview_id, viewport_description) => {
528                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
529                    painter.set_viewport_description(webview_id, viewport_description);
530                }
531            },
532            PaintMessage::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
533                if let Some(painter) = self.maybe_painter(webview_id.into()) {
534                    painter.handle_screenshot_readiness_reply(webview_id, pipelines_and_epochs);
535                }
536            },
537            PaintMessage::SendLCPCandidate(lcp_candidate, webview_id, pipeline_id, epoch) => {
538                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
539                    painter.append_lcp_candidate(lcp_candidate, webview_id, pipeline_id, epoch);
540                }
541            },
542            PaintMessage::EnableLCPCalculation(webview_id) => {
543                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
544                    painter.enable_lcp_calculation(&webview_id);
545                }
546            },
547        }
548    }
549
550    pub fn remove_webview(&mut self, webview_id: WebViewId) {
551        let painter_id = webview_id.into();
552
553        {
554            let mut painter = self.painter_mut(painter_id);
555            painter.remove_webview(webview_id);
556            if !painter.is_empty() {
557                return;
558            }
559        }
560
561        self.remove_painter(painter_id);
562    }
563
564    fn collect_memory_report(&self, sender: profile_traits::mem::ReportsChan) {
565        let mut memory_report = MemoryReport::default();
566        for painter in &self.painters {
567            memory_report += painter.borrow().report_memory();
568        }
569
570        let mut reports = vec![
571            Report {
572                path: path!["webrender", "fonts"],
573                kind: ReportKind::ExplicitJemallocHeapSize,
574                size: memory_report.fonts,
575            },
576            Report {
577                path: path!["webrender", "images"],
578                kind: ReportKind::ExplicitJemallocHeapSize,
579                size: memory_report.images,
580            },
581            Report {
582                path: path!["webrender", "display-list"],
583                kind: ReportKind::ExplicitJemallocHeapSize,
584                size: memory_report.display_list,
585            },
586        ];
587
588        perform_memory_report(|ops| {
589            let scroll_trees_memory_usage = self
590                .painters
591                .iter()
592                .map(|painter| painter.borrow().scroll_trees_memory_usage(ops))
593                .sum();
594            reports.push(Report {
595                path: path!["paint", "scroll-tree"],
596                kind: ReportKind::ExplicitJemallocHeapSize,
597                size: scroll_trees_memory_usage,
598            });
599        });
600
601        sender.send(ProcessReports::new(reports));
602    }
603
604    /// Handle messages sent to `Paint` during the shutdown process. In general,
605    /// the things `Paint` can do in this state are limited. It's very important to
606    /// answer any synchronous messages though as other threads might be waiting on the
607    /// results to finish their own shut down process. We try to do as little as possible
608    /// during this time.
609    ///
610    /// When that involves generating WebRender ids, our approach here is to simply
611    /// generate them, but assume they will never be used, since once shutting down
612    /// `Paint` no longer does any WebRender frame generation.
613    fn handle_browser_message_while_shutting_down(&self, msg: PaintMessage) {
614        match msg {
615            PaintMessage::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
616                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
617                    painter.notify_pipeline_exited(webview_id, pipeline_id, pipeline_exit_source);
618                }
619            },
620            PaintMessage::GenerateImageKey(webview_id, result_sender) => {
621                self.handle_generate_image_key(webview_id, result_sender);
622            },
623            PaintMessage::GenerateImageKeysForPipeline(webview_id, pipeline_id) => {
624                self.handle_generate_image_keys_for_pipeline(webview_id, pipeline_id);
625            },
626            PaintMessage::GenerateFontKeys(
627                number_of_font_keys,
628                number_of_font_instance_keys,
629                result_sender,
630                painter_id,
631            ) => {
632                self.handle_generate_font_keys(
633                    number_of_font_keys,
634                    number_of_font_instance_keys,
635                    result_sender,
636                    painter_id,
637                );
638            },
639            _ => {
640                debug!("Ignoring message ({:?} while shutting down", msg);
641            },
642        }
643    }
644
645    pub fn add_webview(&self, webview: Box<dyn WebViewTrait>, viewport_details: ViewportDetails) {
646        self.painter_mut(webview.id().into())
647            .add_webview(webview, viewport_details);
648    }
649
650    pub fn show_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
651        self.painter_mut(webview_id.into())
652            .set_webview_hidden(webview_id, false)
653    }
654
655    pub fn hide_webview(&self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
656        self.painter_mut(webview_id.into())
657            .set_webview_hidden(webview_id, true)
658    }
659
660    pub fn set_hidpi_scale_factor(
661        &self,
662        webview_id: WebViewId,
663        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
664    ) {
665        if self.shutdown_state() != ShutdownState::NotShuttingDown {
666            return;
667        }
668        self.painter_mut(webview_id.into())
669            .set_hidpi_scale_factor(webview_id, new_scale_factor);
670    }
671
672    pub fn resize_rendering_context(&self, webview_id: WebViewId, new_size: PhysicalSize<u32>) {
673        if self.shutdown_state() != ShutdownState::NotShuttingDown {
674            return;
675        }
676        self.painter_mut(webview_id.into())
677            .resize_rendering_context(new_size);
678    }
679
680    pub fn set_page_zoom(&self, webview_id: WebViewId, new_zoom: f32) {
681        if self.shutdown_state() != ShutdownState::NotShuttingDown {
682            return;
683        }
684        self.painter_mut(webview_id.into())
685            .set_page_zoom(webview_id, new_zoom);
686    }
687
688    pub fn page_zoom(&self, webview_id: WebViewId) -> f32 {
689        self.painter(webview_id.into()).page_zoom(webview_id)
690    }
691
692    /// Render the WebRender scene to the active `RenderingContext`.
693    pub fn render(&self, webview_id: WebViewId) {
694        self.painter_mut(webview_id.into())
695            .render(&self.time_profiler_chan);
696    }
697
698    /// Get the message receiver for this [`Paint`].
699    pub fn receiver(&self) -> &RoutedReceiver<PaintMessage> {
700        &self.paint_receiver
701    }
702
703    #[servo_tracing::instrument(skip_all)]
704    pub fn handle_messages(&self, mut messages: Vec<PaintMessage>) {
705        // Pull out the `NewWebRenderFrameReady` messages from the list of messages and handle them
706        // at the end of this function. This prevents overdraw when more than a single message of
707        // this type of received. In addition, if any of these frames need a repaint, that reflected
708        // when calling `handle_new_webrender_frame_ready`.
709        let mut saw_webrender_frame_ready_for_painter = HashMap::new();
710        messages.retain(|message| match message {
711            PaintMessage::NewWebRenderFrameReady(painter_id, _document_id, need_repaint) => {
712                if let Some(painter) = self.maybe_painter(*painter_id) {
713                    painter.decrement_pending_frames();
714                    *saw_webrender_frame_ready_for_painter
715                        .entry(*painter_id)
716                        .or_insert(*need_repaint) |= *need_repaint;
717                }
718
719                false
720            },
721            _ => true,
722        });
723
724        for message in messages {
725            self.handle_browser_message(message);
726            if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
727                return;
728            }
729        }
730
731        for (painter_id, repaint_needed) in saw_webrender_frame_ready_for_painter.iter() {
732            if let Some(painter) = self.maybe_painter(*painter_id) {
733                painter.handle_new_webrender_frame_ready(*repaint_needed);
734            }
735        }
736    }
737
738    #[servo_tracing::instrument(skip_all)]
739    pub fn perform_updates(&self) -> bool {
740        if self.shutdown_state() == ShutdownState::FinishedShuttingDown {
741            return false;
742        }
743
744        // Run the WebXR main thread
745        #[cfg(feature = "webxr")]
746        self.webxr_main_thread.borrow_mut().run_one_frame();
747
748        for painter in &self.painters {
749            painter.borrow_mut().perform_updates();
750        }
751
752        self.shutdown_state() != ShutdownState::FinishedShuttingDown
753    }
754
755    pub fn toggle_webrender_debug(&self, option: WebRenderDebugOption) {
756        for painter in &self.painters {
757            painter.borrow_mut().toggle_webrender_debug(option);
758        }
759    }
760
761    pub fn capture_webrender(&self, webview_id: WebViewId) {
762        let capture_id = SystemTime::now()
763            .duration_since(UNIX_EPOCH)
764            .unwrap_or_default()
765            .as_secs()
766            .to_string();
767        let available_path = [env::current_dir(), Ok(env::temp_dir())]
768            .iter()
769            .filter_map(|val| {
770                val.as_ref()
771                    .map(|dir| dir.join("webrender-captures").join(&capture_id))
772                    .ok()
773            })
774            .find(|val| create_dir_all(val).is_ok());
775
776        let Some(capture_path) = available_path else {
777            log::error!("Couldn't create a path for WebRender captures.");
778            return;
779        };
780
781        log::info!("Saving WebRender capture to {capture_path:?}");
782        self.painter(webview_id.into())
783            .webrender_api
784            .save_capture(capture_path.clone(), CaptureBits::all());
785    }
786
787    pub fn notify_input_event(&self, webview_id: WebViewId, event: InputEventAndId) {
788        if self.shutdown_state() != ShutdownState::NotShuttingDown {
789            return;
790        }
791        self.painter_mut(webview_id.into())
792            .notify_input_event(webview_id, event);
793    }
794
795    pub fn notify_scroll_event(&self, webview_id: WebViewId, scroll: Scroll, point: WebViewPoint) {
796        if self.shutdown_state() != ShutdownState::NotShuttingDown {
797            return;
798        }
799        self.painter_mut(webview_id.into())
800            .notify_scroll_event(webview_id, scroll, point);
801    }
802
803    pub fn pinch_zoom(&self, webview_id: WebViewId, pinch_zoom_delta: f32, center: DevicePoint) {
804        if self.shutdown_state() != ShutdownState::NotShuttingDown {
805            return;
806        }
807        self.painter_mut(webview_id.into())
808            .pinch_zoom(webview_id, pinch_zoom_delta, center);
809    }
810
811    pub fn device_pixels_per_page_pixel(
812        &self,
813        webview_id: WebViewId,
814    ) -> Scale<f32, CSSPixel, DevicePixel> {
815        self.painter_mut(webview_id.into())
816            .device_pixels_per_page_pixel(webview_id)
817    }
818
819    pub(crate) fn shutdown_state(&self) -> ShutdownState {
820        self.shutdown_state.get()
821    }
822
823    pub fn request_screenshot(
824        &self,
825        webview_id: WebViewId,
826        rect: Option<WebViewRect>,
827        callback: Box<dyn FnOnce(Result<RgbaImage, ScreenshotCaptureError>) + 'static>,
828    ) {
829        self.painter(webview_id.into())
830            .request_screenshot(webview_id, rect, callback);
831    }
832
833    pub fn notify_input_event_handled(
834        &self,
835        webview_id: WebViewId,
836        input_event_id: InputEventId,
837        result: InputEventResult,
838    ) {
839        if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
840            painter.notify_input_event_handled(webview_id, input_event_id, result);
841        }
842    }
843
844    /// Generate an image key from the appropriate [`Painter`] or, if it is unknown, generate
845    /// a dummy image key. The unknown case needs to be handled because requests for keys
846    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
847    /// in this case because we will never render again in that case.
848    fn handle_generate_image_key(
849        &self,
850        webview_id: WebViewId,
851        result_sender: GenericSender<ImageKey>,
852    ) {
853        let painter_id = webview_id.into();
854        let image_key = self.maybe_painter(painter_id).map_or_else(
855            || ImageKey::new(painter_id.into(), 0),
856            |painter| painter.webrender_api.generate_image_key(),
857        );
858        let _ = result_sender.send(image_key);
859    }
860
861    /// Generate image keys from the appropriate [`Painter`] or, if it is unknown, generate
862    /// dummy image keys. The unknown case needs to be handled because requests for keys
863    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
864    /// in this case because we will never render again in that case.
865    fn handle_generate_image_keys_for_pipeline(
866        &self,
867        webview_id: WebViewId,
868        pipeline_id: PipelineId,
869    ) {
870        let painter_id = webview_id.into();
871        let painter = self.maybe_painter(painter_id);
872        let image_keys = (0..pref!(image_key_batch_size))
873            .map(|_| {
874                painter.as_ref().map_or_else(
875                    || ImageKey::new(painter_id.into(), 0),
876                    |painter| painter.webrender_api.generate_image_key(),
877                )
878            })
879            .collect();
880
881        let _ = self.embedder_to_constellation_sender.send(
882            EmbedderToConstellationMessage::SendImageKeysForPipeline(pipeline_id, image_keys),
883        );
884    }
885
886    /// Generate font keys from the appropriate [`Painter`] or, if it is unknown, generate
887    /// dummy font keys. The unknown case needs to be handled because requests for keys
888    /// could theoretically come after a [`Painter`] has been released. A dummy key is okay
889    /// in this case because we will never render again in that case.
890    fn handle_generate_font_keys(
891        &self,
892        number_of_font_keys: usize,
893        number_of_font_instance_keys: usize,
894        result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
895        painter_id: PainterId,
896    ) {
897        let painter = self.maybe_painter(painter_id);
898        let font_keys = (0..number_of_font_keys)
899            .map(|_| {
900                painter.as_ref().map_or_else(
901                    || FontKey::new(painter_id.into(), 0),
902                    |painter| painter.webrender_api.generate_font_key(),
903                )
904            })
905            .collect();
906        let font_instance_keys = (0..number_of_font_instance_keys)
907            .map(|_| {
908                painter.as_ref().map_or_else(
909                    || FontInstanceKey::new(painter_id.into(), 0),
910                    |painter| painter.webrender_api.generate_font_instance_key(),
911                )
912            })
913            .collect();
914
915        let _ = result_sender.send((font_keys, font_instance_keys));
916    }
917}