aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2025-04-16 09:14:04 -0400
committerGitHub <noreply@github.com>2025-04-16 13:14:04 +0000
commitaf000d6c91f0958d6252b8e474cf596799001ae9 (patch)
tree7a5ab2e6f78a8c4c8be82a7462173b36438da688
parentafe98e9e1e6d42542a33a3e92a7838f03099cf0e (diff)
downloadservo-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.lock4
-rw-r--r--components/compositing/Cargo.toml2
-rw-r--r--components/compositing/compositor.rs37
-rw-r--r--components/compositing/tracing.rs1
-rw-r--r--components/servo/Cargo.toml1
-rw-r--r--components/servo/lib.rs1
-rw-r--r--components/shared/compositing/Cargo.toml1
-rw-r--r--components/shared/compositing/lib.rs11
-rw-r--r--components/shared/profile/mem.rs48
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();
}
}