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"] }