Skip to main content

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