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