From 3af907919be1fdaa228f10dbe7b5da3bf0e16510 Mon Sep 17 00:00:00 2001 From: Ted de Munnik Date: Mon, 16 Dec 2024 09:15:54 +0100 Subject: [PATCH] Use `profiling` crate to support more profiler backends (#5150) Hey! I am not sure if this is something that's been considered before and decided against (I couldn't find any PR's or issues). This change removes the internal profiling macros in library crates and the `puffin` feature and replaces it with similar functions in the [profiling](https://github.com/aclysma/profiling) crate. This crate provides a layer of abstraction over various profiler instrumentation crates and allows library users to pick their favorite (supported) profiler. An additional benefit for puffin users is that dependencies of egui are included in the instrumentation output too (mainly wgpu which uses the profiling crate), so more details might be available when profiling. A breaking change is that instead of using the `puffin` feature on egui, users that want to profile the crate with puffin instead have to enable the `profile-with-puffin` feature on the profiling crate. Similarly they could instead choose to use `profile-with-tracy` etc. I tried to add a 'tracy' feature to egui_demo_app in order to showcase , however the /scripts/check.sh currently breaks on mutually exclusive features (which this introduces), so I decided against including it for the initial PR. I'm happy to iterate more on this if there is interest in taking this PR though. Screenshot showing the additional info for wgpu now available when using puffin ![image](https://github.com/user-attachments/assets/49fc0e7e-8f88-40cb-a69e-74ca2e3f90f3) --- Cargo.lock | 30 +++++++--- Cargo.toml | 1 + crates/eframe/Cargo.toml | 15 +---- crates/eframe/src/epi.rs | 7 +-- crates/eframe/src/icon_data.rs | 6 +- crates/eframe/src/lib.rs | 41 ++++--------- crates/eframe/src/native/app_icon.rs | 8 +-- crates/eframe/src/native/epi_integration.rs | 36 ++++++----- crates/eframe/src/native/file_storage.rs | 14 ++--- crates/eframe/src/native/glow_integration.rs | 48 +++++++-------- crates/eframe/src/native/run.rs | 14 ++--- crates/eframe/src/native/wgpu_integration.rs | 32 +++++----- crates/eframe/src/native/winit_integration.rs | 2 +- crates/egui-wgpu/Cargo.toml | 8 +-- crates/egui-wgpu/src/lib.rs | 38 ++---------- crates/egui-wgpu/src/renderer.rs | 46 +++++++------- crates/egui-wgpu/src/winit.rs | 20 +++---- crates/egui-winit/Cargo.toml | 5 +- crates/egui-winit/src/clipboard.rs | 4 +- crates/egui-winit/src/lib.rs | 59 +++++------------- crates/egui-winit/src/window_settings.rs | 7 +-- crates/egui/Cargo.toml | 6 +- crates/egui/src/context.rs | 60 ++++++++++--------- crates/egui/src/hit_test.rs | 2 +- crates/egui/src/input_state/mod.rs | 2 +- crates/egui/src/interaction.rs | 2 +- crates/egui/src/layers.rs | 2 +- crates/egui/src/lib.rs | 42 ++++--------- crates/egui/src/memory/mod.rs | 2 +- crates/egui/src/pass_state.rs | 2 +- crates/egui/src/util/id_type_map.rs | 12 ++-- crates/egui/src/viewport.rs | 4 +- crates/egui_demo_app/Cargo.toml | 6 +- crates/egui_demo_app/src/main.rs | 1 + crates/egui_extras/Cargo.toml | 7 +-- crates/egui_extras/src/image.rs | 5 +- crates/egui_extras/src/lib.rs | 30 ---------- crates/egui_extras/src/syntax_highlighting.rs | 7 +-- crates/egui_glow/Cargo.toml | 6 +- crates/egui_glow/src/lib.rs | 30 ---------- crates/egui_glow/src/painter.rs | 25 ++++---- crates/epaint/Cargo.toml | 7 +-- crates/epaint/src/lib.rs | 30 ---------- crates/epaint/src/mesh.rs | 4 +- crates/epaint/src/tessellator.rs | 14 ++--- examples/puffin_profiler/Cargo.toml | 5 +- 46 files changed, 272 insertions(+), 482 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ebe0aa08f70..8d43ee086ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ "parking_lot", "percent-encoding", "pollster 0.4.0", - "puffin", + "profiling", "raw-window-handle 0.6.2", "ron", "serde", @@ -1263,7 +1263,7 @@ dependencies = [ "epaint", "log", "nohash-hasher", - "puffin", + "profiling", "ron", "serde", ] @@ -1278,7 +1278,7 @@ dependencies = [ "egui", "epaint", "log", - "puffin", + "profiling", "thiserror", "type-map", "web-time", @@ -1296,7 +1296,7 @@ dependencies = [ "document-features", "egui", "log", - "puffin", + "profiling", "raw-window-handle 0.6.2", "serde", "smithay-clipboard", @@ -1321,6 +1321,7 @@ dependencies = [ "image", "log", "poll-promise", + "profiling", "puffin", "puffin_http", "rfd", @@ -1360,7 +1361,7 @@ dependencies = [ "image", "log", "mime_guess2", - "puffin", + "profiling", "resvg", "serde", "syntect", @@ -1380,7 +1381,7 @@ dependencies = [ "glutin-winit", "log", "memoffset", - "puffin", + "profiling", "wasm-bindgen", "web-sys", "winit", @@ -1528,7 +1529,7 @@ dependencies = [ "log", "nohash-hasher", "parking_lot", - "puffin", + "profiling", "rayon", "serde", ] @@ -3109,6 +3110,20 @@ name = "profiling" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", + "puffin", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn", +] [[package]] name = "puffin" @@ -3147,6 +3162,7 @@ dependencies = [ "eframe", "env_logger", "log", + "profiling", "puffin", "puffin_http", ] diff --git a/Cargo.toml b/Cargo.toml index 6b50ac23a0a..5c00189d068 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -87,6 +87,7 @@ log = { version = "0.4", features = ["std"] } nohash-hasher = "0.2" parking_lot = "0.12" pollster = "0.4" +profiling = {version = "1.0", default-features = false } puffin = "0.19" puffin_http = "0.16" raw-window-handle = "0.6.0" diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 87ab1d3b2c9..c07dc9d0d3a 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -71,19 +71,6 @@ persistence = [ "serde", ] -## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -## -## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you -## -## Only enabled on native, because of the low resolution (1ms) of clocks in browsers. -puffin = [ - "dep:puffin", - "egui/puffin", - "egui_glow?/puffin", - "egui-wgpu?/puffin", - "egui-winit/puffin", -] - ## Enables wayland support and fixes clipboard issue. wayland = ["egui-winit/wayland", "egui-wgpu?/wayland", "egui_glow?/wayland", "glutin?/wayland", "glutin-winit?/wayland"] @@ -127,6 +114,7 @@ ahash.workspace = true document-features.workspace = true log.workspace = true parking_lot.workspace = true +profiling.workspace = true raw-window-handle.workspace = true static_assertions = "1.1.0" web-time.workspace = true @@ -157,7 +145,6 @@ pollster = { workspace = true, optional = true } # needed for wgpu glutin = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] } glutin-winit = { workspace = true, optional = true, default-features = false, features = ["egl", "wgl"] } home = { workspace = true, optional = true } -puffin = { workspace = true, optional = true } wgpu = { workspace = true, optional = true, features = [ # Let's enable some backends so that users can use `eframe` out-of-the-box # without having to explicitly opt-in to backends diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 53051f398e4..45d8335018e 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -788,8 +788,7 @@ pub struct IntegrationInfo { /// /// This includes [`App::update`] as well as rendering (except for vsync waiting). /// - /// For a more detailed view of cpu usage, use the [`puffin`](https://crates.io/crates/puffin) - /// profiler together with the `puffin` feature of `eframe`. + /// For a more detailed view of cpu usage, connect your preferred profiler by enabling it's feature in [`profiling`](https://crates.io/crates/profiling). /// /// `None` if this is the first frame. pub cpu_usage: Option, @@ -831,7 +830,7 @@ impl Storage for DummyStorage { /// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key. #[cfg(feature = "ron")] pub fn get_value(storage: &dyn Storage, key: &str) -> Option { - crate::profile_function!(key); + profiling::function_scope!(key); storage .get_string(key) .and_then(|value| match ron::from_str(&value) { @@ -847,7 +846,7 @@ pub fn get_value(storage: &dyn Storage, key: &st /// Serialize the given value as [RON](https://github.com/ron-rs/ron) and store with the given key. #[cfg(feature = "ron")] pub fn set_value(storage: &mut dyn Storage, key: &str, value: &T) { - crate::profile_function!(key); + profiling::function_scope!(key); match ron::ser::to_string(value) { Ok(string) => storage.set_string(key, string), Err(err) => log::error!("eframe failed to encode data using ron: {}", err), diff --git a/crates/eframe/src/icon_data.rs b/crates/eframe/src/icon_data.rs index ed514d00e1f..4851bee64b3 100644 --- a/crates/eframe/src/icon_data.rs +++ b/crates/eframe/src/icon_data.rs @@ -22,7 +22,7 @@ pub trait IconDataExt { /// # Errors /// If this is not a valid png. pub fn from_png_bytes(png_bytes: &[u8]) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let image = image::load_from_memory(png_bytes)?; Ok(from_image(image)) } @@ -38,7 +38,7 @@ fn from_image(image: image::DynamicImage) -> IconData { impl IconDataExt for IconData { fn to_image(&self) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let Self { rgba, width, @@ -48,7 +48,7 @@ impl IconDataExt for IconData { } fn to_png_bytes(&self) -> Result, String> { - crate::profile_function!(); + profiling::function_scope!(); let image = self.to_image()?; let mut png_bytes: Vec = Vec::new(); image diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 80da6235792..7b342a4c21f 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -129,6 +129,17 @@ //! ## Feature flags #![doc = document_features::document_features!()] //! +//! ## Instrumentation +//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation. +//! You can enable features on the profiling crates in your application to add instrumentation for all +//! crates that support it, including egui. See the profiling crate docs for more information. +//! ```toml +//! [dependencies] +//! profiling = "1.0" +//! [features] +//! profile-with-puffin = ["profiling/profile-with-puffin"] +//! ``` +//! #![warn(missing_docs)] // let's keep eframe well-documented #![allow(clippy::needless_doctest_main)] @@ -445,33 +456,3 @@ impl std::fmt::Display for Error { /// Short for `Result`. pub type Result = std::result::Result; - -// --------------------------------------------------------------------------- - -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs index 840bf367b27..8591ba2a8cc 100644 --- a/crates/eframe/src/native/app_icon.rs +++ b/crates/eframe/src/native/app_icon.rs @@ -59,7 +59,7 @@ enum AppIconStatus { /// Since window creation can be lazy, call this every frame until it's either successfully or gave up. /// (See [`AppIconStatus`]) fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconStatus { - crate::profile_function!(); + profiling::function_scope!(); #[cfg(target_os = "windows")] { @@ -201,7 +201,7 @@ fn set_app_icon_windows(icon_data: &IconData) -> AppIconStatus { #[allow(unsafe_code)] fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconStatus { use crate::icon_data::IconDataExt as _; - crate::profile_function!(); + profiling::function_scope!(); use objc2::ClassType; use objc2_app_kit::{NSApplication, NSImage}; @@ -237,7 +237,7 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS log::trace!("NSImage::initWithData…"); let app_icon = NSImage::initWithData(NSImage::alloc(), &data); - crate::profile_scope!("setApplicationIconImage_"); + profiling::scope!("setApplicationIconImage_"); log::trace!("setApplicationIconImage…"); app.setApplicationIconImage(app_icon.as_deref()); } @@ -246,7 +246,7 @@ fn set_title_and_icon_mac(title: &str, icon_data: Option<&IconData>) -> AppIconS if let Some(main_menu) = app.mainMenu() { if let Some(item) = main_menu.itemAtIndex(0) { if let Some(app_menu) = item.submenu() { - crate::profile_scope!("setTitle_"); + profiling::scope!("setTitle_"); app_menu.setTitle(&NSString::from_str(title)); } } diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 5f9d555e3a5..03b5f2dcd46 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -19,7 +19,7 @@ pub fn viewport_builder( native_options: &mut epi::NativeOptions, window_settings: Option, ) -> ViewportBuilder { - crate::profile_function!(); + profiling::function_scope!(); let mut viewport_builder = native_options.viewport.clone(); @@ -67,7 +67,7 @@ pub fn viewport_builder( #[cfg(not(target_os = "ios"))] if native_options.centered { - crate::profile_scope!("center"); + profiling::scope!("center"); if let Some(monitor) = event_loop .primary_monitor() .or_else(|| event_loop.available_monitors().next()) @@ -94,8 +94,7 @@ pub fn apply_window_settings( window: &winit::window::Window, window_settings: Option, ) { - crate::profile_function!(); - + profiling::function_scope!(); if let Some(window_settings) = window_settings { window_settings.initialize_window(window); } @@ -103,12 +102,11 @@ pub fn apply_window_settings( #[cfg(not(target_os = "ios"))] fn largest_monitor_point_size(egui_zoom_factor: f32, event_loop: &ActiveEventLoop) -> egui::Vec2 { - crate::profile_function!(); - + profiling::function_scope!(); let mut max_size = egui::Vec2::ZERO; let available_monitors = { - crate::profile_scope!("available_monitors"); + profiling::scope!("available_monitors"); event_loop.available_monitors() }; @@ -238,7 +236,7 @@ impl EpiIntegration { egui_winit: &mut egui_winit::State, event: &winit::event::WindowEvent, ) -> EventResponse { - crate::profile_function!(egui_winit::short_window_event_description(event)); + profiling::function_scope!(egui_winit::short_window_event_description(event)); use winit::event::{ElementState, MouseButton, WindowEvent}; @@ -276,10 +274,10 @@ impl EpiIntegration { let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { if let Some(viewport_ui_cb) = viewport_ui_cb { // Child viewport - crate::profile_scope!("viewport_callback"); + profiling::scope!("viewport_callback"); viewport_ui_cb(egui_ctx); } else { - crate::profile_scope!("App::update"); + profiling::scope!("App::update"); app.update(egui_ctx, &mut self.frame); } }); @@ -306,7 +304,7 @@ impl EpiIntegration { } pub fn post_rendering(&mut self, window: &winit::window::Window) { - crate::profile_function!(); + profiling::function_scope!(); if std::mem::take(&mut self.is_first_frame) { // We keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 window.set_visible(true); @@ -332,11 +330,11 @@ impl EpiIntegration { pub fn save(&mut self, _app: &mut dyn epi::App, _window: Option<&winit::window::Window>) { #[cfg(feature = "persistence")] if let Some(storage) = self.frame.storage_mut() { - crate::profile_function!(); + profiling::function_scope!(); if let Some(window) = _window { if self.persist_window { - crate::profile_scope!("native_window"); + profiling::scope!("native_window"); epi::set_value( storage, STORAGE_WINDOW_KEY, @@ -345,23 +343,23 @@ impl EpiIntegration { } } if _app.persist_egui_memory() { - crate::profile_scope!("egui_memory"); + profiling::scope!("egui_memory"); self.egui_ctx .memory(|mem| epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, mem)); } { - crate::profile_scope!("App::save"); + profiling::scope!("App::save"); _app.save(storage); } - crate::profile_scope!("Storage::flush"); + profiling::scope!("Storage::flush"); storage.flush(); } } } fn load_default_egui_icon() -> egui::IconData { - crate::profile_function!(); + profiling::function_scope!(); crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap() } @@ -372,7 +370,7 @@ const STORAGE_EGUI_MEMORY_KEY: &str = "egui"; const STORAGE_WINDOW_KEY: &str = "window"; pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option { - crate::profile_function!(); + profiling::function_scope!(); #[cfg(feature = "persistence")] { epi::get_value(_storage?, STORAGE_WINDOW_KEY) @@ -382,7 +380,7 @@ pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option) -> Option { - crate::profile_function!(); + profiling::function_scope!(); #[cfg(feature = "persistence")] { epi::get_value(_storage?, STORAGE_EGUI_MEMORY_KEY) diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index c47a71e6867..346c46b4254 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -100,7 +100,7 @@ pub struct FileStorage { impl Drop for FileStorage { fn drop(&mut self) { if let Some(join_handle) = self.last_save_join_handle.take() { - crate::profile_scope!("wait_for_save"); + profiling::scope!("wait_for_save"); join_handle.join().ok(); } } @@ -109,7 +109,7 @@ impl Drop for FileStorage { impl FileStorage { /// Store the state in this .ron file. pub(crate) fn from_ron_filepath(ron_filepath: impl Into) -> Self { - crate::profile_function!(); + profiling::function_scope!(); let ron_filepath: PathBuf = ron_filepath.into(); log::debug!("Loading app state from {:?}…", ron_filepath); Self { @@ -122,7 +122,7 @@ impl FileStorage { /// Find a good place to put the files that the OS likes. pub fn from_app_id(app_id: &str) -> Option { - crate::profile_function!(app_id); + profiling::function_scope!(); if let Some(data_dir) = storage_dir(app_id) { if let Err(err) = std::fs::create_dir_all(&data_dir) { log::warn!( @@ -155,7 +155,7 @@ impl crate::Storage for FileStorage { fn flush(&mut self) { if self.dirty { - crate::profile_function!(); + profiling::scope!("FileStorage::flush"); self.dirty = false; let file_path = self.ron_filepath.clone(); @@ -184,7 +184,7 @@ impl crate::Storage for FileStorage { } fn save_to_disk(file_path: &PathBuf, kv: &HashMap) { - crate::profile_function!(); + profiling::function_scope!(); if let Some(parent_dir) = file_path.parent() { if !parent_dir.exists() { @@ -199,7 +199,7 @@ fn save_to_disk(file_path: &PathBuf, kv: &HashMap) { let mut writer = std::io::BufWriter::new(file); let config = Default::default(); - crate::profile_scope!("ron::serialize"); + profiling::scope!("ron::serialize"); if let Err(err) = ron::ser::to_writer_pretty(&mut writer, &kv, config) .and_then(|_| writer.flush().map_err(|err| err.into())) { @@ -220,7 +220,7 @@ fn read_ron(ron_path: impl AsRef) -> Option where T: serde::de::DeserializeOwned, { - crate::profile_function!(); + profiling::function_scope!(); match std::fs::File::open(ron_path) { Ok(file) => { let reader = std::io::BufReader::new(file); diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 51c0cadce2a..f17c6ad5091 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -129,7 +129,7 @@ impl<'app> GlowWinitApp<'app> { native_options: NativeOptions, app_creator: AppCreator<'app>, ) -> Self { - crate::profile_function!(); + profiling::function_scope!(); Self { repaint_proxy: Arc::new(egui::mutex::Mutex::new(event_loop.create_proxy())), app_name: app_name.to_owned(), @@ -146,8 +146,7 @@ impl<'app> GlowWinitApp<'app> { storage: Option<&dyn Storage>, native_options: &mut NativeOptions, ) -> Result<(GlutinWindowContext, egui_glow::Painter)> { - crate::profile_function!(); - + profiling::function_scope!(); let window_settings = epi_integration::load_window_settings(storage); let winit_window_builder = epi_integration::viewport_builder( @@ -172,7 +171,7 @@ impl<'app> GlowWinitApp<'app> { } let gl = unsafe { - crate::profile_scope!("glow::Context::from_loader_function"); + profiling::scope!("glow::Context::from_loader_function"); Arc::new(glow::Context::from_loader_function(|s| { let s = std::ffi::CString::new(s) .expect("failed to construct C string from string for gl proc address"); @@ -195,7 +194,7 @@ impl<'app> GlowWinitApp<'app> { &mut self, event_loop: &ActiveEventLoop, ) -> Result<&mut GlowWinitRunning<'app>> { - crate::profile_function!(); + profiling::function_scope!(); let storage = if let Some(file) = &self.native_options.persistence_path { epi_integration::create_storage_with_file(file) @@ -308,7 +307,7 @@ impl<'app> GlowWinitApp<'app> { raw_display_handle: window.display_handle().map(|h| h.as_raw()), raw_window_handle: window.window_handle().map(|h| h.as_raw()), }; - crate::profile_scope!("app_creator"); + profiling::scope!("app_creator"); app_creator(&cc).map_err(crate::Error::AppCreation)? }; @@ -369,7 +368,7 @@ impl<'app> WinitApp for GlowWinitApp<'app> { fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { - crate::profile_function!(); + profiling::function_scope!(); running.integration.save( running.app.as_mut(), @@ -486,7 +485,7 @@ impl<'app> GlowWinitRunning<'app> { event_loop: &ActiveEventLoop, window_id: WindowId, ) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let Some(viewport_id) = self .glutin @@ -498,8 +497,7 @@ impl<'app> GlowWinitRunning<'app> { return Ok(EventResult::Wait); }; - #[cfg(feature = "puffin")] - puffin::GlobalProfiler::lock().new_frame(); + profiling::finish_frame!(); let mut frame_timer = crate::stopwatch::Stopwatch::new(); frame_timer.start(); @@ -698,7 +696,7 @@ impl<'app> GlowWinitRunning<'app> { { // vsync - don't count as frame-time: frame_timer.pause(); - crate::profile_scope!("swap_buffers"); + profiling::scope!("swap_buffers"); let context = current_gl_context .as_ref() .ok_or(egui_glow::PainterError::from( @@ -726,7 +724,7 @@ impl<'app> GlowWinitRunning<'app> { if window.is_minimized() == Some(true) { // On Mac, a minimized Window uses up all CPU: // https://github.com/emilk/egui/issues/325 - crate::profile_scope!("minimized_sleep"); + profiling::scope!("minimized_sleep"); std::thread::sleep(std::time::Duration::from_millis(10)); } @@ -857,7 +855,7 @@ fn change_gl_context( not_current_gl_context: &mut Option, gl_surface: &glutin::surface::Surface, ) { - crate::profile_function!(); + profiling::function_scope!(); if !cfg!(target_os = "windows") { // According to https://github.com/emilk/egui/issues/4289 @@ -866,7 +864,7 @@ fn change_gl_context( // See https://github.com/emilk/egui/issues/4173 if let Some(current_gl_context) = current_gl_context { - crate::profile_scope!("is_current"); + profiling::scope!("is_current"); if gl_surface.is_current(current_gl_context) { return; // Early-out to save a lot of time. } @@ -876,7 +874,7 @@ fn change_gl_context( let not_current = if let Some(not_current_context) = not_current_gl_context.take() { not_current_context } else { - crate::profile_scope!("make_not_current"); + profiling::scope!("make_not_current"); current_gl_context .take() .unwrap() @@ -884,7 +882,7 @@ fn change_gl_context( .unwrap() }; - crate::profile_scope!("make_current"); + profiling::scope!("make_current"); *current_gl_context = Some(not_current.make_current(gl_surface).unwrap()); } @@ -896,7 +894,7 @@ impl GlutinWindowContext { native_options: &NativeOptions, event_loop: &ActiveEventLoop, ) -> Result { - crate::profile_function!(); + profiling::function_scope!(); // There is a lot of complexity with opengl creation, // so prefer extensive logging to get all the help we can to debug issues. @@ -952,7 +950,7 @@ impl GlutinWindowContext { ))); let (window, gl_config) = { - crate::profile_scope!("DisplayBuilder::build"); + profiling::scope!("DisplayBuilder::build"); display_builder .build( @@ -995,7 +993,7 @@ impl GlutinWindowContext { .build(glutin_raw_window_handle); let gl_context_result = unsafe { - crate::profile_scope!("create_context"); + profiling::scope!("create_context"); gl_config .display() .create_context(&gl_config, &context_attributes) @@ -1070,7 +1068,7 @@ impl GlutinWindowContext { /// /// Errors will be logged. fn initialize_all_windows(&mut self, event_loop: &ActiveEventLoop) { - crate::profile_function!(); + profiling::function_scope!(); let viewports: Vec = self.viewports.keys().copied().collect(); @@ -1088,7 +1086,7 @@ impl GlutinWindowContext { viewport_id: ViewportId, event_loop: &ActiveEventLoop, ) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let viewport = self .viewports @@ -1268,7 +1266,7 @@ impl GlutinWindowContext { egui_ctx: &egui::Context, viewport_output: &ViewportIdMap, ) { - crate::profile_function!(); + profiling::function_scope!(); for ( viewport_id, @@ -1329,7 +1327,7 @@ fn initialize_or_update_viewport( mut builder: ViewportBuilder, viewport_ui_cb: Option>, ) -> &mut Viewport { - crate::profile_function!(); + profiling::function_scope!(); if builder.icon.is_none() { // Inherit icon from parent @@ -1393,7 +1391,7 @@ fn render_immediate_viewport( beginning: Instant, immediate_viewport: ImmediateViewport<'_>, ) { - crate::profile_function!(); + profiling::function_scope!(); let ImmediateViewport { ids, @@ -1516,7 +1514,7 @@ fn render_immediate_viewport( ); { - crate::profile_scope!("swap_buffers"); + profiling::scope!("swap_buffers"); if let Err(err) = gl_surface.swap_buffers(current_gl_context) { log::error!("swap_buffers failed: {err}"); } diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 219edd56625..e328877a4be 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -20,7 +20,7 @@ fn create_event_loop(native_options: &mut epi::NativeOptions) -> Result Result WinitAppWrapper { impl ApplicationHandler for WinitAppWrapper { fn suspended(&mut self, event_loop: &ActiveEventLoop) { - crate::profile_function!("Event::Suspended"); + profiling::scope!("Event::Suspended"); event_loop_context::with_event_loop_context(event_loop, move || { let event_result = self.winit_app.suspended(event_loop); @@ -195,7 +195,7 @@ impl ApplicationHandler for WinitAppWrapper { } fn resumed(&mut self, event_loop: &ActiveEventLoop) { - crate::profile_function!("Event::Resumed"); + profiling::scope!("Event::Resumed"); // Nb: Make sure this guard is dropped after this function returns. event_loop_context::with_event_loop_context(event_loop, move || { @@ -219,7 +219,7 @@ impl ApplicationHandler for WinitAppWrapper { device_id: winit::event::DeviceId, event: winit::event::DeviceEvent, ) { - crate::profile_function!(egui_winit::short_device_event_description(&event)); + profiling::function_scope!(egui_winit::short_device_event_description(&event)); // Nb: Make sure this guard is dropped after this function returns. event_loop_context::with_event_loop_context(event_loop, move || { @@ -229,7 +229,7 @@ impl ApplicationHandler for WinitAppWrapper { } fn user_event(&mut self, event_loop: &ActiveEventLoop, event: UserEvent) { - crate::profile_function!(match &event { + profiling::function_scope!(match &event { UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", #[cfg(feature = "accesskit")] UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", @@ -285,7 +285,7 @@ impl ApplicationHandler for WinitAppWrapper { window_id: WindowId, event: winit::event::WindowEvent, ) { - crate::profile_function!(egui_winit::short_window_event_description(&event)); + profiling::function_scope!(egui_winit::short_window_event_description(&event)); // Nb: Make sure this guard is dropped after this function returns. event_loop_context::with_event_loop_context(event_loop, move || { diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index acbd8c2f830..93643c2230e 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -102,7 +102,7 @@ impl<'app> WgpuWinitApp<'app> { native_options: NativeOptions, app_creator: AppCreator<'app>, ) -> Self { - crate::profile_function!(); + profiling::function_scope!(); #[cfg(feature = "__screenshot")] assert!( @@ -181,8 +181,7 @@ impl<'app> WgpuWinitApp<'app> { window: Window, builder: ViewportBuilder, ) -> crate::Result<&mut WgpuWinitRunning<'app>> { - crate::profile_function!(); - + profiling::function_scope!(); #[allow(unsafe_code, unused_mut, unused_unsafe)] let mut painter = egui_wgpu::winit::Painter::new( egui_ctx.clone(), @@ -199,7 +198,7 @@ impl<'app> WgpuWinitApp<'app> { let window = Arc::new(window); { - crate::profile_scope!("set_window"); + profiling::scope!("set_window"); pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone())))?; } @@ -268,7 +267,7 @@ impl<'app> WgpuWinitApp<'app> { raw_window_handle: window.window_handle().map(|h| h.as_raw()), }; let app = { - crate::profile_scope!("user_app_creator"); + profiling::scope!("user_app_creator"); app_creator(&cc).map_err(crate::Error::AppCreation)? }; @@ -490,7 +489,7 @@ impl<'app> WinitApp for WgpuWinitApp<'app> { impl<'app> WgpuWinitRunning<'app> { fn save_and_destroy(&mut self) { - crate::profile_function!(); + profiling::function_scope!(); let mut shared = self.shared.borrow_mut(); if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { @@ -508,7 +507,7 @@ impl<'app> WgpuWinitRunning<'app> { /// This is called both for the root viewport, and all deferred viewports fn run_ui_and_paint(&mut self, window_id: WindowId) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let Some(viewport_id) = self .shared @@ -520,8 +519,7 @@ impl<'app> WgpuWinitRunning<'app> { return Ok(EventResult::Wait); }; - #[cfg(feature = "puffin")] - puffin::GlobalProfiler::lock().new_frame(); + profiling::finish_frame!(); let Self { app, @@ -533,7 +531,7 @@ impl<'app> WgpuWinitRunning<'app> { frame_timer.start(); let (viewport_ui_cb, raw_input) = { - crate::profile_scope!("Prepare"); + profiling::scope!("Prepare"); let mut shared_lock = shared.borrow_mut(); let SharedState { @@ -577,7 +575,7 @@ impl<'app> WgpuWinitRunning<'app> { egui_winit::update_viewport_info(info, &integration.egui_ctx, window, false); { - crate::profile_scope!("set_window"); + profiling::scope!("set_window"); pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?; } @@ -719,7 +717,7 @@ impl<'app> WgpuWinitRunning<'app> { if window.is_minimized() == Some(true) { // On Mac, a minimized Window uses up all CPU: // https://github.com/emilk/egui/issues/325 - crate::profile_scope!("minimized_sleep"); + profiling::scope!("minimized_sleep"); std::thread::sleep(std::time::Duration::from_millis(10)); } } @@ -846,7 +844,7 @@ impl Viewport { return; // we already have one } - crate::profile_function!(); + profiling::function_scope!(); let viewport_id = self.ids.this; @@ -887,7 +885,7 @@ fn create_window( storage: Option<&dyn Storage>, native_options: &mut NativeOptions, ) -> Result<(Window, ViewportBuilder), winit::error::OsError> { - crate::profile_function!(); + profiling::function_scope!(); let window_settings = epi_integration::load_window_settings(storage); let viewport_builder = epi_integration::viewport_builder( @@ -908,7 +906,7 @@ fn render_immediate_viewport( shared: &RefCell, immediate_viewport: ImmediateViewport<'_>, ) { - crate::profile_function!(); + profiling::function_scope!(); let ImmediateViewport { ids, @@ -988,7 +986,7 @@ fn render_immediate_viewport( }; { - crate::profile_scope!("set_window"); + profiling::scope!("set_window"); if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window.clone()))) { log::error!( "when rendering viewport_id={:?}, set_window Error {err}", @@ -1096,7 +1094,7 @@ fn initialize_or_update_viewport<'a>( viewport_ui_cb: Option>, painter: &mut egui_wgpu::winit::Painter, ) -> &'a mut Viewport { - crate::profile_function!(); + profiling::function_scope!(); if builder.icon.is_none() { // Inherit icon from parent diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index e9d214103b8..2b6c54a67f9 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -11,7 +11,7 @@ use egui_winit::accesskit_winit; /// Create an egui context, restoring it from storage if possible. pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context { - crate::profile_function!(); + profiling::function_scope!(); pub const IS_DESKTOP: bool = cfg!(any( target_os = "freebsd", diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index 4575ce0069b..2f4330236d0 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -33,9 +33,6 @@ rustdoc-args = ["--generate-link-to-definition"] [features] default = ["fragile-send-sync-non-atomic-wasm"] -## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -puffin = ["dep:puffin"] - ## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11` winit = ["dep:winit", "winit/rwh_06"] @@ -60,6 +57,7 @@ ahash.workspace = true bytemuck.workspace = true document-features.workspace = true log.workspace = true +profiling.workspace = true thiserror.workspace = true type-map.workspace = true web-time.workspace = true @@ -68,7 +66,3 @@ wgpu = { workspace = true, features = ["wgsl"] } # Optional dependencies: winit = { workspace = true, optional = true, default-features = false } - -# Native: -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -puffin = { workspace = true, optional = true } diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 54c76f05461..a91662f45b1 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -96,7 +96,7 @@ impl RenderState { msaa_samples: u32, dithering: bool, ) -> Result { - crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function` + profiling::scope!("RenderState::create"); // async yield give bad names using `profile_function` // This is always an empty list on web. #[cfg(not(target_arch = "wasm32"))] @@ -109,7 +109,7 @@ impl RenderState { device_descriptor, } => { let adapter = { - crate::profile_scope!("request_adapter"); + profiling::scope!("request_adapter"); instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference, @@ -164,7 +164,7 @@ impl RenderState { let trace_path = std::env::var("WGPU_TRACE"); let (device, queue) = { - crate::profile_scope!("request_device"); + profiling::scope!("request_device"); adapter .request_device( &(*device_descriptor)(&adapter), @@ -187,7 +187,7 @@ impl RenderState { }; let capabilities = { - crate::profile_scope!("get_capabilities"); + profiling::scope!("get_capabilities"); surface.get_capabilities(&adapter).formats }; let target_format = crate::preferred_framebuffer_format(&capabilities)?; @@ -474,33 +474,3 @@ pub fn adapter_info_summary(info: &wgpu::AdapterInfo) -> String { summary } - -// --------------------------------------------------------------------------- - -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index b6d49d22d4f..2c1fa0428f1 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -214,14 +214,14 @@ impl Renderer { msaa_samples: u32, dithering: bool, ) -> Self { - crate::profile_function!(); + profiling::function_scope!(); let shader = wgpu::ShaderModuleDescriptor { label: Some("egui"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))), }; let module = { - crate::profile_scope!("create_shader_module"); + profiling::scope!("create_shader_module"); device.create_shader_module(shader) }; @@ -236,7 +236,7 @@ impl Renderer { }); let uniform_bind_group_layout = { - crate::profile_scope!("create_bind_group_layout"); + profiling::scope!("create_bind_group_layout"); device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("egui_uniform_bind_group_layout"), entries: &[wgpu::BindGroupLayoutEntry { @@ -253,7 +253,7 @@ impl Renderer { }; let uniform_bind_group = { - crate::profile_scope!("create_bind_group"); + profiling::scope!("create_bind_group"); device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("egui_uniform_bind_group"), layout: &uniform_bind_group_layout, @@ -269,7 +269,7 @@ impl Renderer { }; let texture_bind_group_layout = { - crate::profile_scope!("create_bind_group_layout"); + profiling::scope!("create_bind_group_layout"); device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("egui_texture_bind_group_layout"), entries: &[ @@ -308,7 +308,7 @@ impl Renderer { }); let pipeline = { - crate::profile_scope!("create_render_pipeline"); + profiling::scope!("create_render_pipeline"); device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: Some("egui_pipeline"), layout: Some(&pipeline_layout), @@ -420,7 +420,7 @@ impl Renderer { paint_jobs: &[epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) { - crate::profile_function!(); + profiling::function_scope!(); let pixels_per_point = screen_descriptor.pixels_per_point; let size_in_pixels = screen_descriptor.size_in_pixels; @@ -506,7 +506,7 @@ impl Renderer { let viewport_px = info.viewport_in_pixels(); if viewport_px.width_px > 0 && viewport_px.height_px > 0 { - crate::profile_scope!("callback"); + profiling::scope!("callback"); needs_reset = true; @@ -544,7 +544,7 @@ impl Renderer { id: epaint::TextureId, image_delta: &epaint::ImageDelta, ) { - crate::profile_function!(); + profiling::function_scope!(); let width = image_delta.image.width() as u32; let height = image_delta.image.height() as u32; @@ -570,14 +570,14 @@ impl Renderer { image.pixels.len(), "Mismatch between texture size and texel count" ); - crate::profile_scope!("font -> sRGBA"); + profiling::scope!("font -> sRGBA"); Cow::Owned(image.srgba_pixels(None).collect::>()) } }; let data_bytes: &[u8] = bytemuck::cast_slice(data_color32.as_slice()); let queue_write_data_to_texture = |texture, origin| { - crate::profile_scope!("write_texture"); + profiling::scope!("write_texture"); queue.write_texture( wgpu::ImageCopyTexture { texture, @@ -631,7 +631,7 @@ impl Renderer { } else { // allocate a new texture let texture = { - crate::profile_scope!("create_texture"); + profiling::scope!("create_texture"); device.create_texture(&wgpu::TextureDescriptor { label, size, @@ -756,7 +756,7 @@ impl Renderer { texture: &wgpu::TextureView, sampler_descriptor: wgpu::SamplerDescriptor<'_>, ) -> epaint::TextureId { - crate::profile_function!(); + profiling::function_scope!(); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { compare: None, @@ -804,7 +804,7 @@ impl Renderer { sampler_descriptor: wgpu::SamplerDescriptor<'_>, id: epaint::TextureId, ) { - crate::profile_function!(); + profiling::function_scope!(); let Texture { bind_group: user_texture_binding, @@ -849,7 +849,7 @@ impl Renderer { paint_jobs: &[epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) -> Vec { - crate::profile_function!(); + profiling::function_scope!(); let screen_size_in_points = screen_descriptor.screen_size_in_points(); @@ -859,7 +859,7 @@ impl Renderer { _padding: Default::default(), }; if uniform_buffer_content != self.previous_uniform_buffer_content { - crate::profile_scope!("update uniforms"); + profiling::scope!("update uniforms"); queue.write_buffer( &self.uniform_buffer, 0, @@ -871,7 +871,7 @@ impl Renderer { // Determine how many vertices & indices need to be rendered, and gather prepare callbacks let mut callbacks = Vec::new(); let (vertex_count, index_count) = { - crate::profile_scope!("count_vertices_indices"); + profiling::scope!("count_vertices_indices"); paint_jobs.iter().fold((0, 0), |acc, clipped_primitive| { match &clipped_primitive.primitive { Primitive::Mesh(mesh) => { @@ -890,7 +890,7 @@ impl Renderer { }; if index_count > 0 { - crate::profile_scope!("indices", index_count.to_string()); + profiling::scope!("indices", index_count.to_string().as_str()); self.index_buffer.slices.clear(); @@ -928,7 +928,7 @@ impl Renderer { } } if vertex_count > 0 { - crate::profile_scope!("vertices", vertex_count.to_string()); + profiling::scope!("vertices", vertex_count.to_string().as_str()); self.vertex_buffer.slices.clear(); @@ -969,7 +969,7 @@ impl Renderer { let mut user_cmd_bufs = Vec::new(); { - crate::profile_scope!("prepare callbacks"); + profiling::scope!("prepare callbacks"); for callback in &callbacks { user_cmd_bufs.extend(callback.prepare( device, @@ -981,7 +981,7 @@ impl Renderer { } } { - crate::profile_scope!("finish prepare callbacks"); + profiling::scope!("finish prepare callbacks"); for callback in &callbacks { user_cmd_bufs.extend(callback.finish_prepare( device, @@ -1026,7 +1026,7 @@ fn create_sampler( } fn create_vertex_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { - crate::profile_function!(); + profiling::function_scope!(); device.create_buffer(&wgpu::BufferDescriptor { label: Some("egui_vertex_buffer"), usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, @@ -1036,7 +1036,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!(); + profiling::function_scope!(); 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 cf7c041f002..dea2e7fa329 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -104,7 +104,7 @@ impl Painter { render_state: &RenderState, config: &WgpuConfiguration, ) { - crate::profile_function!(); + profiling::function_scope!(); let width = surface_state.width; let height = surface_state.height; @@ -156,7 +156,7 @@ impl Painter { viewport_id: ViewportId, window: Option>, ) -> Result<(), crate::WgpuError> { - crate::profile_scope!("Painter::set_window"); // profile_function gives bad names for async functions + profiling::scope!("Painter::set_window"); // profile_function gives bad names for async functions if let Some(window) = window { let size = window.inner_size(); @@ -182,7 +182,7 @@ impl Painter { viewport_id: ViewportId, window: Option<&winit::window::Window>, ) -> Result<(), crate::WgpuError> { - crate::profile_scope!("Painter::set_window_unsafe"); // profile_function gives bad names for async functions + profiling::scope!("Painter::set_window_unsafe"); // profile_function gives bad names for async functions if let Some(window) = window { let size = window.inner_size(); @@ -273,7 +273,7 @@ impl Painter { width_in_pixels: NonZeroU32, height_in_pixels: NonZeroU32, ) { - crate::profile_function!(); + profiling::function_scope!(); let width = width_in_pixels.get(); let height = height_in_pixels.get(); @@ -344,7 +344,7 @@ impl Painter { width_in_pixels: NonZeroU32, height_in_pixels: NonZeroU32, ) { - crate::profile_function!(); + profiling::function_scope!(); if self.surfaces.contains_key(&viewport_id) { self.resize_and_generate_depth_texture_view_and_msaa_view( @@ -372,7 +372,7 @@ impl Painter { textures_delta: &epaint::textures::TexturesDelta, capture_data: Vec, ) -> f32 { - crate::profile_function!(); + profiling::function_scope!(); let capture = !capture_data.is_empty(); let mut vsync_sec = 0.0; @@ -418,7 +418,7 @@ impl Painter { }; let output_frame = { - crate::profile_scope!("get_current_texture"); + profiling::scope!("get_current_texture"); // This is what vsync-waiting happens on my Mac. let start = web_time::Instant::now(); let output_frame = surface_state.surface.get_current_texture(); @@ -514,13 +514,13 @@ impl Painter { } let encoded = { - crate::profile_scope!("CommandEncoder::finish"); + profiling::scope!("CommandEncoder::finish"); encoder.finish() }; // Submit the commands: both the main buffer and user-defined ones. { - crate::profile_scope!("Queue::submit"); + profiling::scope!("Queue::submit"); // wgpu doesn't document where vsync can happen. Maybe here? let start = web_time::Instant::now(); render_state @@ -552,7 +552,7 @@ impl Painter { } { - crate::profile_scope!("present"); + profiling::scope!("present"); // wgpu doesn't document where vsync can happen. Maybe here? let start = web_time::Instant::now(); output_frame.present(); diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 2f65548757d..c584db85e70 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -45,9 +45,6 @@ clipboard = ["arboard", "smithay-clipboard"] ## Enable opening links in a browser when an egui hyperlink is clicked. links = ["webbrowser"] -## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -puffin = ["dep:puffin", "egui/puffin"] - ## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde). serde = ["egui/serde", "dep:serde"] @@ -62,6 +59,7 @@ egui = { workspace = true, default-features = false, features = ["log"] } ahash.workspace = true log.workspace = true +profiling.workspace = true raw-window-handle.workspace = true web-time.workspace = true winit = { workspace = true, default-features = false } @@ -74,7 +72,6 @@ accesskit_winit = { version = "0.23", optional = true } ## Enable this when generating docs. document-features = { workspace = true, optional = true } -puffin = { workspace = true, optional = true } serde = { workspace = true, optional = true } webbrowser = { version = "1.0.0", optional = true } diff --git a/crates/egui-winit/src/clipboard.rs b/crates/egui-winit/src/clipboard.rs index 44e3840b64f..c4192f78d55 100644 --- a/crates/egui-winit/src/clipboard.rs +++ b/crates/egui-winit/src/clipboard.rs @@ -112,7 +112,7 @@ impl Clipboard { #[cfg(all(feature = "arboard", not(target_os = "android")))] fn init_arboard() -> Option { - crate::profile_function!(); + profiling::function_scope!(); log::trace!("Initializing arboard clipboard…"); match arboard::Clipboard::new() { @@ -139,7 +139,7 @@ fn init_smithay_clipboard( ) -> Option { #![allow(clippy::undocumented_unsafe_blocks)] - crate::profile_function!(); + profiling::function_scope!(); if let Some(RawDisplayHandle::Wayland(display)) = raw_display_handle { log::trace!("Initializing smithay clipboard…"); diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 1cb2d502c5d..50ff2d31b4b 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -25,9 +25,6 @@ pub use window_settings::WindowSettings; use ahash::HashSet; use raw_window_handle::HasDisplayHandle; -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; - use winit::{ dpi::{PhysicalPosition, PhysicalSize}, event::ElementState, @@ -121,7 +118,7 @@ impl State { theme: Option, max_texture_side: Option, ) -> Self { - crate::profile_function!(); + profiling::function_scope!(); let egui_input = egui::RawInput { focused: false, // winit will tell us when we have focus @@ -172,7 +169,7 @@ impl State { window: &Window, event_loop_proxy: winit::event_loop::EventLoopProxy, ) { - crate::profile_function!(); + profiling::function_scope!(); self.accesskit = Some(accesskit_winit::Adapter::with_event_loop_proxy( window, @@ -233,7 +230,7 @@ impl State { /// Use [`update_viewport_info`] to update the info for each /// viewport. pub fn take_egui_input(&mut self, window: &Window) -> egui::RawInput { - crate::profile_function!(); + profiling::function_scope!(); self.egui_input.time = Some(self.start_time.elapsed().as_secs_f64()); @@ -268,7 +265,7 @@ impl State { window: &Window, event: &winit::event::WindowEvent, ) -> EventResponse { - crate::profile_function!(short_window_event_description(event)); + profiling::function_scope!(short_window_event_description(event)); #[cfg(feature = "accesskit")] if let Some(accesskit) = self.accesskit.as_mut() { @@ -823,7 +820,7 @@ impl State { window: &Window, platform_output: egui::PlatformOutput, ) { - crate::profile_function!(); + profiling::function_scope!(); let egui::PlatformOutput { cursor_icon, @@ -851,7 +848,7 @@ impl State { let allow_ime = ime.is_some(); if self.allow_ime != allow_ime { self.allow_ime = allow_ime; - crate::profile_scope!("set_ime_allowed"); + profiling::scope!("set_ime_allowed"); window.set_ime_allowed(allow_ime); } @@ -862,7 +859,7 @@ impl State { || self.egui_ctx.input(|i| !i.events.is_empty()) { self.ime_rect_px = Some(ime_rect_px); - crate::profile_scope!("set_ime_cursor_area"); + profiling::scope!("set_ime_cursor_area"); window.set_ime_cursor_area( winit::dpi::PhysicalPosition { x: ime_rect_px.min.x, @@ -881,7 +878,7 @@ impl State { #[cfg(feature = "accesskit")] if let Some(accesskit) = self.accesskit.as_mut() { if let Some(update) = accesskit_update { - crate::profile_scope!("accesskit"); + profiling::scope!("accesskit"); accesskit.update_if_active(|| update); } } @@ -953,8 +950,7 @@ pub fn update_viewport_info( window: &Window, is_init: bool, ) { - crate::profile_function!(); - + profiling::function_scope!(); let pixels_per_point = pixels_per_point(egui_ctx, window); let has_a_position = match window.is_minimized() { @@ -975,7 +971,7 @@ pub fn update_viewport_info( }; let monitor_size = { - crate::profile_scope!("monitor_size"); + profiling::scope!("monitor_size"); if let Some(monitor) = window.current_monitor() { let size = monitor.size().to_logical::(pixels_per_point.into()); Some(egui::vec2(size.width, size.height)) @@ -1326,7 +1322,7 @@ fn process_viewport_command( info: &mut ViewportInfo, actions_requested: &mut HashSet, ) { - crate::profile_function!(); + profiling::function_scope!(); use winit::window::ResizeDirection; @@ -1542,7 +1538,7 @@ pub fn create_window( event_loop: &ActiveEventLoop, viewport_builder: &ViewportBuilder, ) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let window_attributes = create_winit_window_attributes(egui_ctx, event_loop, viewport_builder.clone()); @@ -1556,7 +1552,7 @@ pub fn create_winit_window_attributes( event_loop: &ActiveEventLoop, viewport_builder: ViewportBuilder, ) -> winit::window::WindowAttributes { - crate::profile_function!(); + profiling::function_scope!(); // We set sizes and positions in egui:s own ui points, which depends on the egui // zoom_factor and the native pixels per point, so we need to know that here. @@ -1752,7 +1748,7 @@ fn to_winit_icon(icon: &egui::IconData) -> Option { if icon.is_empty() { None } else { - crate::profile_function!(); + profiling::function_scope!(); match winit::window::Icon::from_rgba(icon.rgba.clone(), icon.width, icon.height) { Ok(winit_icon) => Some(winit_icon), Err(err) => { @@ -1867,30 +1863,3 @@ pub fn short_window_event_description(event: &winit::event::WindowEvent) -> &'st WindowEvent::PanGesture { .. } => "WindowEvent::PanGesture", } } - -// --------------------------------------------------------------------------- - -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} diff --git a/crates/egui-winit/src/window_settings.rs b/crates/egui-winit/src/window_settings.rs index 627d88158c0..168a086c70f 100644 --- a/crates/egui-winit/src/window_settings.rs +++ b/crates/egui-winit/src/window_settings.rs @@ -56,7 +56,7 @@ impl WindowSettings { event_loop: &winit::event_loop::ActiveEventLoop, mut viewport_builder: ViewportBuilder, ) -> ViewportBuilder { - crate::profile_function!(); + profiling::function_scope!(); // `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere // See [`winit::window::WindowBuilder::with_position`] for details. @@ -143,8 +143,7 @@ fn find_active_monitor( window_size_pts: egui::Vec2, position_px: &egui::Pos2, ) -> Option { - crate::profile_function!(); - + profiling::function_scope!(); let monitors = event_loop.available_monitors(); // default to primary monitor, in case the correct monitor was disconnected. @@ -178,7 +177,7 @@ fn clamp_pos_to_monitors( window_size_pts: egui::Vec2, position_px: &mut egui::Pos2, ) { - crate::profile_function!(); + profiling::function_scope!(); let Some(active_monitor) = find_active_monitor(egui_zoom_factor, event_loop, window_size_pts, position_px) diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index 9bb1dc0f4e4..a9408da2f6d 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -62,10 +62,6 @@ 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", "epaint/puffin"] ## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon). ## @@ -85,6 +81,7 @@ epaint = { workspace = true, default-features = false } ahash.workspace = true nohash-hasher.workspace = true +profiling.workspace = true #! ### Optional dependencies accesskit = { version = "0.17.0", optional = true } @@ -95,7 +92,6 @@ backtrace = { workspace = true, optional = true } document-features = { workspace = true, optional = true } log = { workspace = true, optional = true } -puffin = { workspace = true, optional = true } ron = { workspace = true, optional = true } serde = { workspace = true, optional = true, features = ["derive", "rc"] } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 9286f38025f..8ab83c21935 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -109,13 +109,13 @@ struct Plugins { impl Plugins { fn call(ctx: &Context, _cb_name: &str, callbacks: &[NamedContextCallback]) { - crate::profile_scope!("plugins", _cb_name); + profiling::scope!("plugins", _cb_name); for NamedContextCallback { debug_name: _name, callback, } in callbacks { - crate::profile_scope!("plugin", _name); + profiling::scope!("plugin", _name); (callback)(ctx); } } @@ -549,7 +549,7 @@ impl ContextImpl { #[cfg(feature = "accesskit")] if self.is_accesskit_enabled { - crate::profile_scope!("accesskit"); + profiling::scope!("accesskit"); use crate::pass_state::AccessKitPassState; let id = crate::accesskit_root_id(); let mut root_node = accesskit::Node::new(accesskit::Role::Window); @@ -568,8 +568,7 @@ impl ContextImpl { /// Load fonts unless already loaded. fn update_fonts_mut(&mut self) { - crate::profile_function!(); - + profiling::function_scope!(); let input = &self.viewport().input; let pixels_per_point = input.pixels_per_point(); let max_texture_side = input.max_texture_side; @@ -616,7 +615,7 @@ impl ContextImpl { log::trace!("Creating new Fonts for pixels_per_point={pixels_per_point}"); is_new = true; - crate::profile_scope!("Fonts::new"); + profiling::scope!("Fonts::new"); Fonts::new( pixels_per_point, max_texture_side, @@ -625,12 +624,12 @@ impl ContextImpl { }); { - crate::profile_scope!("Fonts::begin_pass"); + profiling::scope!("Fonts::begin_pass"); fonts.begin_pass(pixels_per_point, max_texture_side); } if is_new && self.memory.options.preload_font_glyphs { - crate::profile_scope!("preload_font_glyphs"); + profiling::scope!("preload_font_glyphs"); // Preload the most common characters for the most common fonts. // 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() { @@ -812,8 +811,7 @@ impl Context { /// ``` #[must_use] pub fn run(&self, mut new_input: RawInput, mut run_ui: impl FnMut(&Self)) -> FullOutput { - crate::profile_function!(); - + profiling::function_scope!(); let viewport_id = new_input.viewport_id; let max_passes = self.write(|ctx| ctx.memory.options.max_passes.get()); @@ -821,9 +819,13 @@ impl Context { debug_assert_eq!(output.platform_output.num_completed_passes, 0); loop { - crate::profile_scope!( + profiling::scope!( "pass", - output.platform_output.num_completed_passes.to_string() + output + .platform_output + .num_completed_passes + .to_string() + .as_str() ); // We must move the `num_passes` (back) to the viewport output so that [`Self::will_discard`] @@ -886,7 +888,7 @@ impl Context { /// // handle full_output /// ``` pub fn begin_pass(&self, new_input: RawInput) { - crate::profile_function!(); + profiling::function_scope!(); self.write(|ctx| ctx.begin_pass(new_input)); @@ -1760,7 +1762,7 @@ impl Context { /// The new fonts will become active at the start of the next pass. /// This will overwrite the existing fonts. pub fn set_fonts(&self, font_definitions: FontDefinitions) { - crate::profile_function!(); + profiling::function_scope!(); let pixels_per_point = self.pixels_per_point(); @@ -1788,7 +1790,7 @@ impl Context { /// The new font will become active at the start of the next pass. /// This will keep the existing fonts. pub fn add_font(&self, new_font: FontInsert) { - crate::profile_function!(); + profiling::function_scope!(); let pixels_per_point = self.pixels_per_point(); @@ -2152,7 +2154,7 @@ impl Context { /// Call at the end of each frame if you called [`Context::begin_pass`]. #[must_use] pub fn end_pass(&self) -> FullOutput { - crate::profile_function!(); + profiling::function_scope!(); if self.options(|o| o.zoom_with_keyboard) { crate::gui_zoom::zoom_with_keyboard(self); @@ -2342,7 +2344,7 @@ impl ContextImpl { // https://github.com/emilk/egui/issues/3664 // at the cost of a lot of performance. // (This will override any smaller delta that was uploaded above.) - crate::profile_scope!("full_font_atlas_update"); + profiling::scope!("full_font_atlas_update"); let full_delta = ImageDelta::full(fonts.image(), TextureAtlas::texture_options()); tex_mngr.set(TextureId::default(), full_delta); } @@ -2356,7 +2358,7 @@ impl ContextImpl { #[cfg(feature = "accesskit")] { - crate::profile_scope!("accesskit"); + profiling::scope!("accesskit"); let state = viewport.this_pass.accesskit_state.take(); if let Some(state) = state { let root_id = crate::accesskit_root_id().accesskit_id(); @@ -2386,7 +2388,7 @@ impl ContextImpl { let mut repaint_needed = false; if self.memory.options.repaint_on_widget_change { - crate::profile_function!("compare-widget-rects"); + profiling::scope!("compare-widget-rects"); if viewport.prev_pass.widgets != viewport.this_pass.widgets { repaint_needed = true; // Some widget has moved } @@ -2525,7 +2527,7 @@ impl Context { shapes: Vec, pixels_per_point: f32, ) -> Vec { - crate::profile_function!(); + profiling::function_scope!(); // 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 @@ -2552,7 +2554,7 @@ impl Context { let paint_stats = PaintStats::from_shapes(&shapes); let clipped_primitives = { - crate::profile_scope!("tessellator::tessellate_shapes"); + profiling::scope!("tessellator::tessellate_shapes"); tessellator::Tessellator::new( pixels_per_point, tessellation_options, @@ -3368,7 +3370,7 @@ impl Context { pub fn forget_image(&self, uri: &str) { use load::BytesLoader as _; - crate::profile_function!(); + profiling::function_scope!(); let loaders = self.loaders(); @@ -3390,7 +3392,7 @@ impl Context { pub fn forget_all_images(&self) { use load::BytesLoader as _; - crate::profile_function!(); + profiling::function_scope!(); let loaders = self.loaders(); @@ -3425,7 +3427,7 @@ impl Context { /// [not_supported]: crate::load::LoadError::NotSupported /// [custom]: crate::load::LoadError::Loading pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult { - crate::profile_function!(uri); + profiling::function_scope!(uri); let loaders = self.loaders(); let bytes_loaders = loaders.bytes.lock(); @@ -3462,7 +3464,7 @@ impl Context { /// [not_supported]: crate::load::LoadError::NotSupported /// [custom]: crate::load::LoadError::Loading pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult { - crate::profile_function!(uri); + profiling::function_scope!(uri); let loaders = self.loaders(); let image_loaders = loaders.image.lock(); @@ -3513,7 +3515,7 @@ impl Context { texture_options: TextureOptions, size_hint: load::SizeHint, ) -> load::TextureLoadResult { - crate::profile_function!(uri); + profiling::function_scope!(uri); let loaders = self.loaders(); let texture_loaders = loaders.texture.lock(); @@ -3531,7 +3533,7 @@ impl Context { /// The loaders of bytes, images, and textures. pub fn loaders(&self) -> Arc { - crate::profile_function!(); + profiling::function_scope!(); self.read(|this| this.loaders.clone()) } } @@ -3663,7 +3665,7 @@ impl Context { viewport_builder: ViewportBuilder, viewport_ui_cb: impl Fn(&Self, ViewportClass) + Send + Sync + 'static, ) { - crate::profile_function!(); + profiling::function_scope!(); if self.embed_viewports() { viewport_ui_cb(self, ViewportClass::Embedded); @@ -3715,7 +3717,7 @@ impl Context { builder: ViewportBuilder, mut viewport_ui_cb: impl FnMut(&Self, ViewportClass) -> T, ) -> T { - crate::profile_function!(); + profiling::function_scope!(); if self.embed_viewports() { return viewport_ui_cb(self, ViewportClass::Embedded); diff --git a/crates/egui/src/hit_test.rs b/crates/egui/src/hit_test.rs index fe0ce75f9c9..0d85621220e 100644 --- a/crates/egui/src/hit_test.rs +++ b/crates/egui/src/hit_test.rs @@ -39,7 +39,7 @@ pub fn hit_test( pos: Pos2, search_radius: f32, ) -> WidgetHits { - crate::profile_function!(); + profiling::function_scope!(); let search_radius_sq = search_radius * search_radius; diff --git a/crates/egui/src/input_state/mod.rs b/crates/egui/src/input_state/mod.rs index 99c166cf443..048e880e3d4 100644 --- a/crates/egui/src/input_state/mod.rs +++ b/crates/egui/src/input_state/mod.rs @@ -269,7 +269,7 @@ impl InputState { pixels_per_point: f32, options: &crate::Options, ) -> Self { - crate::profile_function!(); + profiling::function_scope!(); 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/interaction.rs b/crates/egui/src/interaction.rs index 7cbadbf1193..afac1602836 100644 --- a/crates/egui/src/interaction.rs +++ b/crates/egui/src/interaction.rs @@ -113,7 +113,7 @@ pub(crate) fn interact( input: &InputState, interaction: &mut InteractionState, ) -> InteractionSnapshot { - crate::profile_function!(); + profiling::function_scope!(); if let Some(id) = interaction.potential_click_id { if !widgets.contains(id) { diff --git a/crates/egui/src/layers.rs b/crates/egui/src/layers.rs index 1133f13a97b..81a60812ece 100644 --- a/crates/egui/src/layers.rs +++ b/crates/egui/src/layers.rs @@ -222,7 +222,7 @@ impl GraphicLayers { area_order: &[LayerId], to_global: &ahash::HashMap, ) -> Vec { - crate::profile_function!(); + profiling::function_scope!(); let mut all_shapes: Vec<_> = Default::default(); diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 8c70ca5a812..1afaada95e1 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -388,6 +388,18 @@ //! ## Installing additional fonts //! The default egui fonts only support latin and cryllic characters, and some emojis. //! To use egui with e.g. asian characters you need to install your own font (`.ttf` or `.otf`) using [`Context::set_fonts`]. +//! +//! ## Instrumentation +//! This crate supports using the [profiling](https://crates.io/crates/profiling) crate for instrumentation. +//! You can enable features on the profiling crates in your application to add instrumentation for all +//! crates that support it, including egui. See the profiling crate docs for more information. +//! ```toml +//! [dependencies] +//! profiling = "1.0" +//! [features] +//! profile-with-puffin = ["profiling/profile-with-puffin"] +//! ``` +//! #![allow(clippy::float_cmp)] #![allow(clippy::manual_range_contains)] @@ -691,33 +703,3 @@ pub fn __run_test_ui(add_contents: impl Fn(&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(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index 0a44df8cc57..2d1449c1694 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -779,7 +779,7 @@ impl Focus { impl Memory { pub(crate) fn begin_pass(&mut self, new_raw_input: &RawInput, viewports: &ViewportIdSet) { - crate::profile_function!(); + profiling::function_scope!(); self.viewport_id = new_raw_input.viewport_id; diff --git a/crates/egui/src/pass_state.rs b/crates/egui/src/pass_state.rs index da42e0932ae..5501220f769 100644 --- a/crates/egui/src/pass_state.rs +++ b/crates/egui/src/pass_state.rs @@ -248,7 +248,7 @@ impl Default for PassState { impl PassState { pub(crate) fn begin_pass(&mut self, screen_rect: Rect) { - crate::profile_function!(); + profiling::function_scope!(); let Self { used_ids, widgets, diff --git a/crates/egui/src/util/id_type_map.rs b/crates/egui/src/util/id_type_map.rs index 7e812ae5572..6e382f9917f 100644 --- a/crates/egui/src/util/id_type_map.rs +++ b/crates/egui/src/util/id_type_map.rs @@ -574,7 +574,7 @@ struct PersistedMap(Vec<(u64, SerializedElement)>); #[cfg(feature = "persistence")] impl PersistedMap { fn from_map(map: &IdTypeMap) -> Self { - crate::profile_function!(); + profiling::function_scope!(); use std::collections::BTreeMap; @@ -593,7 +593,7 @@ impl PersistedMap { let max_bytes_per_type = map.max_bytes_per_type; { - crate::profile_scope!("gather"); + profiling::scope!("gather"); for (hash, element) in &map.map { if let Some(element) = element.to_serialize() { let stats = types_map.entry(element.type_id).or_default(); @@ -610,7 +610,7 @@ impl PersistedMap { let mut persisted = vec![]; { - crate::profile_scope!("gc"); + profiling::scope!("gc"); for stats in types_map.values() { let mut bytes_written = 0; @@ -634,7 +634,7 @@ impl PersistedMap { } fn into_map(self) -> IdTypeMap { - crate::profile_function!(); + profiling::function_scope!(); let map = self .0 .into_iter() @@ -671,7 +671,7 @@ impl serde::Serialize for IdTypeMap { where S: serde::Serializer, { - crate::profile_scope!("IdTypeMap::serialize"); + profiling::scope!("IdTypeMap::serialize"); PersistedMap::from_map(self).serialize(serializer) } } @@ -682,7 +682,7 @@ impl<'de> serde::Deserialize<'de> for IdTypeMap { where D: serde::Deserializer<'de>, { - crate::profile_scope!("IdTypeMap::deserialize"); + profiling::scope!("IdTypeMap::deserialize"); ::deserialize(deserializer).map(PersistedMap::into_map) } } diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index d8b26429c77..adafeca43a8 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -188,7 +188,7 @@ impl std::fmt::Debug for IconData { impl From for epaint::ColorImage { fn from(icon: IconData) -> Self { - crate::profile_function!(); + profiling::function_scope!(); let IconData { rgba, width, @@ -200,7 +200,7 @@ impl From for epaint::ColorImage { impl From<&IconData> for epaint::ColorImage { fn from(icon: &IconData) -> Self { - crate::profile_function!(); + profiling::function_scope!(); let IconData { rgba, width, diff --git a/crates/egui_demo_app/Cargo.toml b/crates/egui_demo_app/Cargo.toml index a66b6a7587f..2a1661fcbde 100644 --- a/crates/egui_demo_app/Cargo.toml +++ b/crates/egui_demo_app/Cargo.toml @@ -8,6 +8,9 @@ rust-version.workspace = true publish = false default-run = "egui_demo_app" +[package.metadata.cargo-machete] +ignored = ["profiling"] + [lints] workspace = true @@ -33,7 +36,7 @@ persistence = [ "egui/persistence", "serde", ] -puffin = ["eframe/puffin", "dep:puffin", "dep:puffin_http"] +puffin = ["dep:puffin", "dep:puffin_http", "profiling/profile-with-puffin"] serde = ["dep:serde", "egui_demo_lib/serde", "egui/serde"] syntect = ["egui_demo_lib/syntect"] @@ -54,6 +57,7 @@ egui = { workspace = true, features = ["callstack", "default", "log"] } egui_demo_lib = { workspace = true, features = ["default", "chrono"] } egui_extras = { workspace = true, features = ["default", "image"] } log.workspace = true +profiling.workspace = true # Optional dependencies: diff --git a/crates/egui_demo_app/src/main.rs b/crates/egui_demo_app/src/main.rs index 9f42b422d04..6a2bb2da796 100644 --- a/crates/egui_demo_app/src/main.rs +++ b/crates/egui_demo_app/src/main.rs @@ -51,6 +51,7 @@ fn main() -> eframe::Result { ..Default::default() }; + eframe::run_native( "egui demo app", options, diff --git a/crates/egui_extras/Cargo.toml b/crates/egui_extras/Cargo.toml index b13a518e8d0..41fbcf0a462 100644 --- a/crates/egui_extras/Cargo.toml +++ b/crates/egui_extras/Cargo.toml @@ -53,11 +53,6 @@ http = ["dep:ehttp"] ## ``` image = ["dep:image"] -## 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", "egui/puffin"] - ## Derive serde Serialize/Deserialize on stateful structs serde = ["egui/serde", "dep:serde"] @@ -74,6 +69,7 @@ egui = { workspace = true, default-features = false } ahash.workspace = true enum-map = { version = "2", features = ["serde"] } log.workspace = true +profiling.workspace = true #! ### Optional dependencies @@ -96,7 +92,6 @@ image = { workspace = true, optional = true } # file feature mime_guess2 = { version = "2", optional = true, default-features = false } -puffin = { workspace = true, optional = true } syntect = { version = "5", optional = true, default-features = false, features = [ "default-fancy", diff --git a/crates/egui_extras/src/image.rs b/crates/egui_extras/src/image.rs index 1d2f6afa480..46d9df170a0 100644 --- a/crates/egui_extras/src/image.rs +++ b/crates/egui_extras/src/image.rs @@ -199,7 +199,7 @@ impl RetainedImage { /// On invalid image or unsupported image format. #[cfg(feature = "image")] pub fn load_image_bytes(image_bytes: &[u8]) -> Result { - crate::profile_function!(); + profiling::function_scope!(); let image = image::load_from_memory(image_bytes).map_err(|err| match err { image::ImageError::Unsupported(err) => match err.kind() { image::error::UnsupportedErrorKind::Format(format) => { @@ -245,7 +245,8 @@ pub fn load_svg_bytes_with_size( use resvg::tiny_skia::{IntSize, Pixmap}; use resvg::usvg::{Options, Tree, TreeParsing}; - crate::profile_function!(); + profiling::function_scope!(); + let opt = Options::default(); let mut rtree = Tree::from_data(svg_bytes, &opt).map_err(|err| err.to_string())?; diff --git a/crates/egui_extras/src/lib.rs b/crates/egui_extras/src/lib.rs index ab2dde735b9..6f339010224 100644 --- a/crates/egui_extras/src/lib.rs +++ b/crates/egui_extras/src/lib.rs @@ -37,36 +37,6 @@ pub use loaders::install_image_loaders; // --------------------------------------------------------------------------- -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::profile_function; - -// --------------------------------------------------------------------------- - /// Panic in debug builds, log otherwise. macro_rules! log_or_panic { ($fmt: literal) => {$crate::log_or_panic!($fmt,)}; diff --git a/crates/egui_extras/src/syntax_highlighting.rs b/crates/egui_extras/src/syntax_highlighting.rs index 9275d345b5b..027ba5ee9da 100644 --- a/crates/egui_extras/src/syntax_highlighting.rs +++ b/crates/egui_extras/src/syntax_highlighting.rs @@ -403,7 +403,7 @@ struct Highlighter { #[cfg(feature = "syntect")] impl Default for Highlighter { fn default() -> Self { - crate::profile_function!(); + profiling::function_scope!(); Self { ps: syntect::parsing::SyntaxSet::load_defaults_newlines(), ts: syntect::highlighting::ThemeSet::load_defaults(), @@ -437,8 +437,7 @@ impl Highlighter { #[cfg(feature = "syntect")] fn highlight_impl(&self, theme: &CodeTheme, text: &str, language: &str) -> Option { - crate::profile_function!(); - + profiling::function_scope!(); use syntect::easy::HighlightLines; use syntect::highlighting::FontStyle; use syntect::util::LinesWithEndings; @@ -512,7 +511,7 @@ impl Highlighter { mut text: &str, language: &str, ) -> Option { - crate::profile_function!(); + profiling::function_scope!(); let language = Language::new(language)?; diff --git a/crates/egui_glow/Cargo.toml b/crates/egui_glow/Cargo.toml index 2bb184c833b..7a402d4067a 100644 --- a/crates/egui_glow/Cargo.toml +++ b/crates/egui_glow/Cargo.toml @@ -39,9 +39,6 @@ clipboard = ["egui-winit?/clipboard"] ## enable opening links in a browser when an egui hyperlink is clicked. links = ["egui-winit?/links"] -## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. -puffin = ["dep:puffin", "egui-winit?/puffin", "egui/puffin"] - ## Enable [`winit`](https://docs.rs/winit) integration. On Linux, requires either `wayland` or `x11` winit = ["egui-winit", "dep:winit"] @@ -61,14 +58,13 @@ bytemuck.workspace = true glow.workspace = true log.workspace = true memoffset = "0.9" +profiling.workspace = true #! ### Optional dependencies ## Enable this when generating docs. document-features = { workspace = true, optional = true } # Native: -[target.'cfg(not(target_arch = "wasm32"))'.dependencies] -puffin = { workspace = true, optional = true } winit = { workspace = true, optional = true, default-features = false, features = ["rwh_06"] } # Web: diff --git a/crates/egui_glow/src/lib.rs b/crates/egui_glow/src/lib.rs index 0e1a98102af..430f1287ec2 100644 --- a/crates/egui_glow/src/lib.rs +++ b/crates/egui_glow/src/lib.rs @@ -110,33 +110,3 @@ pub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, contex } } } - -// --------------------------------------------------------------------------- - -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index 86d86bedf7c..bec46cf085f 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -144,7 +144,7 @@ impl Painter { shader_version: Option, dithering: bool, ) -> Result { - crate::profile_function!(); + profiling::function_scope!(); crate::check_for_gl_error_even_in_release!(&gl, "before Painter::new"); // some useful debug info. all three of them are present in gl 1.1. @@ -366,7 +366,7 @@ impl Painter { clipped_primitives: &[egui::ClippedPrimitive], textures_delta: &egui::TexturesDelta, ) { - crate::profile_function!(); + profiling::function_scope!(); for (id, image_delta) in &textures_delta.set { self.set_texture(*id, image_delta); @@ -405,7 +405,7 @@ impl Painter { pixels_per_point: f32, clipped_primitives: &[egui::ClippedPrimitive], ) { - crate::profile_function!(); + profiling::function_scope!(); self.assert_not_destroyed(); unsafe { self.prepare_painting(screen_size_px, pixels_per_point) }; @@ -423,7 +423,7 @@ impl Painter { } Primitive::Callback(callback) => { if callback.rect.is_positive() { - crate::profile_scope!("callback"); + profiling::scope!("callback"); let info = egui::PaintCallbackInfo { viewport: callback.rect, @@ -508,7 +508,7 @@ impl Painter { // ------------------------------------------------------------------------ pub fn set_texture(&mut self, tex_id: egui::TextureId, delta: &egui::epaint::ImageDelta) { - crate::profile_function!(); + profiling::function_scope!(); self.assert_not_destroyed(); @@ -540,7 +540,7 @@ impl Painter { ); let data: Vec = { - crate::profile_scope!("font -> sRGBA"); + profiling::scope!("font -> sRGBA"); image .srgba_pixels(None) .flat_map(|a| a.to_array()) @@ -559,7 +559,7 @@ impl Painter { options: egui::TextureOptions, data: &[u8], ) { - crate::profile_function!(); + profiling::function_scope!(); assert_eq!(data.len(), w * h * 4); assert!( w <= self.max_texture_side && h <= self.max_texture_side, @@ -610,7 +610,7 @@ impl Painter { let level = 0; if let Some([x, y]) = pos { - crate::profile_scope!("gl.tex_sub_image_2d"); + profiling::scope!("gl.tex_sub_image_2d"); self.gl.tex_sub_image_2d( glow::TEXTURE_2D, level, @@ -625,7 +625,7 @@ impl Painter { check_for_gl_error!(&self.gl, "tex_sub_image_2d"); } else { let border = 0; - crate::profile_scope!("gl.tex_image_2d"); + profiling::scope!("gl.tex_image_2d"); self.gl.tex_image_2d( glow::TEXTURE_2D, level, @@ -675,7 +675,7 @@ impl Painter { } pub fn read_screen_rgba(&self, [w, h]: [u32; 2]) -> egui::ColorImage { - crate::profile_function!(); + profiling::function_scope!(); let mut pixels = vec![0_u8; (w * h * 4) as usize]; unsafe { @@ -700,8 +700,7 @@ impl Painter { } pub fn read_screen_rgb(&self, [w, h]: [u32; 2]) -> Vec { - crate::profile_function!(); - + profiling::function_scope!(); let mut pixels = vec![0_u8; (w * h * 3) as usize]; unsafe { self.gl.read_pixels( @@ -748,7 +747,7 @@ impl Painter { } pub fn clear(gl: &glow::Context, screen_size_in_pixels: [u32; 2], clear_color: [f32; 4]) { - crate::profile_function!(); + profiling::function_scope!(); unsafe { gl.disable(glow::SCISSOR_TEST); diff --git a/crates/epaint/Cargo.toml b/crates/epaint/Cargo.toml index 7815189dc5a..0a4afde26b5 100644 --- a/crates/epaint/Cargo.toml +++ b/crates/epaint/Cargo.toml @@ -55,11 +55,6 @@ log = ["dep:log"] ## [`mint`](https://docs.rs/mint) enables interoperability with other math libraries such as [`glam`](https://docs.rs/glam) and [`nalgebra`](https://docs.rs/nalgebra). mint = ["emath/mint"] -## 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"] - ## Enable parallel tessellation using [`rayon`](https://docs.rs/rayon). ## ## This can help performance for graphics-intense applications. @@ -79,6 +74,7 @@ ab_glyph = "0.2.11" ahash.workspace = true nohash-hasher.workspace = true parking_lot.workspace = true # Using parking_lot over std::sync::Mutex gives 50% speedups in some real-world scenarios. +profiling = { workspace = true} #! ### Optional dependencies bytemuck = { workspace = true, optional = true, features = ["derive"] } @@ -87,7 +83,6 @@ bytemuck = { workspace = true, optional = true, features = ["derive"] } document-features = { workspace = true, optional = true } log = { workspace = true, optional = true } -puffin = { workspace = true, optional = true } rayon = { version = "1.7", optional = true } ## Allow serialization using [`serde`](https://docs.rs/serde) . diff --git a/crates/epaint/src/lib.rs b/crates/epaint/src/lib.rs index 4dee42381cd..89664cdb844 100644 --- a/crates/epaint/src/lib.rs +++ b/crates/epaint/src/lib.rs @@ -143,33 +143,3 @@ pub enum Primitive { /// Was epaint compiled with the `rayon` feature? pub const HAS_RAYON: bool = cfg!(feature = "rayon"); - -// --------------------------------------------------------------------------- - -mod profiling_scopes { - #![allow(unused_macros)] - #![allow(unused_imports)] - - /// Profiling macro for feature "puffin" - macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. - 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"))] // Disabled on web because of the coarse 1ms clock resolution there. - puffin::profile_scope!($($arg)*); - }; - } - pub(crate) use profile_scope; -} - -#[allow(unused_imports)] -pub(crate) use profiling_scopes::{profile_function, profile_scope}; diff --git a/crates/epaint/src/mesh.rs b/crates/epaint/src/mesh.rs index ba37e715856..2447cad293e 100644 --- a/crates/epaint/src/mesh.rs +++ b/crates/epaint/src/mesh.rs @@ -85,7 +85,7 @@ impl Mesh { /// Are all indices within the bounds of the contained vertices? pub fn is_valid(&self) -> bool { - crate::profile_function!(); + profiling::function_scope!(); if let Ok(n) = u32::try_from(self.vertices.len()) { self.indices.iter().all(|&i| i < n) @@ -111,7 +111,7 @@ impl Mesh { /// /// Panics when `other` mesh has a different texture. pub fn append(&mut self, other: Self) { - crate::profile_function!(); + profiling::function_scope!(); debug_assert!(other.is_valid()); if self.is_empty() { diff --git a/crates/epaint/src/tessellator.rs b/crates/epaint/src/tessellator.rs index fdbe270914e..9821e4021e1 100644 --- a/crates/epaint/src/tessellator.rs +++ b/crates/epaint/src/tessellator.rs @@ -1363,7 +1363,7 @@ impl Tessellator { self.tessellate_ellipse(ellipse, out); } Shape::Mesh(mesh) => { - crate::profile_scope!("mesh"); + profiling::scope!("mesh"); if self.options.validate_meshes && !mesh.is_valid() { debug_assert!(false, "Invalid Mesh in Shape::Mesh"); @@ -1596,7 +1596,7 @@ impl Tessellator { return; } - crate::profile_function!(); + profiling::function_scope!(); let PathShape { points, @@ -1977,7 +1977,7 @@ impl Tessellator { /// A list of clip rectangles with matching [`Mesh`]. #[allow(unused_mut)] pub fn tessellate_shapes(&mut self, mut shapes: Vec) -> Vec { - crate::profile_function!(); + profiling::function_scope!(); #[cfg(feature = "rayon")] if self.options.parallel_tessellation { @@ -1987,7 +1987,7 @@ impl Tessellator { let mut clipped_primitives: Vec = Vec::default(); { - crate::profile_scope!("tessellate"); + profiling::scope!("tessellate"); for clipped_shape in shapes { self.tessellate_clipped_shape(clipped_shape, &mut clipped_primitives); } @@ -2024,7 +2024,7 @@ impl Tessellator { /// then replace the original shape with their tessellated meshes. #[cfg(feature = "rayon")] fn parallel_tessellation_of_large_shapes(&self, shapes: &mut [ClippedShape]) { - crate::profile_function!(); + profiling::function_scope!(); use rayon::prelude::*; @@ -2054,7 +2054,7 @@ impl Tessellator { .enumerate() .filter(|(_, clipped_shape)| should_parallelize(&clipped_shape.shape)) .map(|(index, clipped_shape)| { - crate::profile_scope!("tessellate_big_shape"); + profiling::scope!("tessellate_big_shape"); // TODO(emilk): reuse tessellator in a thread local let mut tessellator = (*self).clone(); let mut mesh = Mesh::default(); @@ -2063,7 +2063,7 @@ impl Tessellator { }) .collect(); - crate::profile_scope!("distribute results", tessellated.len().to_string()); + profiling::scope!("distribute results", tessellated.len().to_string()); for (index, mesh) in tessellated { shapes[index].shape = Shape::Mesh(mesh); } diff --git a/examples/puffin_profiler/Cargo.toml b/examples/puffin_profiler/Cargo.toml index 3389de5c928..d0e9e485a8d 100644 --- a/examples/puffin_profiler/Cargo.toml +++ b/examples/puffin_profiler/Cargo.toml @@ -7,6 +7,9 @@ edition = "2021" rust-version = "1.80" publish = false +[package.metadata.cargo-machete] +ignored = ["profiling"] + [lints] workspace = true @@ -18,7 +21,6 @@ wgpu = ["eframe/wgpu"] [dependencies] eframe = { workspace = true, features = [ "default", - "puffin", "__screenshot", # __screenshot is so we can dump a screenshot using EFRAME_SCREENSHOT_TO ] } env_logger = { version = "0.10", default-features = false, features = [ @@ -28,3 +30,4 @@ env_logger = { version = "0.10", default-features = false, features = [ log = { workspace = true } puffin = "0.19" puffin_http = "0.16" +profiling = {workspace = true, features = ["profile-with-puffin"] }