diff --git a/Cargo.lock b/Cargo.lock index 9d227a65e..350246bae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1231,6 +1231,7 @@ dependencies = [ "epaint", "log", "nohash-hasher", + "puffin", "ron", "serde", ] diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index fd4076ed0..782905201 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 30e206306..1496149ab 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 615195836..2d184631f 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 b50bca014..53a6ab337 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -328,6 +328,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); @@ -346,25 +347,33 @@ impl ContextImpl { /// Load fonts unless already loaded. fn update_fonts_mut(&mut self) { + crate::profile_function!(); + let input = self.input.entry(self.get_viewport_id()).or_default(); let pixels_per_point = input.pixels_per_point(); let max_texture_side = 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(); } @@ -531,6 +540,8 @@ impl Context { parent_viewport_id: ViewportId, run_ui: impl FnOnce(&Context), ) -> FullOutput { + crate::profile_function!(); + self.begin_frame(new_input, viewport_id, parent_viewport_id); run_ui(self); self.end_frame() @@ -559,6 +570,8 @@ impl Context { viewport_id: ViewportId, parent_viewport_id: ViewportId, ) { + crate::profile_function!(); + self.write(|ctx| ctx.begin_frame_mut(new_input, viewport_id, parent_viewport_id)); } @@ -1424,6 +1437,8 @@ impl Context { /// Call at the end of each frame. #[must_use] pub fn end_frame(&self) -> FullOutput { + crate::profile_function!(); + let mut viewports: Vec = self.write(|ctx| { ctx.layer_rects_prev_viewports.insert( ctx.get_viewport_id(), @@ -1466,6 +1481,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); @@ -1564,6 +1580,7 @@ impl Context { } fn drain_paint_lists(&self) -> Vec { + crate::profile_function!(); self.write(|ctx| { ctx.graphics .entry(ctx.get_viewport_id()) @@ -1575,6 +1592,8 @@ impl Context { /// 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. @@ -1598,13 +1617,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 287b35c56..f82b2e7e6 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 70b140b58..031a75d19 100644 --- a/crates/egui/src/input_state.rs +++ b/crates/egui/src/input_state.rs @@ -169,6 +169,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 afcbae971..38dca2017 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -591,3 +591,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 73b54efba..3257654a3 100644 --- a/crates/egui/src/memory.rs +++ b/crates/egui/src/memory.rs @@ -511,6 +511,8 @@ impl Memory { new_input: &crate::data::input::RawInput, viewport_id: ViewportId, ) { + crate::profile_function!(); + self.viewport_id = viewport_id; self.interactions .entry(viewport_id) diff --git a/crates/egui/src/util/id_type_map.rs b/crates/egui/src/util/id_type_map.rs index 60cf65954..1f2960e64 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 dfd6e0de4..1e611b982 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"]