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