diff options
author | Josh Matthews <josh@joshmatthews.net> | 2025-04-16 09:14:04 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-04-16 13:14:04 +0000 |
commit | af000d6c91f0958d6252b8e474cf596799001ae9 (patch) | |
tree | 7a5ab2e6f78a8c4c8be82a7462173b36438da688 | |
parent | afe98e9e1e6d42542a33a3e92a7838f03099cf0e (diff) | |
download | servo-af000d6c91f0958d6252b8e474cf596799001ae9.tar.gz servo-af000d6c91f0958d6252b8e474cf596799001ae9.zip |
compositing: Add memory reporter for WebRender. (#36557)
This adds a memory reporter for WebRender's memory usage. I seeded it
with a couple entries that looked reasonable based on
https://searchfox.org/mozilla-central/rev/2c71f1e9b5947612abdc16b64008162c58c1b9d3/gfx/thebes/gfxPlatform.cpp#722-738.
Testing: Verified that new numbers appear in about:memory for servo.org.
The new images category is surprisingly large (40mb).
Signed-off-by: Josh Matthews <josh@joshmatthews.net>
-rw-r--r-- | Cargo.lock | 4 | ||||
-rw-r--r-- | components/compositing/Cargo.toml | 2 | ||||
-rw-r--r-- | components/compositing/compositor.rs | 37 | ||||
-rw-r--r-- | components/compositing/tracing.rs | 1 | ||||
-rw-r--r-- | components/servo/Cargo.toml | 1 | ||||
-rw-r--r-- | components/servo/lib.rs | 1 | ||||
-rw-r--r-- | components/shared/compositing/Cargo.toml | 1 | ||||
-rw-r--r-- | components/shared/compositing/lib.rs | 11 | ||||
-rw-r--r-- | components/shared/profile/mem.rs | 48 |
9 files changed, 98 insertions, 8 deletions
diff --git a/Cargo.lock b/Cargo.lock index f339a5a7736..29a9c388625 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1106,6 +1106,7 @@ dependencies = [ "pixels", "profile_traits", "script_traits", + "servo_allocator", "servo_config", "servo_geometry", "stylo_traits", @@ -1114,6 +1115,7 @@ dependencies = [ "webrender", "webrender_api", "webxr", + "wr_malloc_size_of", ] [[package]] @@ -1132,6 +1134,7 @@ dependencies = [ "ipc-channel", "log", "pixels", + "profile_traits", "raw-window-handle", "serde", "servo_geometry", @@ -4353,6 +4356,7 @@ dependencies = [ "servo-media", "servo-media-dummy", "servo-media-gstreamer", + "servo_allocator", "servo_config", "servo_geometry", "servo_url", diff --git a/components/compositing/Cargo.toml b/components/compositing/Cargo.toml index c665a9bc93d..8a670a4b4a7 100644 --- a/components/compositing/Cargo.toml +++ b/components/compositing/Cargo.toml @@ -35,6 +35,7 @@ net = { path = "../net" } pixels = { path = "../pixels" } profile_traits = { workspace = true } script_traits = { workspace = true } +servo_allocator = { path = "../allocator" } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } stylo_traits = { workspace = true } @@ -42,6 +43,7 @@ tracing = { workspace = true, optional = true } webrender = { workspace = true } webrender_api = { workspace = true } webxr = { path = "../webxr", optional = true } +wr_malloc_size_of = { workspace = true } [dev-dependencies] surfman = { workspace = true } diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 461a5a819d5..53debad39d6 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -34,8 +34,9 @@ use ipc_channel::ipc::{self, IpcSharedMemory}; use libc::c_void; use log::{debug, info, trace, warn}; use pixels::{CorsStatus, Image, ImageFrame, PixelFormat}; +use profile_traits::mem::{ProcessReports, ProfilerRegistration, Report, ReportKind}; use profile_traits::time::{self as profile_time, ProfilerCategory}; -use profile_traits::time_profile; +use profile_traits::{path, time_profile}; use servo_config::opts; use servo_geometry::DeviceIndependentPixel; use style_traits::CSSPixel; @@ -147,6 +148,10 @@ pub struct IOCompositor { /// The [`Instant`] of the last animation tick, used to avoid flooding the Constellation and /// ScriptThread with a deluge of animation ticks. last_animation_tick: Instant, + + /// A handle to the memory profiler which will automatically unregister + /// when it's dropped. + _mem_profiler_registration: ProfilerRegistration, } /// Why we need to be repainted. This is used for debugging. @@ -391,6 +396,11 @@ impl ServoRenderer { impl IOCompositor { pub fn new(state: InitialCompositorState, convert_mouse_to_touch: bool) -> Self { + let registration = state.mem_profiler_chan.prepare_memory_reporting( + "compositor".into(), + state.sender.clone(), + CompositorMsg::CollectMemoryReport, + ); let compositor = IOCompositor { global: Rc::new(RefCell::new(ServoRenderer { shutdown_state: state.shutdown_state, @@ -414,6 +424,7 @@ impl IOCompositor { rendering_context: state.rendering_context, pending_frames: 0, last_animation_tick: Instant::now(), + _mem_profiler_registration: registration, }; { @@ -496,6 +507,30 @@ impl IOCompositor { } match msg { + CompositorMsg::CollectMemoryReport(sender) => { + let ops = + wr_malloc_size_of::MallocSizeOfOps::new(servo_allocator::usable_size, None); + let report = self.global.borrow().webrender_api.report_memory(ops); + let reports = vec![ + Report { + path: path!["webrender", "fonts"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.fonts, + }, + Report { + path: path!["webrender", "images"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.images, + }, + Report { + path: path!["webrender", "display-list"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: report.display_list, + }, + ]; + sender.send(ProcessReports::new(reports)); + }, + CompositorMsg::ChangeRunningAnimationsState( webview_id, pipeline_id, diff --git a/components/compositing/tracing.rs b/components/compositing/tracing.rs index 0373d0f1d1a..ae7338106d0 100644 --- a/components/compositing/tracing.rs +++ b/components/compositing/tracing.rs @@ -57,6 +57,7 @@ mod from_constellation { Self::GetClientWindowRect(..) => target!("GetClientWindowRect"), Self::GetScreenSize(..) => target!("GetScreenSize"), Self::GetAvailableScreenSize(..) => target!("GetAvailableScreenSize"), + Self::CollectMemoryReport(..) => target!("CollectMemoryReport"), } } } diff --git a/components/servo/Cargo.toml b/components/servo/Cargo.toml index e4347d76af0..49669a0b376 100644 --- a/components/servo/Cargo.toml +++ b/components/servo/Cargo.toml @@ -96,6 +96,7 @@ serde = { workspace = true } servo-media = { workspace = true } servo-media-dummy = { workspace = true } servo-media-gstreamer = { workspace = true, optional = true } +servo_allocator = { path = "../allocator" } servo_config = { path = "../config" } servo_geometry = { path = "../geometry" } servo_url = { path = "../url" } diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 87b95330021..62ef9e85dde 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -373,6 +373,7 @@ impl Servo { clear_color, upload_method, workers, + size_of_op: Some(servo_allocator::usable_size), ..Default::default() }, None, diff --git a/components/shared/compositing/Cargo.toml b/components/shared/compositing/Cargo.toml index f2b19c3886c..2462d6b42e9 100644 --- a/components/shared/compositing/Cargo.toml +++ b/components/shared/compositing/Cargo.toml @@ -27,6 +27,7 @@ image = { workspace = true } ipc-channel = { workspace = true } log = { workspace = true } pixels = { path = '../../pixels' } +profile_traits = { path = '../profile' } raw-window-handle = { version = "0.6" } serde = { workspace = true } servo_geometry = { path = "../../geometry" } diff --git a/components/shared/compositing/lib.rs b/components/shared/compositing/lib.rs index 3a5e188c5d6..7c2bd669d02 100644 --- a/components/shared/compositing/lib.rs +++ b/components/shared/compositing/lib.rs @@ -29,6 +29,7 @@ use display_list::CompositorDisplayListInfo; use embedder_traits::{CompositorHitTestResult, ScreenGeometry}; use euclid::default::Size2D as UntypedSize2D; use ipc_channel::ipc::{self, IpcSharedMemory}; +use profile_traits::mem::{OpaqueSender, ReportsChan}; use serde::{Deserialize, Serialize}; use servo_geometry::{DeviceIndependentIntRect, DeviceIndependentIntSize}; use webrender_api::units::{DevicePoint, LayoutPoint, TexelRect}; @@ -50,6 +51,12 @@ pub struct CompositorProxy { pub event_loop_waker: Box<dyn EventLoopWaker>, } +impl OpaqueSender<CompositorMsg> for CompositorProxy { + fn send(&self, message: CompositorMsg) { + CompositorProxy::send(self, message) + } +} + impl CompositorProxy { pub fn send(&self, msg: CompositorMsg) { if let Err(err) = self.sender.send(msg) { @@ -154,6 +161,10 @@ pub enum CompositorMsg { /// Get the available screen size (without toolbars and docks) for the screen /// the client window inhabits. GetAvailableScreenSize(WebViewId, IpcSender<DeviceIndependentIntSize>), + + /// Measure the current memory usage associated with the compositor. + /// The report must be sent on the provided channel once it's complete. + CollectMemoryReport(ReportsChan), } impl Debug for CompositorMsg { diff --git a/components/shared/profile/mem.rs b/components/shared/profile/mem.rs index daeadc242c9..62fc30d9438 100644 --- a/components/shared/profile/mem.rs +++ b/components/shared/profile/mem.rs @@ -50,6 +50,21 @@ where #[derive(Clone, Debug, Deserialize, Serialize)] pub struct ProfilerChan(pub IpcSender<ProfilerMsg>); +/// A handle that encompasses a registration with the memory profiler. +/// The registration is tied to the lifetime of this type; the memory +/// profiler unregister the reporter when this object is dropped. +pub struct ProfilerRegistration { + sender: ProfilerChan, + reporter_name: String, +} + +impl Drop for ProfilerRegistration { + fn drop(&mut self) { + self.sender + .send(ProfilerMsg::UnregisterReporter(self.reporter_name.clone())); + } +} + impl ProfilerChan { /// Send `msg` on this `IpcSender`. /// @@ -60,15 +75,15 @@ impl ProfilerChan { } } - /// Runs `f()` with memory profiling. - pub fn run_with_memory_reporting<F, M, T, C>( + /// Register a new reporter and return a handle to automatically + /// unregister it in the future. + pub fn prepare_memory_reporting<M, T, C>( &self, - f: F, reporter_name: String, channel_for_reporter: C, msg: M, - ) where - F: FnOnce(), + ) -> ProfilerRegistration + where M: Fn(ReportsChan) -> T + Send + 'static, T: Send + 'static, C: OpaqueSender<T> + Send + 'static, @@ -88,9 +103,28 @@ impl ProfilerChan { Reporter(reporter_sender), )); - f(); + ProfilerRegistration { + sender: self.clone(), + reporter_name, + } + } + + /// Runs `f()` with memory profiling. + pub fn run_with_memory_reporting<F, M, T, C>( + &self, + f: F, + reporter_name: String, + channel_for_reporter: C, + msg: M, + ) where + F: FnOnce(), + M: Fn(ReportsChan) -> T + Send + 'static, + T: Send + 'static, + C: OpaqueSender<T> + Send + 'static, + { + let _registration = self.prepare_memory_reporting(reporter_name, channel_for_reporter, msg); - self.send(ProfilerMsg::UnregisterReporter(reporter_name)); + f(); } } |