compositing/
compositor.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};
6use std::collections::HashMap;
7use std::collections::hash_map::Entry;
8use std::env;
9use std::fs::create_dir_all;
10use std::iter::once;
11use std::rc::Rc;
12use std::sync::Arc;
13use std::time::{SystemTime, UNIX_EPOCH};
14
15use base::Epoch;
16use base::cross_process_instant::CrossProcessInstant;
17use base::generic_channel::{GenericSender, RoutedReceiver};
18use base::id::{PipelineId, WebViewId};
19use bitflags::bitflags;
20use compositing_traits::display_list::{CompositorDisplayListInfo, ScrollTree, ScrollType};
21use compositing_traits::rendering_context::RenderingContext;
22use compositing_traits::{
23    CompositionPipeline, CompositorMsg, ImageUpdate, PipelineExitSource, SendableFrameTree,
24    WebViewTrait,
25};
26use constellation_traits::{EmbedderToConstellationMessage, PaintMetricEvent};
27use crossbeam_channel::Sender;
28use dpi::PhysicalSize;
29use embedder_traits::{CompositorHitTestResult, InputEvent, ShutdownState, ViewportDetails};
30use euclid::{Point2D, Rect, Scale, Size2D, Transform3D};
31use ipc_channel::ipc::{self, IpcSharedMemory};
32use log::{debug, info, trace, warn};
33use pixels::{CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage};
34use profile_traits::mem::{
35    ProcessReports, ProfilerRegistration, Report, ReportKind, perform_memory_report,
36};
37use profile_traits::time::{self as profile_time, ProfilerCategory};
38use profile_traits::{path, time_profile};
39use rustc_hash::{FxHashMap, FxHashSet};
40use servo_config::{opts, pref};
41use servo_geometry::DeviceIndependentPixel;
42use style_traits::CSSPixel;
43use webrender::{CaptureBits, RenderApi, Transaction};
44use webrender_api::units::{
45    DeviceIntPoint, DeviceIntRect, DevicePixel, DevicePoint, DeviceRect, LayoutPoint, LayoutRect,
46    LayoutSize, WorldPoint,
47};
48use webrender_api::{
49    self, BuiltDisplayList, DirtyRect, DisplayListPayload, DocumentId, Epoch as WebRenderEpoch,
50    ExternalScrollId, FontInstanceFlags, FontInstanceKey, FontInstanceOptions, FontKey,
51    FontVariation, HitTestFlags, ImageKey, PipelineId as WebRenderPipelineId, PropertyBinding,
52    ReferenceFrameKind, RenderReasons, SampledScrollOffset, ScrollLocation, SpaceAndClipInfo,
53    SpatialId, SpatialTreeItemKey, TransformStyle,
54};
55
56use crate::InitialCompositorState;
57use crate::refresh_driver::RefreshDriver;
58use crate::webview_manager::WebViewManager;
59use crate::webview_renderer::{PinchZoomResult, UnknownWebView, WebViewRenderer};
60
61#[derive(Debug, PartialEq)]
62pub enum UnableToComposite {
63    NotReadyToPaintImage(NotReadyToPaint),
64}
65
66#[derive(Debug, PartialEq)]
67pub enum NotReadyToPaint {
68    JustNotifiedConstellation,
69    WaitingOnConstellation,
70}
71
72/// Holds the state when running reftests that determines when it is
73/// safe to save the output image.
74#[derive(Clone, Copy, Debug, PartialEq)]
75enum ReadyState {
76    Unknown,
77    WaitingForConstellationReply,
78    ReadyToSaveImage,
79}
80
81/// An option to control what kind of WebRender debugging is enabled while Servo is running.
82#[derive(Clone)]
83pub enum WebRenderDebugOption {
84    Profiler,
85    TextureCacheDebug,
86    RenderTargetDebug,
87}
88
89/// Data that is shared by all WebView renderers.
90pub struct ServoRenderer {
91    /// The [`RefreshDriver`] which manages the rythym of painting.
92    refresh_driver: RefreshDriver,
93
94    /// Tracks whether we are in the process of shutting down, or have shut down and should close
95    /// the compositor. This is shared with the `Servo` instance.
96    shutdown_state: Rc<Cell<ShutdownState>>,
97
98    /// The port on which we receive messages.
99    compositor_receiver: RoutedReceiver<CompositorMsg>,
100
101    /// The channel on which messages can be sent to the constellation.
102    pub(crate) constellation_sender: Sender<EmbedderToConstellationMessage>,
103
104    /// The channel on which messages can be sent to the time profiler.
105    time_profiler_chan: profile_time::ProfilerChan,
106
107    /// The WebRender [`RenderApi`] interface used to communicate with WebRender.
108    pub(crate) webrender_api: RenderApi,
109
110    /// The active webrender document.
111    pub(crate) webrender_document: DocumentId,
112
113    /// The GL bindings for webrender
114    webrender_gl: Rc<dyn gleam::gl::Gl>,
115
116    #[cfg(feature = "webxr")]
117    /// Some XR devices want to run on the main thread.
118    webxr_main_thread: webxr::MainThreadRegistry,
119
120    /// True to translate mouse input into touch events.
121    pub(crate) convert_mouse_to_touch: bool,
122
123    /// The last position in the rendered view that the mouse moved over. This becomes `None`
124    /// when the mouse leaves the rendered view.
125    pub(crate) last_mouse_move_position: Option<DevicePoint>,
126
127    /// A [`FrameRequestDelayer`] which is used to wait for canvas image updates to
128    /// arrive before requesting a new frame, as these happen asynchronously with
129    /// `ScriptThread` display list construction.
130    frame_delayer: FrameDelayer,
131}
132
133/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
134pub struct IOCompositor {
135    /// Data that is shared by all WebView renderers.
136    global: Rc<RefCell<ServoRenderer>>,
137
138    /// Our [`WebViewRenderer`]s, one for every `WebView`.
139    webview_renderers: WebViewManager<WebViewRenderer>,
140
141    /// Tracks whether or not the view needs to be repainted.
142    needs_repaint: Cell<RepaintReason>,
143
144    /// Used by the logic that determines when it is safe to output an
145    /// image for the reftest framework.
146    ready_to_save_state: ReadyState,
147
148    /// The webrender renderer.
149    webrender: Option<webrender::Renderer>,
150
151    /// The [`RenderingContext`] instance that webrender targets, which is the viewport.
152    rendering_context: Rc<dyn RenderingContext>,
153
154    /// The number of frames pending to receive from WebRender.
155    pending_frames: Cell<usize>,
156
157    /// A handle to the memory profiler which will automatically unregister
158    /// when it's dropped.
159    _mem_profiler_registration: ProfilerRegistration,
160}
161
162/// Why we need to be repainted. This is used for debugging.
163#[derive(Clone, Copy, Default, PartialEq)]
164pub(crate) struct RepaintReason(u8);
165
166bitflags! {
167    impl RepaintReason: u8 {
168        /// We're performing the single repaint in headless mode.
169        const ReadyForScreenshot = 1 << 0;
170        /// We're performing a repaint to run an animation.
171        const ChangedAnimationState = 1 << 1;
172        /// A new WebRender frame has arrived.
173        const NewWebRenderFrame = 1 << 2;
174        /// The window has been resized and will need to be synchronously repainted.
175        const Resize = 1 << 3;
176    }
177}
178
179/// The paint status of a particular pipeline in the Servo renderer. This is used to trigger metrics
180/// in script (via the constellation) when display lists are received.
181///
182/// See <https://w3c.github.io/paint-timing/#first-contentful-paint>.
183#[derive(PartialEq)]
184pub(crate) enum PaintMetricState {
185    /// The renderer is still waiting to process a display list which triggers this metric.
186    Waiting,
187    /// The renderer has processed the display list which will trigger this event, marked the Servo
188    /// instance ready to paint, and is waiting for the given epoch to actually be rendered.
189    Seen(WebRenderEpoch, bool /* first_reflow */),
190    /// The metric has been sent to the constellation and no more work needs to be done.
191    Sent,
192}
193
194pub(crate) struct PipelineDetails {
195    /// The pipeline associated with this PipelineDetails object.
196    pub pipeline: Option<CompositionPipeline>,
197
198    /// The id of the parent pipeline, if any.
199    pub parent_pipeline_id: Option<PipelineId>,
200
201    /// Whether animations are running
202    pub animations_running: bool,
203
204    /// Whether there are animation callbacks
205    pub animation_callbacks_running: bool,
206
207    /// Whether to use less resources by stopping animations.
208    pub throttled: bool,
209
210    /// The compositor-side [ScrollTree]. This is used to allow finding and scrolling
211    /// nodes in the compositor before forwarding new offsets to WebRender.
212    pub scroll_tree: ScrollTree,
213
214    /// The paint metric status of the first paint.
215    pub first_paint_metric: PaintMetricState,
216
217    /// The paint metric status of the first contentful paint.
218    pub first_contentful_paint_metric: PaintMetricState,
219
220    /// The CSS pixel to device pixel scale of the viewport of this pipeline, including
221    /// page zoom, but not including any pinch zoom amount. This is used to detect
222    /// situations where the current display list is for an old scale.
223    pub viewport_scale: Option<Scale<f32, CSSPixel, DevicePixel>>,
224
225    /// Which parts of Servo have reported that this `Pipeline` has exited. Only when all
226    /// have done so will it be discarded.
227    pub exited: PipelineExitSource,
228}
229
230impl PipelineDetails {
231    pub(crate) fn animation_callbacks_running(&self) -> bool {
232        self.animation_callbacks_running
233    }
234
235    pub(crate) fn animating(&self) -> bool {
236        !self.throttled && (self.animation_callbacks_running || self.animations_running)
237    }
238}
239
240impl PipelineDetails {
241    pub(crate) fn new() -> PipelineDetails {
242        PipelineDetails {
243            pipeline: None,
244            parent_pipeline_id: None,
245            viewport_scale: None,
246            animations_running: false,
247            animation_callbacks_running: false,
248            throttled: false,
249            scroll_tree: ScrollTree::default(),
250            first_paint_metric: PaintMetricState::Waiting,
251            first_contentful_paint_metric: PaintMetricState::Waiting,
252            exited: PipelineExitSource::empty(),
253        }
254    }
255
256    fn install_new_scroll_tree(&mut self, new_scroll_tree: ScrollTree) {
257        let old_scroll_offsets = self.scroll_tree.scroll_offsets();
258        self.scroll_tree = new_scroll_tree;
259        self.scroll_tree.set_all_scroll_offsets(&old_scroll_offsets);
260    }
261}
262
263impl ServoRenderer {
264    pub fn shutdown_state(&self) -> ShutdownState {
265        self.shutdown_state.get()
266    }
267
268    pub(crate) fn hit_test_at_point(&self, point: DevicePoint) -> Vec<CompositorHitTestResult> {
269        self.hit_test_at_point_with_flags(point, HitTestFlags::empty())
270    }
271
272    // TODO: split this into first half (global) and second half (one for whole compositor, one for webview)
273    pub(crate) fn hit_test_at_point_with_flags(
274        &self,
275        point: DevicePoint,
276        flags: HitTestFlags,
277    ) -> Vec<CompositorHitTestResult> {
278        // DevicePoint and WorldPoint are the same for us.
279        let world_point = WorldPoint::from_untyped(point.to_untyped());
280        let results = self.webrender_api.hit_test(
281            self.webrender_document,
282            None, /* pipeline_id */
283            world_point,
284            flags,
285        );
286
287        results
288            .items
289            .iter()
290            .map(|item| {
291                let pipeline_id = item.pipeline.into();
292                let external_scroll_id = ExternalScrollId(item.tag.0, item.pipeline);
293                CompositorHitTestResult {
294                    pipeline_id,
295                    point_in_viewport: Point2D::from_untyped(item.point_in_viewport.to_untyped()),
296                    external_scroll_id,
297                }
298            })
299            .collect()
300    }
301
302    pub(crate) fn send_transaction(&mut self, transaction: Transaction) {
303        self.webrender_api
304            .send_transaction(self.webrender_document, transaction);
305    }
306}
307
308impl IOCompositor {
309    pub fn new(state: InitialCompositorState, convert_mouse_to_touch: bool) -> Self {
310        let registration = state.mem_profiler_chan.prepare_memory_reporting(
311            "compositor".into(),
312            state.sender.clone(),
313            CompositorMsg::CollectMemoryReport,
314        );
315        let compositor = IOCompositor {
316            global: Rc::new(RefCell::new(ServoRenderer {
317                refresh_driver: RefreshDriver::new(
318                    state.constellation_chan.clone(),
319                    state.event_loop_waker,
320                ),
321                shutdown_state: state.shutdown_state,
322                compositor_receiver: state.receiver,
323                constellation_sender: state.constellation_chan,
324                time_profiler_chan: state.time_profiler_chan,
325                webrender_api: state.webrender_api,
326                webrender_document: state.webrender_document,
327                webrender_gl: state.webrender_gl,
328                #[cfg(feature = "webxr")]
329                webxr_main_thread: state.webxr_main_thread,
330                convert_mouse_to_touch,
331                last_mouse_move_position: None,
332                frame_delayer: Default::default(),
333            })),
334            webview_renderers: WebViewManager::default(),
335            needs_repaint: Cell::default(),
336            ready_to_save_state: ReadyState::Unknown,
337            webrender: Some(state.webrender),
338            rendering_context: state.rendering_context,
339            pending_frames: Cell::new(0),
340            _mem_profiler_registration: registration,
341        };
342
343        {
344            let gl = &compositor.global.borrow().webrender_gl;
345            info!("Running on {}", gl.get_string(gleam::gl::RENDERER));
346            info!("OpenGL Version {}", gl.get_string(gleam::gl::VERSION));
347        }
348        compositor.assert_gl_framebuffer_complete();
349        compositor
350    }
351
352    pub fn deinit(&mut self) {
353        if let Err(err) = self.rendering_context.make_current() {
354            warn!("Failed to make the rendering context current: {:?}", err);
355        }
356        if let Some(webrender) = self.webrender.take() {
357            webrender.deinit();
358        }
359    }
360
361    pub fn rendering_context_size(&self) -> Size2D<u32, DevicePixel> {
362        self.rendering_context.size2d()
363    }
364
365    pub fn webxr_running(&self) -> bool {
366        #[cfg(feature = "webxr")]
367        {
368            self.global.borrow().webxr_main_thread.running()
369        }
370        #[cfg(not(feature = "webxr"))]
371        {
372            false
373        }
374    }
375
376    fn set_needs_repaint(&self, reason: RepaintReason) {
377        let mut needs_repaint = self.needs_repaint.get();
378        needs_repaint.insert(reason);
379        self.needs_repaint.set(needs_repaint);
380    }
381
382    pub fn needs_repaint(&self) -> bool {
383        let repaint_reason = self.needs_repaint.get();
384        if repaint_reason.is_empty() {
385            return false;
386        }
387
388        !self
389            .global
390            .borrow()
391            .refresh_driver
392            .wait_to_paint(repaint_reason)
393    }
394
395    pub fn finish_shutting_down(&mut self) {
396        // Drain compositor port, sometimes messages contain channels that are blocking
397        // another thread from finishing (i.e. SetFrameTree).
398        while self
399            .global
400            .borrow_mut()
401            .compositor_receiver
402            .try_recv()
403            .is_ok()
404        {}
405
406        // Tell the profiler, memory profiler, and scrolling timer to shut down.
407        if let Ok((sender, receiver)) = ipc::channel() {
408            self.global
409                .borrow()
410                .time_profiler_chan
411                .send(profile_time::ProfilerMsg::Exit(sender));
412            let _ = receiver.recv();
413        }
414    }
415
416    fn handle_browser_message(&mut self, msg: CompositorMsg) {
417        trace_msg_from_constellation!(msg, "{msg:?}");
418
419        match self.shutdown_state() {
420            ShutdownState::NotShuttingDown => {},
421            ShutdownState::ShuttingDown => {
422                self.handle_browser_message_while_shutting_down(msg);
423                return;
424            },
425            ShutdownState::FinishedShuttingDown => {
426                // Messages to the compositor are ignored after shutdown is complete.
427                return;
428            },
429        }
430
431        match msg {
432            CompositorMsg::CollectMemoryReport(sender) => {
433                let ops =
434                    wr_malloc_size_of::MallocSizeOfOps::new(servo_allocator::usable_size, None);
435                let report = self.global.borrow().webrender_api.report_memory(ops);
436                let mut reports = vec![
437                    Report {
438                        path: path!["webrender", "fonts"],
439                        kind: ReportKind::ExplicitJemallocHeapSize,
440                        size: report.fonts,
441                    },
442                    Report {
443                        path: path!["webrender", "images"],
444                        kind: ReportKind::ExplicitJemallocHeapSize,
445                        size: report.images,
446                    },
447                    Report {
448                        path: path!["webrender", "display-list"],
449                        kind: ReportKind::ExplicitJemallocHeapSize,
450                        size: report.display_list,
451                    },
452                ];
453
454                perform_memory_report(|ops| {
455                    reports.push(Report {
456                        path: path!["compositor", "scroll-tree"],
457                        kind: ReportKind::ExplicitJemallocHeapSize,
458                        size: self.webview_renderers.scroll_trees_memory_usage(ops),
459                    });
460                });
461
462                sender.send(ProcessReports::new(reports));
463            },
464
465            CompositorMsg::ChangeRunningAnimationsState(
466                webview_id,
467                pipeline_id,
468                animation_state,
469            ) => {
470                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
471                    return;
472                };
473
474                if webview_renderer
475                    .change_pipeline_running_animations_state(pipeline_id, animation_state)
476                {
477                    self.global
478                        .borrow()
479                        .refresh_driver
480                        .notify_animation_state_changed(webview_renderer);
481                }
482            },
483
484            CompositorMsg::CreateOrUpdateWebView(frame_tree) => {
485                self.set_frame_tree_for_webview(&frame_tree);
486            },
487
488            CompositorMsg::RemoveWebView(webview_id) => {
489                self.remove_webview(webview_id);
490            },
491
492            CompositorMsg::TouchEventProcessed(webview_id, result) => {
493                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
494                    warn!("Handling input event for unknown webview: {webview_id}");
495                    return;
496                };
497                webview_renderer.on_touch_event_processed(result);
498            },
499            CompositorMsg::IsReadyToSaveImageReply(is_ready) => {
500                assert_eq!(
501                    self.ready_to_save_state,
502                    ReadyState::WaitingForConstellationReply
503                );
504                if is_ready && self.pending_frames.get() == 0 {
505                    self.ready_to_save_state = ReadyState::ReadyToSaveImage;
506                } else {
507                    self.ready_to_save_state = ReadyState::Unknown;
508                }
509                self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
510            },
511
512            CompositorMsg::SetThrottled(webview_id, pipeline_id, throttled) => {
513                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
514                    return;
515                };
516
517                if webview_renderer.set_throttled(pipeline_id, throttled) {
518                    self.global
519                        .borrow()
520                        .refresh_driver
521                        .notify_animation_state_changed(webview_renderer);
522                }
523            },
524
525            CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
526                debug!(
527                    "Compositor got pipeline exited: {:?} {:?}",
528                    webview_id, pipeline_id
529                );
530                if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
531                    webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
532                }
533            },
534
535            CompositorMsg::NewWebRenderFrameReady(_document_id, recomposite_needed) => {
536                self.handle_new_webrender_frame_ready(recomposite_needed);
537            },
538
539            CompositorMsg::LoadComplete(_) => {
540                if opts::get().wait_for_stable_image {
541                    self.set_needs_repaint(RepaintReason::ReadyForScreenshot);
542                }
543            },
544
545            CompositorMsg::SendInitialTransaction(pipeline) => {
546                let mut txn = Transaction::new();
547                txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default()));
548                self.generate_frame(&mut txn, RenderReasons::SCENE);
549                self.global.borrow_mut().send_transaction(txn);
550            },
551
552            CompositorMsg::SendScrollNode(webview_id, pipeline_id, offset, external_scroll_id) => {
553                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
554                    return;
555                };
556
557                let pipeline_id = pipeline_id.into();
558                let Some(pipeline_details) = webview_renderer.pipelines.get_mut(&pipeline_id)
559                else {
560                    return;
561                };
562
563                let Some(offset) = pipeline_details
564                    .scroll_tree
565                    .set_scroll_offset_for_node_with_external_scroll_id(
566                        external_scroll_id,
567                        offset,
568                        ScrollType::Script,
569                    )
570                else {
571                    // The renderer should be fully up-to-date with script at this point and script
572                    // should never try to scroll to an invalid location.
573                    warn!("Could not scroll node with id: {external_scroll_id:?}");
574                    return;
575                };
576
577                let mut txn = Transaction::new();
578                txn.set_scroll_offsets(
579                    external_scroll_id,
580                    vec![SampledScrollOffset {
581                        offset,
582                        generation: 0,
583                    }],
584                );
585                self.generate_frame(&mut txn, RenderReasons::APZ);
586                self.global.borrow_mut().send_transaction(txn);
587            },
588
589            CompositorMsg::SendDisplayList {
590                webview_id,
591                display_list_descriptor,
592                display_list_receiver,
593            } => {
594                // This must match the order from the sender, currently in `shared/script/lib.rs`.
595                let display_list_info = match display_list_receiver.recv() {
596                    Ok(display_list_info) => display_list_info,
597                    Err(error) => {
598                        return warn!("Could not receive display list info: {error}");
599                    },
600                };
601                let display_list_info: CompositorDisplayListInfo =
602                    match bincode::deserialize(&display_list_info) {
603                        Ok(display_list_info) => display_list_info,
604                        Err(error) => {
605                            return warn!("Could not deserialize display list info: {error}");
606                        },
607                    };
608                let items_data = match display_list_receiver.recv() {
609                    Ok(display_list_data) => display_list_data,
610                    Err(error) => {
611                        return warn!(
612                            "Could not receive WebRender display list items data: {error}"
613                        );
614                    },
615                };
616                let cache_data = match display_list_receiver.recv() {
617                    Ok(display_list_data) => display_list_data,
618                    Err(error) => {
619                        return warn!(
620                            "Could not receive WebRender display list cache data: {error}"
621                        );
622                    },
623                };
624                let spatial_tree = match display_list_receiver.recv() {
625                    Ok(display_list_data) => display_list_data,
626                    Err(error) => {
627                        return warn!(
628                            "Could not receive WebRender display list spatial tree: {error}."
629                        );
630                    },
631                };
632                let built_display_list = BuiltDisplayList::from_data(
633                    DisplayListPayload {
634                        items_data,
635                        cache_data,
636                        spatial_tree,
637                    },
638                    display_list_descriptor,
639                );
640
641                #[cfg(feature = "tracing")]
642                let _span = tracing::trace_span!(
643                    "ScriptToCompositorMsg::BuiltDisplayList",
644                    servo_profiling = true,
645                )
646                .entered();
647
648                let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
649                    return warn!("Could not find WebView for incoming display list");
650                };
651
652                let old_scale = webview_renderer.device_pixels_per_page_pixel();
653
654                let pipeline_id = display_list_info.pipeline_id;
655                let details = webview_renderer.ensure_pipeline_details(pipeline_id.into());
656                details.install_new_scroll_tree(display_list_info.scroll_tree);
657                details.viewport_scale =
658                    Some(display_list_info.viewport_details.hidpi_scale_factor);
659
660                let epoch = display_list_info.epoch;
661                let first_reflow = display_list_info.first_reflow;
662                if details.first_paint_metric == PaintMetricState::Waiting {
663                    details.first_paint_metric = PaintMetricState::Seen(epoch, first_reflow);
664                }
665                if details.first_contentful_paint_metric == PaintMetricState::Waiting &&
666                    display_list_info.is_contentful
667                {
668                    details.first_contentful_paint_metric =
669                        PaintMetricState::Seen(epoch, first_reflow);
670                }
671
672                let mut transaction = Transaction::new();
673                let is_root_pipeline =
674                    Some(pipeline_id.into()) == webview_renderer.root_pipeline_id;
675                if is_root_pipeline && old_scale != webview_renderer.device_pixels_per_page_pixel()
676                {
677                    self.send_root_pipeline_display_list_in_transaction(&mut transaction);
678                }
679
680                transaction
681                    .set_display_list(display_list_info.epoch, (pipeline_id, built_display_list));
682                self.update_transaction_with_all_scroll_offsets(&mut transaction);
683                self.global.borrow_mut().send_transaction(transaction);
684            },
685
686            CompositorMsg::GenerateFrame => {
687                let mut global = self.global.borrow_mut();
688                global.frame_delayer.set_pending_frame(true);
689
690                if global.frame_delayer.needs_new_frame() {
691                    let mut transaction = Transaction::new();
692                    self.generate_frame(&mut transaction, RenderReasons::SCENE);
693                    global.send_transaction(transaction);
694
695                    let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
696                    let _ = global.constellation_sender.send(
697                        EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
698                            waiting_pipelines,
699                        ),
700                    );
701                    global.frame_delayer.set_pending_frame(false);
702                }
703            },
704
705            CompositorMsg::GenerateImageKey(sender) => {
706                let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
707            },
708
709            CompositorMsg::GenerateImageKeysForPipeline(pipeline_id) => {
710                let image_keys = (0..pref!(image_key_batch_size))
711                    .map(|_| self.global.borrow().webrender_api.generate_image_key())
712                    .collect();
713                if let Err(error) = self.global.borrow().constellation_sender.send(
714                    EmbedderToConstellationMessage::SendImageKeysForPipeline(
715                        pipeline_id,
716                        image_keys,
717                    ),
718                ) {
719                    warn!("Sending Image Keys to Constellation failed with({error:?}).");
720                }
721            },
722            CompositorMsg::UpdateImages(updates) => {
723                let mut global = self.global.borrow_mut();
724                let mut txn = Transaction::new();
725                for update in updates {
726                    match update {
727                        ImageUpdate::AddImage(key, desc, data) => {
728                            txn.add_image(key, desc, data.into(), None)
729                        },
730                        ImageUpdate::DeleteImage(key) => {
731                            txn.delete_image(key);
732                            global.frame_delayer.delete_image(key);
733                        },
734                        ImageUpdate::UpdateImage(key, desc, data, epoch) => {
735                            if let Some(epoch) = epoch {
736                                global.frame_delayer.update_image(key, epoch);
737                            }
738                            txn.update_image(key, desc, data.into(), &DirtyRect::All)
739                        },
740                    }
741                }
742
743                if global.frame_delayer.needs_new_frame() {
744                    global.frame_delayer.set_pending_frame(false);
745                    self.generate_frame(&mut txn, RenderReasons::SCENE);
746                    let waiting_pipelines = global.frame_delayer.take_waiting_pipelines();
747                    let _ = global.constellation_sender.send(
748                        EmbedderToConstellationMessage::NoLongerWaitingOnAsynchronousImageUpdates(
749                            waiting_pipelines,
750                        ),
751                    );
752                }
753
754                global.send_transaction(txn);
755            },
756
757            CompositorMsg::DelayNewFrameForCanvas(pipeline_id, canvas_epoch, image_keys) => self
758                .global
759                .borrow_mut()
760                .frame_delayer
761                .add_delay(pipeline_id, canvas_epoch, image_keys),
762
763            CompositorMsg::AddFont(font_key, data, index) => {
764                self.add_font(font_key, index, data);
765            },
766
767            CompositorMsg::AddSystemFont(font_key, native_handle) => {
768                let mut transaction = Transaction::new();
769                transaction.add_native_font(font_key, native_handle);
770                self.global.borrow_mut().send_transaction(transaction);
771            },
772
773            CompositorMsg::AddFontInstance(
774                font_instance_key,
775                font_key,
776                size,
777                flags,
778                variations,
779            ) => {
780                self.add_font_instance(font_instance_key, font_key, size, flags, variations);
781            },
782
783            CompositorMsg::RemoveFonts(keys, instance_keys) => {
784                let mut transaction = Transaction::new();
785
786                for instance in instance_keys.into_iter() {
787                    transaction.delete_font_instance(instance);
788                }
789                for key in keys.into_iter() {
790                    transaction.delete_font(key);
791                }
792
793                self.global.borrow_mut().send_transaction(transaction);
794            },
795
796            CompositorMsg::GenerateFontKeys(
797                number_of_font_keys,
798                number_of_font_instance_keys,
799                result_sender,
800            ) => {
801                self.handle_generate_font_keys(
802                    number_of_font_keys,
803                    number_of_font_instance_keys,
804                    result_sender,
805                );
806            },
807            CompositorMsg::Viewport(webview_id, viewport_description) => {
808                if let Some(webview) = self.webview_renderers.get_mut(webview_id) {
809                    webview.set_viewport_description(viewport_description);
810                }
811            },
812        }
813    }
814
815    /// Handle messages sent to the compositor during the shutdown process. In general,
816    /// the things the compositor can do in this state are limited. It's very important to
817    /// answer any synchronous messages though as other threads might be waiting on the
818    /// results to finish their own shut down process. We try to do as little as possible
819    /// during this time.
820    ///
821    /// When that involves generating WebRender ids, our approach here is to simply
822    /// generate them, but assume they will never be used, since once shutting down the
823    /// compositor no longer does any WebRender frame generation.
824    fn handle_browser_message_while_shutting_down(&mut self, msg: CompositorMsg) {
825        match msg {
826            CompositorMsg::PipelineExited(webview_id, pipeline_id, pipeline_exit_source) => {
827                debug!(
828                    "Compositor got pipeline exited: {:?} {:?}",
829                    webview_id, pipeline_id
830                );
831                if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
832                    webview_renderer.pipeline_exited(pipeline_id, pipeline_exit_source);
833                }
834            },
835            CompositorMsg::GenerateImageKey(sender) => {
836                let _ = sender.send(self.global.borrow().webrender_api.generate_image_key());
837            },
838            CompositorMsg::GenerateFontKeys(
839                number_of_font_keys,
840                number_of_font_instance_keys,
841                result_sender,
842            ) => {
843                self.handle_generate_font_keys(
844                    number_of_font_keys,
845                    number_of_font_instance_keys,
846                    result_sender,
847                );
848            },
849            CompositorMsg::NewWebRenderFrameReady(..) => {
850                // Subtract from the number of pending frames, but do not do any compositing.
851                self.pending_frames.set(self.pending_frames.get() - 1);
852            },
853            _ => {
854                debug!("Ignoring message ({:?} while shutting down", msg);
855            },
856        }
857    }
858
859    /// Generate the font keys and send them to the `result_sender`.
860    fn handle_generate_font_keys(
861        &self,
862        number_of_font_keys: usize,
863        number_of_font_instance_keys: usize,
864        result_sender: GenericSender<(Vec<FontKey>, Vec<FontInstanceKey>)>,
865    ) {
866        let font_keys = (0..number_of_font_keys)
867            .map(|_| self.global.borrow().webrender_api.generate_font_key())
868            .collect();
869        let font_instance_keys = (0..number_of_font_instance_keys)
870            .map(|_| {
871                self.global
872                    .borrow()
873                    .webrender_api
874                    .generate_font_instance_key()
875            })
876            .collect();
877        let _ = result_sender.send((font_keys, font_instance_keys));
878    }
879
880    /// Queue a new frame in the transaction and increase the pending frames count.
881    pub(crate) fn generate_frame(&self, transaction: &mut Transaction, reason: RenderReasons) {
882        self.pending_frames.set(self.pending_frames.get() + 1);
883        transaction.generate_frame(0, true /* present */, reason);
884    }
885
886    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
887    /// for each visible top-level browsing context, applying a transformation on the root for
888    /// pinch zoom, page zoom, and HiDPI scaling.
889    fn send_root_pipeline_display_list(&mut self) {
890        let mut transaction = Transaction::new();
891        self.send_root_pipeline_display_list_in_transaction(&mut transaction);
892        self.generate_frame(&mut transaction, RenderReasons::SCENE);
893        self.global.borrow_mut().send_transaction(transaction);
894    }
895
896    /// Set the root pipeline for our WebRender scene to a display list that consists of an iframe
897    /// for each visible top-level browsing context, applying a transformation on the root for
898    /// pinch zoom, page zoom, and HiDPI scaling.
899    pub(crate) fn send_root_pipeline_display_list_in_transaction(
900        &self,
901        transaction: &mut Transaction,
902    ) {
903        // Every display list needs a pipeline, but we'd like to choose one that is unlikely
904        // to conflict with our content pipelines, which start at (1, 1). (0, 0) is WebRender's
905        // dummy pipeline, so we choose (0, 1).
906        let root_pipeline = WebRenderPipelineId(0, 1);
907        transaction.set_root_pipeline(root_pipeline);
908
909        let mut builder = webrender_api::DisplayListBuilder::new(root_pipeline);
910        builder.begin();
911
912        let root_reference_frame = SpatialId::root_reference_frame(root_pipeline);
913
914        let viewport_size = self.rendering_context.size2d().to_f32().to_untyped();
915        let viewport_rect = LayoutRect::from_origin_and_size(
916            LayoutPoint::zero(),
917            LayoutSize::from_untyped(viewport_size),
918        );
919
920        let root_clip_id = builder.define_clip_rect(root_reference_frame, viewport_rect);
921        let clip_chain_id = builder.define_clip_chain(None, [root_clip_id]);
922        for (_, webview_renderer) in self.webview_renderers.painting_order() {
923            let Some(pipeline_id) = webview_renderer.root_pipeline_id else {
924                continue;
925            };
926
927            let device_pixels_per_page_pixel = webview_renderer.device_pixels_per_page_pixel().0;
928            let webview_reference_frame = builder.push_reference_frame(
929                LayoutPoint::zero(),
930                root_reference_frame,
931                TransformStyle::Flat,
932                PropertyBinding::Value(Transform3D::scale(
933                    device_pixels_per_page_pixel,
934                    device_pixels_per_page_pixel,
935                    1.,
936                )),
937                ReferenceFrameKind::Transform {
938                    is_2d_scale_translation: true,
939                    should_snap: true,
940                    paired_with_perspective: false,
941                },
942                SpatialTreeItemKey::new(0, 0),
943            );
944
945            let scaled_webview_rect = webview_renderer.rect / device_pixels_per_page_pixel;
946            builder.push_iframe(
947                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
948                LayoutRect::from_untyped(&scaled_webview_rect.to_untyped()),
949                &SpaceAndClipInfo {
950                    spatial_id: webview_reference_frame,
951                    clip_chain_id,
952                },
953                pipeline_id.into(),
954                true,
955            );
956        }
957
958        let built_display_list = builder.end();
959
960        // NB: We are always passing 0 as the epoch here, but this doesn't seem to
961        // be an issue. WebRender will still update the scene and generate a new
962        // frame even though the epoch hasn't changed.
963        transaction.set_display_list(WebRenderEpoch(0), built_display_list);
964        self.update_transaction_with_all_scroll_offsets(transaction);
965    }
966
967    /// Update the given transaction with the scroll offsets of all active scroll nodes in
968    /// the WebRender scene. This is necessary because WebRender does not preserve scroll
969    /// offsets between scroll tree modifications. If a display list could potentially
970    /// modify a scroll tree branch, WebRender needs to have scroll offsets for that
971    /// branch.
972    ///
973    /// TODO(mrobinson): Could we only send offsets for the branch being modified
974    /// and not the entire scene?
975    fn update_transaction_with_all_scroll_offsets(&self, transaction: &mut Transaction) {
976        for webview_renderer in self.webview_renderers.iter() {
977            for details in webview_renderer.pipelines.values() {
978                for node in details.scroll_tree.nodes.iter() {
979                    let (Some(offset), Some(external_id)) = (node.offset(), node.external_id())
980                    else {
981                        continue;
982                    };
983
984                    transaction.set_scroll_offsets(
985                        external_id,
986                        vec![SampledScrollOffset {
987                            offset,
988                            generation: 0,
989                        }],
990                    );
991                }
992            }
993        }
994    }
995
996    pub fn add_webview(
997        &mut self,
998        webview: Box<dyn WebViewTrait>,
999        viewport_details: ViewportDetails,
1000    ) {
1001        self.webview_renderers
1002            .entry(webview.id())
1003            .or_insert(WebViewRenderer::new(
1004                self.global.clone(),
1005                webview,
1006                viewport_details,
1007            ));
1008    }
1009
1010    fn set_frame_tree_for_webview(&mut self, frame_tree: &SendableFrameTree) {
1011        debug!("{}: Setting frame tree for webview", frame_tree.pipeline.id);
1012
1013        let webview_id = frame_tree.pipeline.webview_id;
1014        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1015            warn!(
1016                "Attempted to set frame tree on unknown WebView (perhaps closed?): {webview_id:?}"
1017            );
1018            return;
1019        };
1020
1021        webview_renderer.set_frame_tree(frame_tree);
1022        self.send_root_pipeline_display_list();
1023    }
1024
1025    fn remove_webview(&mut self, webview_id: WebViewId) {
1026        debug!("{}: Removing", webview_id);
1027        if self.webview_renderers.remove(webview_id).is_err() {
1028            warn!("{webview_id}: Removing unknown webview");
1029            return;
1030        };
1031
1032        self.send_root_pipeline_display_list();
1033    }
1034
1035    pub fn show_webview(
1036        &mut self,
1037        webview_id: WebViewId,
1038        hide_others: bool,
1039    ) -> Result<(), UnknownWebView> {
1040        debug!("{webview_id}: Showing webview; hide_others={hide_others}");
1041        let painting_order_changed = if hide_others {
1042            let result = self
1043                .webview_renderers
1044                .painting_order()
1045                .map(|(&id, _)| id)
1046                .ne(once(webview_id));
1047            self.webview_renderers.hide_all();
1048            self.webview_renderers.show(webview_id)?;
1049            result
1050        } else {
1051            self.webview_renderers.show(webview_id)?
1052        };
1053        if painting_order_changed {
1054            self.send_root_pipeline_display_list();
1055        }
1056        Ok(())
1057    }
1058
1059    pub fn hide_webview(&mut self, webview_id: WebViewId) -> Result<(), UnknownWebView> {
1060        debug!("{webview_id}: Hiding webview");
1061        if self.webview_renderers.hide(webview_id)? {
1062            self.send_root_pipeline_display_list();
1063        }
1064        Ok(())
1065    }
1066
1067    pub fn raise_webview_to_top(
1068        &mut self,
1069        webview_id: WebViewId,
1070        hide_others: bool,
1071    ) -> Result<(), UnknownWebView> {
1072        debug!("{webview_id}: Raising webview to top; hide_others={hide_others}");
1073        let painting_order_changed = if hide_others {
1074            let result = self
1075                .webview_renderers
1076                .painting_order()
1077                .map(|(&id, _)| id)
1078                .ne(once(webview_id));
1079            self.webview_renderers.hide_all();
1080            self.webview_renderers.raise_to_top(webview_id)?;
1081            result
1082        } else {
1083            self.webview_renderers.raise_to_top(webview_id)?
1084        };
1085        if painting_order_changed {
1086            self.send_root_pipeline_display_list();
1087        }
1088        Ok(())
1089    }
1090
1091    pub fn move_resize_webview(&mut self, webview_id: WebViewId, rect: DeviceRect) {
1092        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1093            return;
1094        }
1095        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1096            return;
1097        };
1098        if !webview_renderer.set_rect(rect) {
1099            return;
1100        }
1101
1102        self.send_root_pipeline_display_list();
1103        self.set_needs_repaint(RepaintReason::Resize);
1104    }
1105
1106    pub fn set_hidpi_scale_factor(
1107        &mut self,
1108        webview_id: WebViewId,
1109        new_scale_factor: Scale<f32, DeviceIndependentPixel, DevicePixel>,
1110    ) {
1111        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1112            return;
1113        }
1114        let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) else {
1115            return;
1116        };
1117        if !webview_renderer.set_hidpi_scale_factor(new_scale_factor) {
1118            return;
1119        }
1120
1121        self.send_root_pipeline_display_list();
1122        self.set_needs_repaint(RepaintReason::Resize);
1123    }
1124
1125    pub fn resize_rendering_context(&mut self, new_size: PhysicalSize<u32>) {
1126        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1127            return;
1128        }
1129        if self.rendering_context.size() == new_size {
1130            return;
1131        }
1132
1133        self.rendering_context.resize(new_size);
1134
1135        let mut transaction = Transaction::new();
1136        let output_region = DeviceIntRect::new(
1137            Point2D::zero(),
1138            Point2D::new(new_size.width as i32, new_size.height as i32),
1139        );
1140        transaction.set_document_view(output_region);
1141        self.global.borrow_mut().send_transaction(transaction);
1142
1143        self.send_root_pipeline_display_list();
1144        self.set_needs_repaint(RepaintReason::Resize);
1145    }
1146
1147    pub fn on_zoom_reset_window_event(&mut self, webview_id: WebViewId) {
1148        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1149            return;
1150        }
1151
1152        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1153            webview_renderer.set_page_zoom(Scale::new(1.0));
1154        }
1155    }
1156
1157    pub fn on_zoom_window_event(&mut self, webview_id: WebViewId, magnification: f32) {
1158        if self.global.borrow().shutdown_state() != ShutdownState::NotShuttingDown {
1159            return;
1160        }
1161
1162        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1163            let current_page_zoom = webview_renderer.page_zoom();
1164            webview_renderer.set_page_zoom(current_page_zoom * Scale::new(magnification));
1165        }
1166    }
1167
1168    /// Returns true if any animation callbacks (ie `requestAnimationFrame`) are waiting for a response.
1169    fn animation_callbacks_running(&self) -> bool {
1170        self.webview_renderers
1171            .iter()
1172            .any(WebViewRenderer::animation_callbacks_running)
1173    }
1174
1175    /// Query the constellation to see if the current compositor
1176    /// output matches the current frame tree output, and if the
1177    /// associated script threads are idle.
1178    fn is_ready_to_paint_image_output(&mut self) -> Result<(), NotReadyToPaint> {
1179        match self.ready_to_save_state {
1180            ReadyState::Unknown => {
1181                // Unsure if the output image is stable.
1182
1183                // Collect the currently painted epoch of each pipeline that is
1184                // complete (i.e. has *all* layers painted to the requested epoch).
1185                // This gets sent to the constellation for comparison with the current
1186                // frame tree.
1187                let mut pipeline_epochs = FxHashMap::default();
1188                for id in self
1189                    .webview_renderers
1190                    .iter()
1191                    .flat_map(WebViewRenderer::pipeline_ids)
1192                {
1193                    if let Some(WebRenderEpoch(epoch)) = self
1194                        .webrender
1195                        .as_ref()
1196                        .and_then(|wr| wr.current_epoch(self.webrender_document(), id.into()))
1197                    {
1198                        let epoch = Epoch(epoch);
1199                        pipeline_epochs.insert(*id, epoch);
1200                    }
1201                }
1202
1203                // Pass the pipeline/epoch states to the constellation and check
1204                // if it's safe to output the image.
1205                let msg = EmbedderToConstellationMessage::IsReadyToSaveImage(pipeline_epochs);
1206                if let Err(e) = self.global.borrow().constellation_sender.send(msg) {
1207                    warn!("Sending ready to save to constellation failed ({:?}).", e);
1208                }
1209                self.ready_to_save_state = ReadyState::WaitingForConstellationReply;
1210                Err(NotReadyToPaint::JustNotifiedConstellation)
1211            },
1212            ReadyState::WaitingForConstellationReply => {
1213                // If waiting on a reply from the constellation to the last
1214                // query if the image is stable, then assume not ready yet.
1215                Err(NotReadyToPaint::WaitingOnConstellation)
1216            },
1217            ReadyState::ReadyToSaveImage => {
1218                // Constellation has replied at some point in the past
1219                // that the current output image is stable and ready
1220                // for saving.
1221                // Reset the flag so that we check again in the future
1222                // TODO: only reset this if we load a new document?
1223                self.ready_to_save_state = ReadyState::Unknown;
1224                Ok(())
1225            },
1226        }
1227    }
1228
1229    /// Render the WebRender scene to the active `RenderingContext`. If successful, trigger
1230    /// the next round of animations.
1231    pub fn render(&mut self) -> bool {
1232        self.global
1233            .borrow()
1234            .refresh_driver
1235            .notify_will_paint(self.webview_renderers.iter());
1236
1237        if let Err(error) = self.render_inner() {
1238            warn!("Unable to render: {error:?}");
1239            return false;
1240        }
1241
1242        // We've painted the default target, which means that from the embedder's perspective,
1243        // the scene no longer needs to be repainted.
1244        self.needs_repaint.set(RepaintReason::empty());
1245
1246        true
1247    }
1248
1249    /// Render the WebRender scene to the shared memory, without updating other state of this
1250    /// [`IOCompositor`]. If succesful return the output image in shared memory.
1251    pub fn render_to_shared_memory(
1252        &mut self,
1253        webview_id: WebViewId,
1254        page_rect: Option<Rect<f32, CSSPixel>>,
1255    ) -> Result<Option<RasterImage>, UnableToComposite> {
1256        self.render_inner()?;
1257
1258        let size = self.rendering_context.size2d().to_i32();
1259        let rect = if let Some(rect) = page_rect {
1260            let scale = self
1261                .webview_renderers
1262                .get(webview_id)
1263                .map(WebViewRenderer::device_pixels_per_page_pixel)
1264                .unwrap_or_else(|| Scale::new(1.0));
1265            let rect = scale.transform_rect(&rect);
1266
1267            let x = rect.origin.x as i32;
1268            // We need to convert to the bottom-left origin coordinate
1269            // system used by OpenGL
1270            let y = (size.height as f32 - rect.origin.y - rect.size.height) as i32;
1271            let w = rect.size.width as i32;
1272            let h = rect.size.height as i32;
1273
1274            DeviceIntRect::from_origin_and_size(Point2D::new(x, y), Size2D::new(w, h))
1275        } else {
1276            DeviceIntRect::from_origin_and_size(Point2D::origin(), size)
1277        };
1278
1279        Ok(self
1280            .rendering_context
1281            .read_to_image(rect)
1282            .map(|image| RasterImage {
1283                metadata: ImageMetadata {
1284                    width: image.width(),
1285                    height: image.height(),
1286                },
1287                format: PixelFormat::RGBA8,
1288                frames: vec![ImageFrame {
1289                    delay: None,
1290                    byte_range: 0..image.len(),
1291                    width: image.width(),
1292                    height: image.height(),
1293                }],
1294                bytes: ipc::IpcSharedMemory::from_bytes(&image),
1295                id: None,
1296                cors_status: CorsStatus::Safe,
1297            }))
1298    }
1299
1300    #[servo_tracing::instrument(skip_all)]
1301    fn render_inner(&mut self) -> Result<(), UnableToComposite> {
1302        if let Err(err) = self.rendering_context.make_current() {
1303            warn!("Failed to make the rendering context current: {:?}", err);
1304        }
1305        self.assert_no_gl_error();
1306
1307        if let Some(webrender) = self.webrender.as_mut() {
1308            webrender.update();
1309        }
1310
1311        if opts::get().wait_for_stable_image {
1312            if let Err(result) = self.is_ready_to_paint_image_output() {
1313                return Err(UnableToComposite::NotReadyToPaintImage(result));
1314            }
1315        }
1316
1317        self.rendering_context.prepare_for_rendering();
1318
1319        let time_profiler_chan = self.global.borrow().time_profiler_chan.clone();
1320        time_profile!(
1321            ProfilerCategory::Compositing,
1322            None,
1323            time_profiler_chan,
1324            || {
1325                trace!("Compositing");
1326
1327                // Paint the scene.
1328                // TODO(gw): Take notice of any errors the renderer returns!
1329                self.clear_background();
1330                if let Some(webrender) = self.webrender.as_mut() {
1331                    let size = self.rendering_context.size2d().to_i32();
1332                    webrender.render(size, 0 /* buffer_age */).ok();
1333                }
1334            },
1335        );
1336
1337        self.send_pending_paint_metrics_messages_after_composite();
1338        Ok(())
1339    }
1340
1341    /// Send all pending paint metrics messages after a composite operation, which may advance
1342    /// the epoch for pipelines in the WebRender scene.
1343    ///
1344    /// If there are pending paint metrics, we check if any of the painted epochs is one
1345    /// of the ones that the paint metrics recorder is expecting. In that case, we get the
1346    /// current time, inform the constellation about it and remove the pending metric from
1347    /// the list.
1348    fn send_pending_paint_metrics_messages_after_composite(&mut self) {
1349        let paint_time = CrossProcessInstant::now();
1350        let document_id = self.webrender_document();
1351        for webview_renderer in self.webview_renderers.iter_mut() {
1352            for (pipeline_id, pipeline) in webview_renderer.pipelines.iter_mut() {
1353                let Some(current_epoch) = self
1354                    .webrender
1355                    .as_ref()
1356                    .and_then(|wr| wr.current_epoch(document_id, pipeline_id.into()))
1357                else {
1358                    continue;
1359                };
1360
1361                match pipeline.first_paint_metric {
1362                    // We need to check whether the current epoch is later, because
1363                    // CrossProcessCompositorMessage::SendInitialTransaction sends an
1364                    // empty display list to WebRender which can happen before we receive
1365                    // the first "real" display list.
1366                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1367                        assert!(epoch <= current_epoch);
1368                        if let Err(error) = self.global.borrow().constellation_sender.send(
1369                            EmbedderToConstellationMessage::PaintMetric(
1370                                *pipeline_id,
1371                                PaintMetricEvent::FirstPaint(paint_time, first_reflow),
1372                            ),
1373                        ) {
1374                            warn!(
1375                                "Sending paint metric event to constellation failed ({error:?})."
1376                            );
1377                        }
1378                        pipeline.first_paint_metric = PaintMetricState::Sent;
1379                    },
1380                    _ => {},
1381                }
1382
1383                match pipeline.first_contentful_paint_metric {
1384                    PaintMetricState::Seen(epoch, first_reflow) if epoch <= current_epoch => {
1385                        if let Err(error) = self.global.borrow().constellation_sender.send(
1386                            EmbedderToConstellationMessage::PaintMetric(
1387                                *pipeline_id,
1388                                PaintMetricEvent::FirstContentfulPaint(paint_time, first_reflow),
1389                            ),
1390                        ) {
1391                            warn!(
1392                                "Sending paint metric event to constellation failed ({error:?})."
1393                            );
1394                        }
1395                        pipeline.first_contentful_paint_metric = PaintMetricState::Sent;
1396                    },
1397                    _ => {},
1398                }
1399            }
1400        }
1401    }
1402
1403    fn clear_background(&self) {
1404        let gl = &self.global.borrow().webrender_gl;
1405        self.assert_gl_framebuffer_complete();
1406
1407        // Always clear the entire RenderingContext, regardless of how many WebViews there are
1408        // or where they are positioned. This is so WebView actually clears even before the
1409        // first WebView is ready.
1410        let color = servo_config::pref!(shell_background_color_rgba);
1411        gl.clear_color(
1412            color[0] as f32,
1413            color[1] as f32,
1414            color[2] as f32,
1415            color[3] as f32,
1416        );
1417        gl.clear(gleam::gl::COLOR_BUFFER_BIT);
1418    }
1419
1420    #[track_caller]
1421    fn assert_no_gl_error(&self) {
1422        debug_assert_eq!(
1423            self.global.borrow().webrender_gl.get_error(),
1424            gleam::gl::NO_ERROR
1425        );
1426    }
1427
1428    #[track_caller]
1429    fn assert_gl_framebuffer_complete(&self) {
1430        debug_assert_eq!(
1431            (
1432                self.global.borrow().webrender_gl.get_error(),
1433                self.global
1434                    .borrow()
1435                    .webrender_gl
1436                    .check_frame_buffer_status(gleam::gl::FRAMEBUFFER)
1437            ),
1438            (gleam::gl::NO_ERROR, gleam::gl::FRAMEBUFFER_COMPLETE)
1439        );
1440    }
1441
1442    /// Get the message receiver for this [`IOCompositor`].
1443    pub fn receiver(&self) -> Ref<'_, RoutedReceiver<CompositorMsg>> {
1444        Ref::map(self.global.borrow(), |global| &global.compositor_receiver)
1445    }
1446
1447    #[servo_tracing::instrument(skip_all)]
1448    pub fn handle_messages(&mut self, mut messages: Vec<CompositorMsg>) {
1449        // Check for new messages coming from the other threads in the system.
1450        let mut found_recomposite_msg = false;
1451        messages.retain(|message| {
1452            match message {
1453                CompositorMsg::NewWebRenderFrameReady(..) if found_recomposite_msg => {
1454                    // Only take one of duplicate NewWebRendeFrameReady messages, but do subtract
1455                    // one frame from the pending frames.
1456                    self.pending_frames.set(self.pending_frames.get() - 1);
1457                    false
1458                },
1459                CompositorMsg::NewWebRenderFrameReady(..) => {
1460                    found_recomposite_msg = true;
1461                    true
1462                },
1463                _ => true,
1464            }
1465        });
1466
1467        for message in messages {
1468            self.handle_browser_message(message);
1469            if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1470                return;
1471            }
1472        }
1473    }
1474
1475    #[servo_tracing::instrument(skip_all)]
1476    pub fn perform_updates(&mut self) -> bool {
1477        if self.global.borrow().shutdown_state() == ShutdownState::FinishedShuttingDown {
1478            return false;
1479        }
1480
1481        #[cfg(feature = "webxr")]
1482        // Run the WebXR main thread
1483        self.global.borrow_mut().webxr_main_thread.run_one_frame();
1484
1485        // The WebXR thread may make a different context current
1486        if let Err(err) = self.rendering_context.make_current() {
1487            warn!("Failed to make the rendering context current: {:?}", err);
1488        }
1489
1490        let mut need_zoom = false;
1491        let scroll_offset_updates: Vec<_> = self
1492            .webview_renderers
1493            .iter_mut()
1494            .filter_map(|webview_renderer| {
1495                let (zoom, scroll_result) =
1496                    webview_renderer.process_pending_scroll_and_pinch_zoom_events();
1497                need_zoom = need_zoom || (zoom == PinchZoomResult::DidPinchZoom);
1498                scroll_result
1499            })
1500            .collect();
1501
1502        if need_zoom || !scroll_offset_updates.is_empty() {
1503            let mut transaction = Transaction::new();
1504            if need_zoom {
1505                self.send_root_pipeline_display_list_in_transaction(&mut transaction);
1506            }
1507            for update in scroll_offset_updates {
1508                transaction.set_scroll_offsets(
1509                    update.external_scroll_id,
1510                    vec![SampledScrollOffset {
1511                        offset: update.offset,
1512                        generation: 0,
1513                    }],
1514                );
1515            }
1516
1517            self.generate_frame(&mut transaction, RenderReasons::APZ);
1518            self.global.borrow_mut().send_transaction(transaction);
1519        }
1520
1521        self.global.borrow().shutdown_state() != ShutdownState::FinishedShuttingDown
1522    }
1523
1524    pub fn toggle_webrender_debug(&mut self, option: WebRenderDebugOption) {
1525        let Some(webrender) = self.webrender.as_mut() else {
1526            return;
1527        };
1528        let mut flags = webrender.get_debug_flags();
1529        let flag = match option {
1530            WebRenderDebugOption::Profiler => {
1531                webrender::DebugFlags::PROFILER_DBG |
1532                    webrender::DebugFlags::GPU_TIME_QUERIES |
1533                    webrender::DebugFlags::GPU_SAMPLE_QUERIES
1534            },
1535            WebRenderDebugOption::TextureCacheDebug => webrender::DebugFlags::TEXTURE_CACHE_DBG,
1536            WebRenderDebugOption::RenderTargetDebug => webrender::DebugFlags::RENDER_TARGET_DBG,
1537        };
1538        flags.toggle(flag);
1539        webrender.set_debug_flags(flags);
1540
1541        let mut txn = Transaction::new();
1542        self.generate_frame(&mut txn, RenderReasons::TESTING);
1543        self.global.borrow_mut().send_transaction(txn);
1544    }
1545
1546    pub fn capture_webrender(&mut self) {
1547        let capture_id = SystemTime::now()
1548            .duration_since(UNIX_EPOCH)
1549            .unwrap_or_default()
1550            .as_secs()
1551            .to_string();
1552        let available_path = [env::current_dir(), Ok(env::temp_dir())]
1553            .iter()
1554            .filter_map(|val| {
1555                val.as_ref()
1556                    .map(|dir| dir.join("webrender-captures").join(&capture_id))
1557                    .ok()
1558            })
1559            .find(|val| create_dir_all(val).is_ok());
1560
1561        let Some(capture_path) = available_path else {
1562            eprintln!("Couldn't create a path for WebRender captures.");
1563            return;
1564        };
1565
1566        println!("Saving WebRender capture to {capture_path:?}");
1567        self.global
1568            .borrow()
1569            .webrender_api
1570            .save_capture(capture_path.clone(), CaptureBits::all());
1571    }
1572
1573    fn add_font_instance(
1574        &mut self,
1575        instance_key: FontInstanceKey,
1576        font_key: FontKey,
1577        size: f32,
1578        flags: FontInstanceFlags,
1579        variations: Vec<FontVariation>,
1580    ) {
1581        let variations = if pref!(layout_variable_fonts_enabled) {
1582            variations
1583        } else {
1584            vec![]
1585        };
1586
1587        let mut transaction = Transaction::new();
1588
1589        let font_instance_options = FontInstanceOptions {
1590            flags,
1591            ..Default::default()
1592        };
1593        transaction.add_font_instance(
1594            instance_key,
1595            font_key,
1596            size,
1597            Some(font_instance_options),
1598            None,
1599            variations,
1600        );
1601
1602        self.global.borrow_mut().send_transaction(transaction);
1603    }
1604
1605    fn add_font(&mut self, font_key: FontKey, index: u32, data: Arc<IpcSharedMemory>) {
1606        let mut transaction = Transaction::new();
1607        transaction.add_raw_font(font_key, (**data).into(), index);
1608        self.global.borrow_mut().send_transaction(transaction);
1609    }
1610
1611    pub fn notify_input_event(&mut self, webview_id: WebViewId, event: InputEvent) {
1612        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1613            webview_renderer.notify_input_event(event);
1614        }
1615    }
1616
1617    pub fn notify_scroll_event(
1618        &mut self,
1619        webview_id: WebViewId,
1620        scroll_location: ScrollLocation,
1621        cursor: DeviceIntPoint,
1622    ) {
1623        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1624            webview_renderer.notify_scroll_event(scroll_location, cursor);
1625        }
1626    }
1627
1628    pub fn on_vsync(&mut self, webview_id: WebViewId) {
1629        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1630            webview_renderer.on_vsync();
1631        }
1632    }
1633
1634    pub fn set_pinch_zoom(&mut self, webview_id: WebViewId, magnification: f32) {
1635        if let Some(webview_renderer) = self.webview_renderers.get_mut(webview_id) {
1636            webview_renderer.set_pinch_zoom(magnification);
1637        }
1638    }
1639
1640    fn webrender_document(&self) -> DocumentId {
1641        self.global.borrow().webrender_document
1642    }
1643
1644    fn shutdown_state(&self) -> ShutdownState {
1645        self.global.borrow().shutdown_state()
1646    }
1647
1648    fn refresh_cursor(&self) {
1649        let global = self.global.borrow();
1650        let Some(last_mouse_move_position) = global.last_mouse_move_position else {
1651            return;
1652        };
1653
1654        let Some(hit_test_result) = global
1655            .hit_test_at_point(last_mouse_move_position)
1656            .first()
1657            .cloned()
1658        else {
1659            return;
1660        };
1661
1662        if let Err(error) =
1663            global
1664                .constellation_sender
1665                .send(EmbedderToConstellationMessage::RefreshCursor(
1666                    hit_test_result.pipeline_id,
1667                ))
1668        {
1669            warn!("Sending event to constellation failed ({:?}).", error);
1670        }
1671    }
1672
1673    fn handle_new_webrender_frame_ready(&mut self, recomposite_needed: bool) {
1674        self.pending_frames.set(self.pending_frames.get() - 1);
1675        if recomposite_needed {
1676            self.refresh_cursor();
1677        }
1678        if recomposite_needed || self.animation_callbacks_running() {
1679            self.set_needs_repaint(RepaintReason::NewWebRenderFrame);
1680        }
1681    }
1682}
1683
1684/// A struct that is reponsible for delaying frame requests until all new canvas images
1685/// for a particular "update the rendering" call in the `ScriptThread` have been
1686/// sent to WebRender.
1687///
1688/// These images may be updated in WebRender asynchronously in the canvas task. A frame
1689/// is then requested if:
1690///
1691///  - The renderer has received a GenerateFrame message from a `ScriptThread`.
1692///  - All pending image updates have finished and have been noted in the [`FrameDelayer`].
1693#[derive(Default)]
1694struct FrameDelayer {
1695    /// The latest [`Epoch`] of canvas images that have been sent to WebRender. Note
1696    /// that this only records the `Epoch`s for canvases and only ones that are involved
1697    /// in "update the rendering".
1698    image_epochs: HashMap<ImageKey, Epoch>,
1699    /// A map of all pending canvas images
1700    pending_canvas_images: HashMap<ImageKey, Epoch>,
1701    /// Whether or not we have a pending frame.
1702    pending_frame: bool,
1703    /// A list of pipelines that should be notified when we are no longer waiting for
1704    /// canvas images.
1705    waiting_pipelines: FxHashSet<PipelineId>,
1706}
1707
1708impl FrameDelayer {
1709    fn delete_image(&mut self, image_key: ImageKey) {
1710        self.image_epochs.remove(&image_key);
1711        self.pending_canvas_images.remove(&image_key);
1712    }
1713
1714    fn update_image(&mut self, image_key: ImageKey, epoch: Epoch) {
1715        self.image_epochs.insert(image_key, epoch);
1716        let Entry::Occupied(entry) = self.pending_canvas_images.entry(image_key) else {
1717            return;
1718        };
1719        if *entry.get() <= epoch {
1720            entry.remove();
1721        }
1722    }
1723
1724    fn add_delay(
1725        &mut self,
1726        pipeline_id: PipelineId,
1727        canvas_epoch: Epoch,
1728        image_keys: Vec<ImageKey>,
1729    ) {
1730        for image_key in image_keys.into_iter() {
1731            // If we've already seen the necessary epoch for this image, do not
1732            // start waiting for it.
1733            if self
1734                .image_epochs
1735                .get(&image_key)
1736                .is_some_and(|epoch_seen| *epoch_seen >= canvas_epoch)
1737            {
1738                continue;
1739            }
1740            self.pending_canvas_images.insert(image_key, canvas_epoch);
1741        }
1742        self.waiting_pipelines.insert(pipeline_id);
1743    }
1744
1745    fn needs_new_frame(&self) -> bool {
1746        self.pending_frame && self.pending_canvas_images.is_empty()
1747    }
1748
1749    fn set_pending_frame(&mut self, value: bool) {
1750        self.pending_frame = value;
1751    }
1752
1753    fn take_waiting_pipelines(&mut self) -> Vec<PipelineId> {
1754        self.waiting_pipelines.drain().collect()
1755    }
1756}