compositing/
paint.rs

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