From 5311beb34ad49dc51576ef89e67d7fe0b04221e0 Mon Sep 17 00:00:00 2001 From: sagudev <16504129+sagudev@users.noreply.github.com> Date: Sat, 28 Jun 2025 19:29:11 +0200 Subject: [PATCH] Lazily initialize canvas paint thread in constellation (#37765) This PR moves canvas paint thread initialization to constellation. This allows us to lazily initialize it on first create canvas request (like we do for webgpu). If we didn't started canvas paint thread we also do not need to wait for it's teardown. Per https://chromestatus.com/metrics/webfeature/timeline/popularity/201 ~30% of websites still use 2d canvas. Testing: Existing WPT tests Signed-off-by: sagudev <16504129+sagudev@users.noreply.github.com> --- Cargo.lock | 2 +- components/constellation/Cargo.toml | 1 + components/constellation/constellation.rs | 51 ++++++++++++++--------- components/servo/Cargo.toml | 1 - components/servo/lib.rs | 11 +---- 5 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b5d8de6c27..dcf80f1c9cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1454,6 +1454,7 @@ dependencies = [ "backtrace", "base", "bluetooth_traits", + "canvas", "canvas_traits", "compositing_traits", "constellation_traits", @@ -4846,7 +4847,6 @@ dependencies = [ "bincode", "bluetooth", "bluetooth_traits", - "canvas", "canvas_traits", "compositing", "compositing_traits", diff --git a/components/constellation/Cargo.toml b/components/constellation/Cargo.toml index c1a0249828a..634166d1050 100644 --- a/components/constellation/Cargo.toml +++ b/components/constellation/Cargo.toml @@ -23,6 +23,7 @@ background_hang_monitor_api = { workspace = true } backtrace = { workspace = true } base = { workspace = true } bluetooth_traits = { workspace = true, optional = true } +canvas = { path = "../canvas" } canvas_traits = { workspace = true } compositing_traits = { workspace = true } constellation_traits = { workspace = true } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 29d3c656b53..616fb10b288 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -85,6 +85,7 @@ //! See use std::borrow::ToOwned; +use std::cell::OnceCell; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet, VecDeque}; use std::marker::PhantomData; @@ -105,6 +106,7 @@ use base::id::{ }; #[cfg(feature = "bluetooth")] use bluetooth_traits::BluetoothRequest; +use canvas::canvas_paint_thread::CanvasPaintThread; use canvas_traits::ConstellationCanvasMsg; use canvas_traits::canvas::{CanvasId, CanvasMsg}; use canvas_traits::webgl::WebGLThreads; @@ -425,10 +427,8 @@ pub struct Constellation { /// The XR device registry webxr_registry: Option, - /// A channel through which messages can be sent to the canvas paint thread. - canvas_sender: Sender, - - canvas_ipc_sender: IpcSender, + /// Lazily initialized channels for canvas paint thread. + canvas: OnceCell<(Sender, IpcSender)>, /// Navigation requests from script awaiting approval from the embedder. pending_approval_navigations: PendingApprovalNavigations, @@ -584,8 +584,6 @@ where random_pipeline_closure_probability: Option, random_pipeline_closure_seed: Option, hard_fail: bool, - canvas_create_sender: Sender, - canvas_ipc_sender: IpcSender, ) -> Sender { let (compositor_sender, compositor_receiver) = unbounded(); @@ -707,8 +705,7 @@ where }), webgl_threads: state.webgl_threads, webxr_registry: state.webxr_registry, - canvas_sender: canvas_create_sender, - canvas_ipc_sender, + canvas: OnceCell::new(), pending_approval_navigations: HashMap::new(), pressed_mouse_buttons: 0, active_keyboard_modifiers: Modifiers::empty(), @@ -2665,14 +2662,16 @@ where } } - debug!("Exiting Canvas Paint thread."); - let (canvas_exit_sender, canvas_exit_receiver) = unbounded(); - if let Err(e) = self - .canvas_sender - .send(ConstellationCanvasMsg::Exit(canvas_exit_sender)) - { - warn!("Exit Canvas Paint thread failed ({})", e); - } + let canvas_exit_receiver = if let Some((canvas_sender, _)) = self.canvas.get() { + debug!("Exiting Canvas Paint thread."); + let (canvas_exit_sender, canvas_exit_receiver) = unbounded(); + if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Exit(canvas_exit_sender)) { + warn!("Exit Canvas Paint thread failed ({})", e); + } + Some(canvas_exit_receiver) + } else { + None + }; debug!("Exiting WebGPU threads."); #[cfg(feature = "webgpu")] @@ -2714,7 +2713,9 @@ where // Wait for the canvas thread to exit before shutting down the font service, as // canvas might still be using the system font service before shutting down. - let _ = canvas_exit_receiver.recv(); + if let Some(canvas_exit_receiver) = canvas_exit_receiver { + let _ = canvas_exit_receiver.recv(); + } debug!("Exiting the system font service thread."); self.system_font_service.exit(); @@ -4493,8 +4494,11 @@ where response_sender: IpcSender<(IpcSender, CanvasId, ImageKey)>, ) { let (canvas_data_sender, canvas_data_receiver) = unbounded(); + let (canvas_sender, canvas_ipc_sender) = self + .canvas + .get_or_init(|| self.create_canvas_paint_thread()); - if let Err(e) = self.canvas_sender.send(ConstellationCanvasMsg::Create { + if let Err(e) = canvas_sender.send(ConstellationCanvasMsg::Create { sender: canvas_data_sender, size, }) { @@ -4504,8 +4508,7 @@ where Ok(canvas_data) => canvas_data, Err(e) => return warn!("Create canvas paint thread id response failed ({})", e), }; - if let Err(e) = response_sender.send((self.canvas_ipc_sender.clone(), canvas_id, image_key)) - { + if let Err(e) = response_sender.send((canvas_ipc_sender.clone(), canvas_id, image_key)) { warn!("Create canvas paint thread response failed ({})", e); } } @@ -5745,4 +5748,12 @@ where warn!("Could not sent paint metric event to pipeline: {pipeline_id:?}: {error:?}"); } } + + fn create_canvas_paint_thread(&self) -> (Sender, IpcSender) { + CanvasPaintThread::start( + self.compositor_proxy.cross_process_compositor_api.clone(), + self.system_font_service.clone(), + self.public_resource_threads.clone(), + ) + } } diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index db9864ff9b0..ab7b977ad14 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -69,7 +69,6 @@ base = { workspace = true } bincode = { workspace = true } bluetooth = { path = "../bluetooth", optional = true } bluetooth_traits = { workspace = true, optional = true } -canvas = { path = "../canvas" } webgl = { path = "../webgl", default-features = false } canvas_traits = { workspace = true } compositing = { path = "../compositing" } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index d26777a38d5..3b9be5bc011 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -39,7 +39,6 @@ use base::id::{PipelineNamespace, PipelineNamespaceId}; use bluetooth::BluetoothThreadFactory; #[cfg(feature = "bluetooth")] use bluetooth_traits::BluetoothRequest; -use canvas::canvas_paint_thread::CanvasPaintThread; use canvas_traits::webgl::{GlType, WebGLThreads}; use clipboard_delegate::StringRequest; pub use compositing::WebRenderDebugOption; @@ -115,7 +114,7 @@ use webview::WebViewInner; #[cfg(feature = "webxr")] pub use webxr; pub use { - background_hang_monitor, base, canvas, canvas_traits, devtools, devtools_traits, euclid, fonts, + background_hang_monitor, base, canvas_traits, devtools, devtools_traits, euclid, fonts, ipc_channel, layout_api, media, net, net_traits, profile, profile_traits, script, script_traits, servo_config as config, servo_config, servo_geometry, servo_url, style, style_traits, webrender_api, @@ -1095,12 +1094,6 @@ fn create_constellation( .to_proxy(), ); - let (canvas_create_sender, canvas_ipc_sender) = CanvasPaintThread::start( - compositor_proxy.cross_process_compositor_api.clone(), - system_font_service.clone(), - public_resource_threads.clone(), - ); - let initial_state = InitialConstellationState { compositor_proxy, embedder_proxy, @@ -1133,8 +1126,6 @@ fn create_constellation( opts.random_pipeline_closure_probability, opts.random_pipeline_closure_seed, opts.hard_fail, - canvas_create_sender, - canvas_ipc_sender, ) }