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                variations,
554            ) => {
555                debug_assert!(painter_id == font_key.into());
556                debug_assert!(painter_id == font_instance_key.into());
557
558                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
559                    painter.add_font_instance(font_instance_key, font_key, size, flags, variations);
560                }
561            },
562            PaintMessage::RemoveFonts(painter_id, keys, instance_keys) => {
563                if let Some(mut painter) = self.maybe_painter_mut(painter_id) {
564                    painter.remove_fonts(keys, instance_keys);
565                }
566            },
567            PaintMessage::GenerateFontKeys(
568                number_of_font_keys,
569                number_of_font_instance_keys,
570                result_sender,
571                painter_id,
572            ) => {
573                self.handle_generate_font_keys(
574                    number_of_font_keys,
575                    number_of_font_instance_keys,
576                    result_sender,
577                    painter_id,
578                );
579            },
580            PaintMessage::Viewport(webview_id, viewport_description) => {
581                if let Some(mut painter) = self.maybe_painter_mut(webview_id.into()) {
582                    painter.set_viewport_description(webview_id, viewport_description);
583                }
584            },
585            PaintMessage::ScreenshotReadinessReponse(webview_id, pipelines_and_epochs) => {
586                if let Some(painter) = self.maybe_painter(webview_id.into()) {
587                    painter.handle_screenshot_readiness_reply(webview_id, pipelines_and_epochs);
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}