diff --git a/Cargo.lock b/Cargo.lock index c2ca14a8..b83be68c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1179,6 +1179,7 @@ dependencies = [ "epaint", "log", "nohash-hasher", + "puffin", "ron", "serde", ] diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index fd4076ed..78290520 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -63,9 +63,9 @@ persistence = [ ## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. ## -## Only enabled on native, because of the low resolution (1ms) of time keeping in browsers. +## Only enabled on native, because of the low resolution (1ms) of clocks in browsers. ## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you -puffin = ["dep:puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"] +puffin = ["dep:puffin", "egui/puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"] ## Enable screen reader support (requires `ctx.options_mut(|o| o.screen_reader = true);`) on web. ## diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 30e20630..1496149a 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -34,7 +34,7 @@ clipboard = ["arboard", "smithay-clipboard"] links = ["webbrowser"] ## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -puffin = ["dep:puffin"] +puffin = ["dep:puffin", "egui/puffin"] ## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde). serde = ["egui/serde", "dep:serde"] diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index 61519583..2d184631 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -54,6 +54,11 @@ mint = ["epaint/mint"] ## Enable persistence of memory (window positions etc). persistence = ["serde", "epaint/serde", "ron"] +## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. +## +## Only enabled on native, because of the low resolution (1ms) of clocks in browsers. +puffin = ["dep:puffin"] + ## Allow serialization using [`serde`](https://docs.rs/serde). serde = ["dep:serde", "epaint/serde", "accesskit?/serde"] @@ -79,5 +84,6 @@ accesskit = { version = "0.11", optional = true } document-features = { version = "0.2", optional = true } log = { version = "0.4", optional = true, features = ["std"] } +puffin = { version = "0.16", optional = true } ron = { version = "0.8", optional = true } serde = { version = "1", optional = true, features = ["derive", "rc"] } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index c79e2683..231783fd 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -207,6 +207,7 @@ impl ContextImpl { #[cfg(feature = "accesskit")] if self.is_accesskit_enabled { + crate::profile_scope!("accesskit"); use crate::frame_state::AccessKitFrameState; let id = crate::accesskit_root_id(); let mut builder = accesskit::NodeBuilder::new(accesskit::Role::Window); @@ -224,24 +225,32 @@ impl ContextImpl { /// Load fonts unless already loaded. fn update_fonts_mut(&mut self) { + crate::profile_function!(); + let pixels_per_point = self.input.pixels_per_point(); let max_texture_side = self.input.max_texture_side; if let Some(font_definitions) = self.memory.new_font_definitions.take() { + crate::profile_scope!("Fonts::new"); let fonts = Fonts::new(pixels_per_point, max_texture_side, font_definitions); self.fonts = Some(fonts); } let fonts = self.fonts.get_or_insert_with(|| { let font_definitions = FontDefinitions::default(); + crate::profile_scope!("Fonts::new"); Fonts::new(pixels_per_point, max_texture_side, font_definitions) }); - fonts.begin_frame(pixels_per_point, max_texture_side); + { + crate::profile_scope!("Fonts::begin_frame"); + fonts.begin_frame(pixels_per_point, max_texture_side); + } if self.memory.options.preload_font_glyphs { + crate::profile_scope!("preload_font_glyphs"); // Preload the most common characters for the most common fonts. - // This is not very important to do, but may a few GPU operations. + // This is not very important to do, but may save a few GPU operations. for font_id in self.memory.options.style.text_styles.values() { fonts.lock().fonts.font(font_id).preload_common_characters(); } @@ -370,6 +379,7 @@ impl Context { /// ``` #[must_use] pub fn run(&self, new_input: RawInput, run_ui: impl FnOnce(&Context)) -> FullOutput { + crate::profile_function!(); self.begin_frame(new_input); run_ui(self); self.end_frame() @@ -393,6 +403,7 @@ impl Context { /// // handle full_output /// ``` pub fn begin_frame(&self, new_input: RawInput) { + crate::profile_function!(); self.write(|ctx| ctx.begin_frame_mut(new_input)); } } @@ -1207,6 +1218,7 @@ impl Context { /// Call at the end of each frame. #[must_use] pub fn end_frame(&self) -> FullOutput { + crate::profile_function!(); if self.input(|i| i.wants_repaint()) { self.request_repaint(); } @@ -1230,6 +1242,7 @@ impl Context { #[cfg(feature = "accesskit")] { + crate::profile_scope!("accesskit"); let state = self.frame_state_mut(|fs| fs.accesskit_state.take()); if let Some(state) = state { let has_focus = self.input(|i| i.raw.focused); @@ -1269,11 +1282,13 @@ impl Context { } fn drain_paint_lists(&self) -> Vec { + crate::profile_function!(); self.write(|ctx| ctx.graphics.drain(ctx.memory.areas.order()).collect()) } /// Tessellate the given shapes into triangle meshes. pub fn tessellate(&self, shapes: Vec) -> Vec { + crate::profile_function!(); // A tempting optimization is to reuse the tessellation from last frame if the // shapes are the same, but just comparing the shapes takes about 50% of the time // it takes to tessellate them, so it is not a worth optimization. @@ -1293,13 +1308,16 @@ impl Context { }; let paint_stats = PaintStats::from_shapes(&shapes); - let clipped_primitives = tessellator::tessellate_shapes( - pixels_per_point, - tessellation_options, - font_tex_size, - prepared_discs, - shapes, - ); + let clipped_primitives = { + crate::profile_scope!("tessellator::tessellate_shapes"); + tessellator::tessellate_shapes( + pixels_per_point, + tessellation_options, + font_tex_size, + prepared_discs, + shapes, + ) + }; ctx.paint_stats = paint_stats.with_clipped_primitives(&clipped_primitives); clipped_primitives }) diff --git a/crates/egui/src/frame_state.rs b/crates/egui/src/frame_state.rs index 287b35c5..f82b2e7e 100644 --- a/crates/egui/src/frame_state.rs +++ b/crates/egui/src/frame_state.rs @@ -76,6 +76,7 @@ impl Default for FrameState { impl FrameState { pub(crate) fn begin_frame(&mut self, input: &InputState) { + crate::profile_function!(); let Self { used_ids, available_rect, diff --git a/crates/egui/src/input_state.rs b/crates/egui/src/input_state.rs index 69ea42d2..7f94cf16 100644 --- a/crates/egui/src/input_state.rs +++ b/crates/egui/src/input_state.rs @@ -149,6 +149,7 @@ impl InputState { mut new: RawInput, requested_repaint_last_frame: bool, ) -> InputState { + crate::profile_function!(); let time = new.time.unwrap_or(self.time + new.predicted_dt as f64); let unstable_dt = (time - self.time) as f32; diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 4b2c531f..2201afef 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -579,3 +579,32 @@ pub fn __run_test_ui(mut add_contents: impl FnMut(&mut Ui)) { pub fn accesskit_root_id() -> Id { Id::new("accesskit_root") } + +// --------------------------------------------------------------------------- + +mod profiling_scopes { + #![allow(unused_macros)] + #![allow(unused_imports)] + + /// Profiling macro for feature "puffin" + macro_rules! profile_function { + ($($arg: tt)*) => { + #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. + #[cfg(feature = "puffin")] + puffin::profile_function!($($arg)*); + }; + } + pub(crate) use profile_function; + + /// Profiling macro for feature "puffin" + macro_rules! profile_scope { + ($($arg: tt)*) => { + #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. + #[cfg(feature = "puffin")] + puffin::profile_scope!($($arg)*); + }; + } + pub(crate) use profile_scope; +} + +pub(crate) use profiling_scopes::*; diff --git a/crates/egui/src/memory.rs b/crates/egui/src/memory.rs index 7b2f9d61..0013ddf0 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -493,6 +493,7 @@ impl Memory { prev_input: &crate::input_state::InputState, new_input: &crate::data::input::RawInput, ) { + crate::profile_function!(); self.interaction.begin_frame(prev_input, new_input); if !prev_input.pointer.any_down() { diff --git a/crates/egui/src/util/id_type_map.rs b/crates/egui/src/util/id_type_map.rs index 60cf6595..1f2960e6 100644 --- a/crates/egui/src/util/id_type_map.rs +++ b/crates/egui/src/util/id_type_map.rs @@ -515,6 +515,7 @@ struct PersistedMap(Vec<(u64, SerializedElement)>); #[cfg(feature = "persistence")] impl PersistedMap { fn from_map(map: &IdTypeMap) -> Self { + crate::profile_function!(); // filter out the elements which cannot be serialized: Self( map.0 @@ -525,6 +526,7 @@ impl PersistedMap { } fn into_map(self) -> IdTypeMap { + crate::profile_function!(); IdTypeMap( self.0 .into_iter() @@ -542,6 +544,7 @@ impl serde::Serialize for IdTypeMap { where S: serde::Serializer, { + crate::profile_scope!("IdTypeMap::serialize"); PersistedMap::from_map(self).serialize(serializer) } } @@ -552,6 +555,7 @@ impl<'de> serde::Deserialize<'de> for IdTypeMap { where D: serde::Deserializer<'de>, { + crate::profile_scope!("IdTypeMap::deserialize"); ::deserialize(deserializer).map(PersistedMap::into_map) } } diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index dfd6e0de..1e611b98 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -37,7 +37,7 @@ clipboard = ["egui-winit?/clipboard"] links = ["egui-winit?/links"] ## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -puffin = ["dep:puffin", "egui-winit?/puffin"] +puffin = ["dep:puffin", "egui-winit?/puffin", "egui/puffin"] ## Enable [`winit`](https://docs.rs/winit) integration. winit = ["egui-winit"]