diff --git a/Cargo.lock b/Cargo.lock index f81c1ce28..1057e8366 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1175,6 +1175,7 @@ dependencies = [ "document-features", "egui", "pollster", + "puffin", "tracing", "type-map", "wgpu", diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 93319e00e..04fdf2afd 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -47,7 +47,7 @@ persistence = [ ## ## Only enabled on native, because of the low resolution (1ms) of time keeping in browsers. ## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you -puffin = ["dep:puffin", "egui_glow?/puffin"] +puffin = ["dep:puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"] ## Enable screen reader support (requires `ctx.options().screen_reader = true;`) screen_reader = ["egui-winit/screen_reader", "tts"] diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index a450ca57e..dad6ce7aa 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -3,9 +3,9 @@ name = "egui-wgpu" version = "0.19.0" description = "Bindings for using egui natively using the wgpu library" authors = [ - "Nils Hasenbanck ", - "embotech ", - "Emil Ernerfeldt ", + "Nils Hasenbanck ", + "embotech ", + "Emil Ernerfeldt ", ] edition = "2021" rust-version = "1.62" @@ -28,13 +28,16 @@ all-features = true [features] +## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. +puffin = ["dep:puffin"] + ## Enable [`winit`](https://docs.rs/winit) integration. winit = ["dep:pollster", "dep:winit"] [dependencies] egui = { version = "0.19.0", path = "../egui", default-features = false, features = [ - "bytemuck", + "bytemuck", ] } bytemuck = "1.7" @@ -48,3 +51,7 @@ document-features = { version = "0.2", optional = true } pollster = { version = "0.2", optional = true } winit = { version = "0.27.2", optional = true } + +# Native: +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +puffin = { version = "0.14", optional = true } diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index d88b78e7d..d4381fe1b 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -95,3 +95,25 @@ pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::Te } formats[0] // take the first } + +// --------------------------------------------------------------------------- + +/// Profiling macro for feature "puffin" +macro_rules! profile_function { + ($($arg: tt)*) => { + #[cfg(feature = "puffin")] + #[cfg(not(target_arch = "wasm32"))] + puffin::profile_function!($($arg)*); + }; +} +pub(crate) use profile_function; + +/// Profiling macro for feature "puffin" +macro_rules! profile_scope { + ($($arg: tt)*) => { + #[cfg(feature = "puffin")] + #[cfg(not(target_arch = "wasm32"))] + puffin::profile_scope!($($arg)*); + }; +} +pub(crate) use profile_scope; diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index d56ff483f..d7112b291 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -174,6 +174,8 @@ impl Renderer { output_depth_format: Option, msaa_samples: u32, ) -> Self { + crate::profile_function!(); + let shader = wgpu::ShaderModuleDescriptor { label: Some("egui"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))), @@ -347,6 +349,8 @@ impl Renderer { paint_jobs: &[egui::epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) { + crate::profile_function!(); + let pixels_per_point = screen_descriptor.pixels_per_point; let size_in_pixels = screen_descriptor.size_in_pixels; @@ -421,6 +425,8 @@ impl Renderer { }; if callback.rect.is_positive() { + crate::profile_scope!("callback"); + needs_reset = true; { @@ -472,6 +478,8 @@ impl Renderer { id: egui::TextureId, image_delta: &egui::epaint::ImageDelta, ) { + crate::profile_function!(); + let width = image_delta.image.width() as u32; let height = image_delta.image.height() as u32; @@ -653,6 +661,8 @@ impl Renderer { texture: &wgpu::TextureView, sampler_descriptor: wgpu::SamplerDescriptor<'_>, ) -> egui::TextureId { + crate::profile_function!(); + let sampler = device.create_sampler(&wgpu::SamplerDescriptor { compare: None, ..sampler_descriptor @@ -692,6 +702,8 @@ impl Renderer { sampler_descriptor: wgpu::SamplerDescriptor<'_>, id: egui::TextureId, ) { + crate::profile_function!(); + let (_user_texture, user_texture_binding) = self .textures .get_mut(&id) @@ -732,20 +744,26 @@ impl Renderer { paint_jobs: &[egui::epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) -> Vec { + crate::profile_function!(); + let screen_size_in_points = screen_descriptor.screen_size_in_points(); - // Update uniform buffer - queue.write_buffer( - &self.uniform_buffer, - 0, - bytemuck::cast_slice(&[UniformBuffer { - screen_size_in_points, - _padding: Default::default(), - }]), - ); + { + crate::profile_scope!("uniforms"); + // Update uniform buffer + queue.write_buffer( + &self.uniform_buffer, + 0, + bytemuck::cast_slice(&[UniformBuffer { + screen_size_in_points, + _padding: Default::default(), + }]), + ); + } // Determine how many vertices & indices need to be rendered. - let (vertex_count, index_count) = + let (vertex_count, index_count) = { + crate::profile_scope!("count_vertices_indices"); paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| { match &clipped_primitive.primitive { Primitive::Mesh(mesh) => { @@ -753,9 +771,11 @@ impl Renderer { } Primitive::Callback(_) => acc, } - }); - // Resize index buffer if needed. + }) + }; + { + // Resize index buffer if needed: self.index_buffer.slices.clear(); let required_size = (std::mem::size_of::() * index_count) as u64; if self.index_buffer.capacity < required_size { @@ -764,8 +784,9 @@ impl Renderer { self.index_buffer.buffer = create_index_buffer(device, self.index_buffer.capacity); } } - // Resize vertex buffer if needed. + { + // Resize vertex buffer if needed: self.vertex_buffer.slices.clear(); let required_size = (std::mem::size_of::() * vertex_count) as u64; if self.vertex_buffer.capacity < required_size { @@ -778,6 +799,8 @@ impl Renderer { // Upload index & vertex data and call user callbacks let mut user_cmd_bufs = Vec::new(); // collect user command buffers + + crate::profile_scope!("primitives"); for egui::ClippedPrimitive { primitive, .. } in paint_jobs.iter() { match primitive { Primitive::Mesh(mesh) => { @@ -806,6 +829,7 @@ impl Renderer { continue; }; + crate::profile_scope!("callback"); user_cmd_bufs.extend((cbfn.prepare)( device, queue, @@ -841,6 +865,7 @@ fn create_sampler(options: egui::TextureOptions, device: &wgpu::Device) -> wgpu: } fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { + crate::profile_function!(); device.create_buffer(&wgpu::BufferDescriptor { label: Some("egui_vertex_buffer"), usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, @@ -850,6 +875,7 @@ fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { } fn create_index_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { + crate::profile_function!(); device.create_buffer(&wgpu::BufferDescriptor { label: Some("egui_index_buffer"), usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index d37c7c3b6..8eb788dcf 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -109,6 +109,8 @@ impl Painter { } fn configure_surface(&mut self, width_in_pixels: u32, height_in_pixels: u32) { + crate::profile_function!(); + let render_state = self .render_state .as_ref() @@ -227,6 +229,8 @@ impl Painter { clipped_primitives: &[egui::ClippedPrimitive], textures_delta: &egui::TexturesDelta, ) { + crate::profile_function!(); + let render_state = match self.render_state.as_mut() { Some(rs) => rs, None => return, @@ -237,7 +241,13 @@ impl Painter { }; let (width, height) = (surface_state.width, surface_state.height); - let output_frame = match surface_state.surface.get_current_texture() { + let output_frame = { + crate::profile_scope!("get_current_texture"); + // This is what vsync-waiting happens, at least on Mac. + surface_state.surface.get_current_texture() + }; + + let output_frame = match output_frame { Ok(frame) => frame, #[allow(clippy::single_match_else)] Err(e) => match (*self.configuration.on_surface_error)(e) { @@ -326,15 +336,24 @@ impl Painter { } } + let encoded = { + crate::profile_scope!("CommandEncoder::finish"); + encoder.finish() + }; + // Submit the commands: both the main buffer and user-defined ones. - render_state.queue.submit( - user_cmd_bufs - .into_iter() - .chain(std::iter::once(encoder.finish())), - ); + { + crate::profile_scope!("Queue::submit"); + render_state + .queue + .submit(user_cmd_bufs.into_iter().chain(std::iter::once(encoded))); + }; // Redraw egui - output_frame.present(); + { + crate::profile_scope!("present"); + output_frame.present(); + } } #[allow(clippy::unused_self)]