From 46adfc18e5590ccfffac2c74643c72df17cfd6dc Mon Sep 17 00:00:00 2001 From: Martin Robinson Date: Mon, 4 Mar 2024 18:41:39 +0100 Subject: [PATCH] Have all new WebRender frames go through the same path This simplifies the way that the notifier passes new frame notifications to WebRender and keeps a simple count of pending frames. In addition, it makes sure that the compositor increases the pending frame count each time it calls generate frame. --- components/compositing/compositor.rs | 96 +++++++++++++++++----------- components/servo/lib.rs | 19 ++---- components/shared/compositing/lib.rs | 46 ++----------- 3 files changed, 68 insertions(+), 93 deletions(-) diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 65cfb606ba106..db4a00e38b13d 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -13,7 +13,7 @@ use std::time::{SystemTime, UNIX_EPOCH}; use canvas::canvas_paint_thread::ImageUpdate; use compositing_traits::{ - CanvasToCompositorMsg, CompositingReason, CompositionPipeline, CompositorMsg, + CanvasToCompositorMsg, CompositionPipeline, CompositorMsg, CompositorReceiver, ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, SendableFrameTree, }; @@ -172,9 +172,6 @@ pub struct IOCompositor { /// Pending scroll/zoom events. pending_scroll_zoom_events: Vec, - /// Whether we're waiting on a recomposite after dispatching a scroll. - waiting_for_results_of_scroll: bool, - /// Used by the logic that determines when it is safe to output an /// image for the reftest framework. ready_to_save_state: ReadyState, @@ -234,10 +231,8 @@ pub struct IOCompositor { /// True to translate mouse input into touch events. convert_mouse_to_touch: bool, - /// True if a WR frame render has been requested. Screenshots - /// taken before the render is complete will not reflect the - /// most up to date rendering. - waiting_on_pending_frame: bool, + /// The number of frames pending to receive from WebRender. + pending_frames: u32, /// Waiting for external code to call present. waiting_on_present: bool, @@ -262,6 +257,23 @@ enum ScrollZoomEvent { Scroll(ScrollEvent), } +/// Why we performed a composite. This is used for debugging. +/// +/// TODO: It would be good to have a bit more precision here about why a composite +/// was originally triggered, but that would require tracking the reason when a +/// frame is queued in WebRender and then remembering when the frame is ready. +#[derive(Clone, Copy, Debug, PartialEq)] +enum CompositingReason { + /// We're performing the single composite in headless mode. + Headless, + /// We're performing a composite to run an animation. + Animation, + /// A new WebRender frame has arrived. + NewWebRenderFrame, + /// The window has been resized and will need to be synchronously repainted. + Resize, +} + #[derive(Debug, PartialEq)] enum CompositionRequest { NoCompositingNecessary, @@ -376,7 +388,6 @@ impl IOCompositor { composition_request: CompositionRequest::NoCompositingNecessary, touch_handler: TouchHandler::new(), pending_scroll_zoom_events: Vec::new(), - waiting_for_results_of_scroll: false, composite_target, shutdown_state: ShutdownState::NotShuttingDown, page_zoom: Scale::new(1.0), @@ -404,7 +415,7 @@ impl IOCompositor { is_running_problem_test, exit_after_load, convert_mouse_to_touch, - waiting_on_pending_frame: false, + pending_frames: 0, waiting_on_present: false, } } @@ -537,11 +548,6 @@ impl IOCompositor { self.send_scroll_positions_to_layout_for_pipeline(&frame_tree.pipeline.id); }, - (CompositorMsg::Recomposite(reason), ShutdownState::NotShuttingDown) => { - self.waiting_on_pending_frame = false; - self.composition_request = CompositionRequest::CompositeNow(reason) - }, - (CompositorMsg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => { self.touch_handler.on_event_processed(result); }, @@ -562,8 +568,7 @@ impl IOCompositor { self.ready_to_save_state, ReadyState::WaitingForConstellationReply ); - if is_ready && !self.waiting_on_pending_frame && !self.waiting_for_results_of_scroll - { + if is_ready && self.pending_frames <= 0 { self.ready_to_save_state = ReadyState::ReadyToSaveImage; if self.is_running_problem_test { println!("ready to save image!"); @@ -592,17 +597,18 @@ impl IOCompositor { }, ( - CompositorMsg::NewScrollFrameReady(recomposite_needed), - ShutdownState::NotShuttingDown, + CompositorMsg::NewWebRenderFrameReady(recomposite_needed), + _, ) => { - self.waiting_for_results_of_scroll = false; - if let Some(result) = self.hit_test_at_device_point(self.cursor_pos) { - self.update_cursor(result); - } - if recomposite_needed { - self.composition_request = CompositionRequest::CompositeNow( - CompositingReason::NewWebRenderScrollFrame, - ); + assert!(self.pending_frames > 0); + self.pending_frames -= 1; + + if self.shutdown_state != ShutdownState::NotShuttingDown && recomposite_needed { + if let Some(result) = self.hit_test_at_device_point(self.cursor_pos) { + self.update_cursor(result); + } + self.composition_request = + CompositionRequest::CompositeNow(CompositingReason::NewWebRenderFrame); } }, @@ -685,10 +691,12 @@ impl IOCompositor { ForwardedToCompositorMsg::Layout( script_traits::ScriptToCompositorMsg::SendInitialTransaction(pipeline), ) => { - self.waiting_on_pending_frame = true; let mut txn = Transaction::new(); txn.set_display_list(WebRenderEpoch(0), (pipeline, Default::default())); + self.pending_frames += 1; + txn.generate_frame(0, RenderReasons::APZ); + self.webrender_api .send_transaction(self.webrender_document, txn); }, @@ -696,8 +704,6 @@ impl IOCompositor { ForwardedToCompositorMsg::Layout( script_traits::ScriptToCompositorMsg::SendScrollNode(point, scroll_id), ) => { - self.waiting_for_results_of_scroll = true; - let mut txn = Transaction::new(); let offset = point.to_vector(); txn.set_scroll_offsets( @@ -707,7 +713,10 @@ impl IOCompositor { generation: 0, }], ); + + self.pending_frames += 1; txn.generate_frame(0, RenderReasons::APZ); + self.webrender_api .send_transaction(self.webrender_document, txn); }, @@ -753,8 +762,6 @@ impl IOCompositor { display_list_descriptor, ); - self.waiting_on_pending_frame = true; - let pipeline_id = display_list_info.pipeline_id; let details = self.pipeline_details(PipelineId::from_webrender(pipeline_id)); details.most_recent_display_list_epoch = Some(display_list_info.epoch); @@ -778,7 +785,9 @@ impl IOCompositor { } } + self.pending_frames += 1; transaction.generate_frame(0, RenderReasons::SCENE); + self.webrender_api .send_transaction(self.webrender_document, transaction); }, @@ -1039,7 +1048,10 @@ impl IOCompositor { let mut txn = Transaction::new(); self.set_root_content_pipeline_handling_device_scaling(&mut txn); + + self.pending_frames += 1; txn.generate_frame(0, RenderReasons::NONE); + self.webrender_api .send_transaction(self.webrender_document, txn); @@ -1089,6 +1101,10 @@ impl IOCompositor { } pub fn on_resize_window_event(&mut self) -> bool { + if self.shutdown_state != ShutdownState::NotShuttingDown { + return false; + } + let old_coords = self.embedder_coordinates; self.embedder_coordinates = self.window.get_coordinates(); @@ -1460,10 +1476,11 @@ impl IOCompositor { }], ); self.send_scroll_positions_to_layout_for_pipeline(&pipeline_id); - self.waiting_for_results_of_scroll = true } + self.pending_frames += 1; transaction.generate_frame(0, RenderReasons::APZ); + self.webrender_api .send_transaction(self.webrender_document, transaction); } @@ -1954,7 +1971,6 @@ impl IOCompositor { self.composition_request = CompositionRequest::NoCompositingNecessary; self.process_animations(); - self.waiting_for_results_of_scroll = false; Ok(rv) } @@ -2037,8 +2053,8 @@ impl IOCompositor { let mut found_recomposite_msg = false; while let Some(msg) = self.port.try_recv_compositor_msg() { match msg { - CompositorMsg::Recomposite(_) if found_recomposite_msg => {}, - CompositorMsg::Recomposite(_) => { + CompositorMsg::NewWebRenderFrameReady(..) if found_recomposite_msg => {}, + CompositorMsg::NewWebRenderFrameReady(..) => { found_recomposite_msg = true; compositor_messages.push(msg) }, @@ -2078,7 +2094,7 @@ impl IOCompositor { // The WebXR thread may make a different context current let _ = self.rendering_context.make_gl_context_current(); - if !self.pending_scroll_zoom_events.is_empty() && !self.waiting_for_results_of_scroll { + if !self.pending_scroll_zoom_events.is_empty() { self.process_pending_scroll_events() } self.shutdown_state != ShutdownState::FinishedShuttingDown @@ -2092,7 +2108,7 @@ impl IOCompositor { while self.shutdown_state != ShutdownState::ShuttingDown { let msg = self.port.recv_compositor_msg(); let need_recomposite = match msg { - CompositorMsg::Recomposite(_) => true, + CompositorMsg::NewWebRenderFrameReady(..) => true, _ => false, }; let keep_going = self.handle_browser_message(msg); @@ -2137,7 +2153,11 @@ impl IOCompositor { self.webrender.set_debug_flags(flags); let mut txn = Transaction::new(); + + + self.pending_frames += 1; txn.generate_frame(0, RenderReasons::TESTING); + self.webrender_api .send_transaction(self.webrender_document, txn); } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index dcd8ab9b40e68..b5110b5d60fe5 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -33,7 +33,7 @@ use canvas_traits::webgl::WebGLThreads; use compositing::windowing::{EmbedderEvent, EmbedderMethods, WindowMethods}; use compositing::{CompositeTarget, IOCompositor, InitialCompositorState, ShutdownState}; use compositing_traits::{ - CanvasToCompositorMsg, CompositingReason, CompositorMsg, CompositorProxy, CompositorReceiver, + CanvasToCompositorMsg, CompositorMsg, CompositorProxy, CompositorReceiver, ConstellationMsg, FontToCompositorMsg, ForwardedToCompositorMsg, }; #[cfg(all( @@ -198,26 +198,17 @@ impl webrender_api::RenderNotifier for RenderNotifier { Box::new(RenderNotifier::new(self.compositor_proxy.clone())) } - fn wake_up(&self, composite_needed: bool) { - if composite_needed { - self.compositor_proxy - .recomposite(CompositingReason::NewWebRenderFrame); - } - } + fn wake_up(&self, _composite_needed: bool) { } fn new_frame_ready( &self, _document_id: DocumentId, - scrolled: bool, + _scrolled: bool, composite_needed: bool, _frame_publish_id: FramePublishId, ) { - if scrolled { - self.compositor_proxy - .send(CompositorMsg::NewScrollFrameReady(composite_needed)); - } else { - self.wake_up(true); - } + self.compositor_proxy + .send(CompositorMsg::NewWebRenderFrameReady(composite_needed)); } } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index b38103444399b..ca5e5443be487 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -27,33 +27,6 @@ use style_traits::CSSPixel; use webrender_api::units::{DeviceIntPoint, DeviceIntSize}; use webrender_api::{self, FontInstanceKey, FontKey, ImageKey}; -/// Why we performed a composite. This is used for debugging. -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CompositingReason { - /// We hit the delayed composition timeout. (See `delayed_composition.rs`.) - DelayedCompositeTimeout, - /// The window has been scrolled and we're starting the first recomposite. - Scroll, - /// A scroll has continued and we need to recomposite again. - ContinueScroll, - /// We're performing the single composite in headless mode. - Headless, - /// We're performing a composite to run an animation. - Animation, - /// A new frame tree has been loaded. - NewFrameTree, - /// New painted buffers have been received. - NewPaintedBuffers, - /// The window has been zoomed. - Zoom, - /// A new WebRender frame has arrived. - NewWebRenderFrame, - /// WebRender has processed a scroll event and has generated a new frame. - NewWebRenderScrollFrame, - /// The window has been resized and will need to be synchronously repainted. - Resize, -} - /// Sends messages to the compositor. pub struct CompositorProxy { pub sender: Sender, @@ -92,12 +65,6 @@ impl CompositorReceiver { } } -impl CompositorProxy { - pub fn recomposite(&self, reason: CompositingReason) { - self.send(CompositorMsg::Recomposite(reason)); - } -} - /// Messages from (or via) the constellation thread to the compositor. pub enum CompositorMsg { /// Informs the compositor that the constellation has completed shutdown. @@ -108,8 +75,6 @@ pub enum CompositorMsg { ChangeRunningAnimationsState(PipelineId, AnimationState), /// Replaces the current frame tree, typically called during main frame navigation. SetFrameTree(SendableFrameTree), - /// Composite. - Recomposite(CompositingReason), /// Script has handled a touch event, and either prevented or allowed default actions. TouchEventProcessed(EventResult), /// Composite to a PNG file and return the Image over a passed channel. @@ -118,16 +83,16 @@ pub enum CompositorMsg { IsReadyToSaveImageReply(bool), /// Pipeline visibility changed PipelineVisibilityChanged(PipelineId, bool), - /// WebRender has successfully processed a scroll. The boolean specifies whether a composite is - /// needed. - NewScrollFrameReady(bool), + /// WebRender has produced a new frame. This message informs the compositor that + /// the frame is ready, so that it may trigger a recomposite. + NewWebRenderFrameReady(bool /* composite_needed */), /// A pipeline was shut down. // This message acts as a synchronization point between the constellation, // when it shuts down a pipeline, to the compositor; when the compositor // sends a reply on the IpcSender, the constellation knows it's safe to // tear down the other threads associated with this pipeline. PipelineExited(PipelineId, IpcSender<()>), - /// Runs a closure in the compositor thread. + /// Runs a closure in, bool /* scrolled */ the compositor thread. /// It's used to dispatch functions from webrender to the main thread's event loop. /// Required to allow WGL GLContext sharing in Windows. Dispatch(Box), @@ -193,13 +158,12 @@ impl Debug for CompositorMsg { write!(f, "ChangeRunningAnimationsState({:?})", state) }, CompositorMsg::SetFrameTree(..) => write!(f, "SetFrameTree"), - CompositorMsg::Recomposite(..) => write!(f, "Recomposite"), CompositorMsg::TouchEventProcessed(..) => write!(f, "TouchEventProcessed"), CompositorMsg::CreatePng(..) => write!(f, "CreatePng"), CompositorMsg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"), CompositorMsg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"), CompositorMsg::PipelineExited(..) => write!(f, "PipelineExited"), - CompositorMsg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"), + CompositorMsg::NewWebRenderFrameReady(..) => write!(f, "NewWebRenderFrameReady"), CompositorMsg::Dispatch(..) => write!(f, "Dispatch"), CompositorMsg::PendingPaintMetric(..) => write!(f, "PendingPaintMetric"), CompositorMsg::LoadComplete(..) => write!(f, "LoadComplete"),