Integrate profiling into gpui (#8176)

[Profiling](https://crates.io/crates/profiling) crate allows easy
integration with various profiler tools. The best thing is - annotations
compile to nothing unless you request a specific feature.

For example, I used this command to enable Tracy support:
```bash
cargo run --features profiling/profile-with-tracy
```
At the same time I had Tracy tool open and waiting for connection. It
gathered nice stats from the run:

![zed-profiler](https://github.com/zed-industries/zed/assets/107301/5233045d-078c-4ad8-8b00-7ae55cf94ebb)


Release Notes:
- N/A
This commit is contained in:
Dzmitry Malyshau 2024-02-22 10:59:52 -08:00 committed by GitHub
parent 250df707bf
commit 991c9ec441
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 45 additions and 2 deletions

21
Cargo.lock generated
View File

@ -4121,6 +4121,7 @@ dependencies = [
"pathfinder_geometry", "pathfinder_geometry",
"png", "png",
"postage", "postage",
"profiling",
"rand 0.8.5", "rand 0.8.5",
"raw-window-handle 0.5.2", "raw-window-handle 0.5.2",
"raw-window-handle 0.6.0", "raw-window-handle 0.6.0",
@ -6863,6 +6864,25 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "profiling"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58"
dependencies = [
"profiling-procmacros",
]
[[package]]
name = "profiling-procmacros"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd"
dependencies = [
"quote",
"syn 2.0.48",
]
[[package]] [[package]]
name = "project" name = "project"
version = "0.1.0" version = "0.1.0"
@ -11978,6 +11998,7 @@ dependencies = [
"outline", "outline",
"parking_lot 0.11.2", "parking_lot 0.11.2",
"postage", "postage",
"profiling",
"project", "project",
"project_panel", "project_panel",
"project_symbols", "project_symbols",

View File

@ -203,6 +203,7 @@ linkify = "0.10.0"
log = { version = "0.4.16", features = ["kv_unstable_serde"] } log = { version = "0.4.16", features = ["kv_unstable_serde"] }
ordered-float = "2.1.1" ordered-float = "2.1.1"
parking_lot = "0.11.1" parking_lot = "0.11.1"
profiling = "1"
postage = { version = "0.5", features = ["futures-traits"] } postage = { version = "0.5", features = ["futures-traits"] }
pretty_assertions = "1.3.0" pretty_assertions = "1.3.0"
prost = "0.8" prost = "0.8"

View File

@ -51,6 +51,7 @@ parking = "2.0.0"
parking_lot.workspace = true parking_lot.workspace = true
pathfinder_geometry = "0.5" pathfinder_geometry = "0.5"
postage.workspace = true postage.workspace = true
profiling.workspace = true
rand.workspace = true rand.workspace = true
raw-window-handle = "0.6" raw-window-handle = "0.6"
refineable.workspace = true refineable.workspace = true

View File

@ -117,6 +117,7 @@ impl PlatformAtlas for BladeAtlas {
if let Some(tile) = lock.tiles_by_key.get(key) { if let Some(tile) = lock.tiles_by_key.get(key) {
Ok(tile.clone()) Ok(tile.clone())
} else { } else {
profiling::scope!("new tile");
let (size, bytes) = build()?; let (size, bytes) = build()?;
let tile = lock.allocate(size, key.texture_kind()); let tile = lock.allocate(size, key.texture_kind());
lock.upload_texture(tile.texture_id, tile.bounds, &bytes); lock.upload_texture(tile.texture_id, tile.bounds, &bytes);

View File

@ -39,6 +39,7 @@ impl BladeBelt {
} }
} }
#[profiling::function]
pub fn alloc(&mut self, size: u64, gpu: &gpu::Context) -> gpu::BufferPiece { pub fn alloc(&mut self, size: u64, gpu: &gpu::Context) -> gpu::BufferPiece {
for &mut (ref rb, ref mut offset) in self.active.iter_mut() { for &mut (ref rb, ref mut offset) in self.active.iter_mut() {
let aligned = offset.next_multiple_of(self.desc.alignment); let aligned = offset.next_multiple_of(self.desc.alignment);

View File

@ -444,6 +444,7 @@ impl BladeRenderer {
self.gpu.metal_layer().unwrap().as_ptr() self.gpu.metal_layer().unwrap().as_ptr()
} }
#[profiling::function]
fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) { fn rasterize_paths(&mut self, paths: &[Path<ScaledPixels>]) {
self.path_tiles.clear(); self.path_tiles.clear();
let mut vertices_by_texture_id = HashMap::default(); let mut vertices_by_texture_id = HashMap::default();
@ -506,7 +507,10 @@ impl BladeRenderer {
} }
pub fn draw(&mut self, scene: &Scene) { pub fn draw(&mut self, scene: &Scene) {
let frame = self.gpu.acquire_frame(); let frame = {
profiling::scope!("acquire frame");
self.gpu.acquire_frame()
};
self.command_encoder.start(); self.command_encoder.start();
self.command_encoder.init_texture(frame.texture()); self.command_encoder.init_texture(frame.texture());
@ -529,6 +533,7 @@ impl BladeRenderer {
}], }],
depth_stencil: None, depth_stencil: None,
}) { }) {
profiling::scope!("render pass");
for batch in scene.batches() { for batch in scene.batches() {
match batch { match batch {
PrimitiveBatch::Quads(quads) => { PrimitiveBatch::Quads(quads) => {
@ -718,6 +723,7 @@ impl BladeRenderer {
self.command_encoder.present(frame); self.command_encoder.present(frame);
let sync_point = self.gpu.submit(&mut self.command_encoder); let sync_point = self.gpu.submit(&mut self.command_encoder);
profiling::scope!("finish");
self.instance_belt.flush(&sync_point); self.instance_belt.flush(&sync_point);
self.atlas.after_frame(&sync_point); self.atlas.after_frame(&sync_point);
self.atlas.clear_textures(AtlasTextureKind::Path); self.atlas.clear_textures(AtlasTextureKind::Path);

View File

@ -33,6 +33,7 @@ impl LinuxDispatcher {
) -> Self { ) -> Self {
let (background_sender, background_receiver) = flume::unbounded::<Runnable>(); let (background_sender, background_receiver) = flume::unbounded::<Runnable>();
let background_thread = thread::spawn(move || { let background_thread = thread::spawn(move || {
profiling::register_thread!("background");
for runnable in background_receiver { for runnable in background_receiver {
let _ignore_panic = panic::catch_unwind(|| runnable.run()); let _ignore_panic = panic::catch_unwind(|| runnable.run());
} }

View File

@ -71,7 +71,10 @@ impl Client for X11Client {
// into window functions as they may invoke callbacks that need // into window functions as they may invoke callbacks that need
// to immediately access the platform (self). // to immediately access the platform (self).
while !self.platform_inner.state.lock().quit_requested { while !self.platform_inner.state.lock().quit_requested {
let event = self.xcb_connection.wait_for_event().unwrap(); let event = {
profiling::scope!("Wait for event");
self.xcb_connection.wait_for_event().unwrap()
};
match event { match event {
xcb::Event::X(x::Event::ClientMessage(ev)) => { xcb::Event::X(x::Event::ClientMessage(ev)) => {
if let x::ClientMessageData::Data32([atom, ..]) = ev.data() { if let x::ClientMessageData::Data32([atom, ..]) = ev.data() {
@ -210,6 +213,7 @@ impl Client for X11Client {
_ => {} _ => {}
} }
profiling::scope!("Runnables");
if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() { if let Ok(runnable) = self.platform_inner.main_receiver.try_recv() {
runnable.run(); runnable.run();
} }
@ -219,6 +223,7 @@ impl Client for X11Client {
fun(); fun();
} }
} }
fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> { fn displays(&self) -> Vec<Rc<dyn PlatformDisplay>> {
let setup = self.xcb_connection.get_setup(); let setup = self.xcb_connection.get_setup();
setup setup
@ -230,6 +235,7 @@ impl Client for X11Client {
}) })
.collect() .collect()
} }
fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> { fn display(&self, id: DisplayId) -> Option<Rc<dyn PlatformDisplay>> {
Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32))) Some(Rc::new(X11Display::new(&self.xcb_connection, id.0 as i32)))
} }

View File

@ -950,6 +950,7 @@ impl<'a> WindowContext<'a> {
/// Produces a new frame and assigns it to `rendered_frame`. To actually show /// Produces a new frame and assigns it to `rendered_frame`. To actually show
/// the contents of the new [Scene], use [present]. /// the contents of the new [Scene], use [present].
#[profiling::function]
pub fn draw(&mut self) { pub fn draw(&mut self) {
self.window.dirty.set(false); self.window.dirty.set(false);
self.window.drawing = true; self.window.drawing = true;
@ -1092,11 +1093,13 @@ impl<'a> WindowContext<'a> {
self.window.needs_present.set(true); self.window.needs_present.set(true);
} }
#[profiling::function]
fn present(&self) { fn present(&self) {
self.window self.window
.platform_window .platform_window
.draw(&self.window.rendered_frame.scene); .draw(&self.window.rendered_frame.scene);
self.window.needs_present.set(false); self.window.needs_present.set(false);
profiling::finish_frame!();
} }
/// Dispatch a given keystroke as though the user had typed it. /// Dispatch a given keystroke as though the user had typed it.
@ -1132,6 +1135,7 @@ impl<'a> WindowContext<'a> {
} }
/// Dispatch a mouse or keyboard event on the window. /// Dispatch a mouse or keyboard event on the window.
#[profiling::function]
pub fn dispatch_event(&mut self, event: PlatformInput) -> bool { pub fn dispatch_event(&mut self, event: PlatformInput) -> bool {
self.window.last_input_timestamp.set(Instant::now()); self.window.last_input_timestamp.set(Instant::now());
// Handlers may set this to false by calling `stop_propagation`. // Handlers may set this to false by calling `stop_propagation`.

View File

@ -77,6 +77,7 @@ num_cpus = "1.13.0"
outline.workspace = true outline.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
postage.workspace = true postage.workspace = true
profiling.workspace = true
project.workspace = true project.workspace = true
project_panel.workspace = true project_panel.workspace = true
project_symbols.workspace = true project_symbols.workspace = true