diff --git a/crates/eframe/src/epi/mod.rs b/crates/eframe/src/epi/mod.rs index 597a9122..d45b39cf 100644 --- a/crates/eframe/src/epi/mod.rs +++ b/crates/eframe/src/epi/mod.rs @@ -1158,6 +1158,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); storage .get_string(key) .and_then(|value| match ron::from_str(&value) { @@ -1172,6 +1173,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); 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/lib.rs b/crates/eframe/src/lib.rs index 2972d672..4ceae307 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -324,7 +324,6 @@ pub type Result = std::result::Result; // --------------------------------------------------------------------------- -#[cfg(not(target_arch = "wasm32"))] mod profiling_scopes { #![allow(unused_macros)] #![allow(unused_imports)] @@ -333,6 +332,7 @@ mod profiling_scopes { 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)*); }; } @@ -342,11 +342,12 @@ mod profiling_scopes { 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; } -#[cfg(not(target_arch = "wasm32"))] +#[allow(unused_imports)] pub(crate) use profiling_scopes::*; diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 1204521d..519972cc 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -622,6 +622,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!(); #[cfg(feature = "persistence")] { epi::get_value(_storage?, STORAGE_WINDOW_KEY) @@ -631,6 +632,7 @@ pub fn load_window_settings(_storage: Option<&dyn epi::Storage>) -> Option) -> Option { + crate::profile_function!(); #[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 eae742cf..1b4c254c 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -31,6 +31,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"); join_handle.join().ok(); } } @@ -39,6 +40,7 @@ impl Drop for FileStorage { impl FileStorage { /// Store the state in this .ron file. fn from_ron_filepath(ron_filepath: impl Into) -> Self { + crate::profile_function!(); let ron_filepath: PathBuf = ron_filepath.into(); log::debug!("Loading app state from {:?}…", ron_filepath); Self { @@ -51,6 +53,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); if let Some(data_dir) = storage_dir(app_id) { if let Err(err) = std::fs::create_dir_all(&data_dir) { log::warn!( @@ -83,6 +86,7 @@ impl crate::Storage for FileStorage { fn flush(&mut self) { if self.dirty { + crate::profile_function!(); self.dirty = false; let file_path = self.ron_filepath.clone(); @@ -142,6 +146,7 @@ fn read_ron(ron_path: impl AsRef) -> Option where T: serde::de::DeserializeOwned, { + crate::profile_function!(); match std::fs::File::open(ron_path) { Ok(file) => { let reader = std::io::BufReader::new(file); diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 986a86d3..278ac6eb 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -92,6 +92,7 @@ trait WinitApp { fn create_event_loop_builder( native_options: &mut epi::NativeOptions, ) -> EventLoopBuilder { + crate::profile_function!(); let mut event_loop_builder = winit::event_loop::EventLoopBuilder::with_user_event(); if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) { @@ -101,6 +102,14 @@ fn create_event_loop_builder( event_loop_builder } +fn create_event_loop(native_options: &mut epi::NativeOptions) -> EventLoop { + crate::profile_function!(); + let mut builder = create_event_loop_builder(native_options); + + crate::profile_scope!("EventLoopBuilder::build"); + builder.build() +} + /// Access a thread-local event loop. /// /// We reuse the event-loop so we can support closing and opening an eframe window @@ -117,8 +126,7 @@ fn with_event_loop( // do that as part of the lazy thread local storage initialization and so we instead // create the event loop lazily here let mut event_loop = event_loop.borrow_mut(); - let event_loop = event_loop - .get_or_insert_with(|| create_event_loop_builder(&mut native_options).build()); + let event_loop = event_loop.get_or_insert_with(|| create_event_loop(&mut native_options)); f(event_loop, native_options) }) } @@ -137,6 +145,8 @@ fn run_and_return( let mut returned_result = Ok(()); event_loop.run_return(|event, event_loop, control_flow| { + crate::profile_scope!("winit_event", short_event_description(&event)); + let event_result = match &event { winit::event::Event::LoopDestroyed => { // On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name), @@ -266,6 +276,8 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + let mut next_repaint_time = Instant::now(); event_loop.run(move |event, event_loop, control_flow| { + crate::profile_scope!("winit_event", short_event_description(&event)); + let event_result = match event { winit::event::Event::LoopDestroyed => { log::debug!("Received Event::LoopDestroyed"); @@ -420,6 +432,8 @@ mod glow_integration { native_options: &epi::NativeOptions, event_loop: &EventLoopWindowTarget, ) -> Result { + crate::profile_function!(); + use glutin::prelude::*; // convert native options to glutin options let hardware_acceleration = match native_options.hardware_acceleration { @@ -460,26 +474,35 @@ mod glow_integration { "trying to create glutin Display with config: {:?}", &config_template_builder ); - // create gl display. this may probably create a window too on most platforms. definitely on `MS windows`. never on android. - let (window, gl_config) = glutin_winit::DisplayBuilder::new() + + // Create GL display. This may probably create a window too on most platforms. Definitely on `MS windows`. Never on Android. + let display_builder = glutin_winit::DisplayBuilder::new() // we might want to expose this option to users in the future. maybe using an env var or using native_options. .with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150 - .with_window_builder(Some(winit_window_builder.clone())) - .build( - event_loop, - config_template_builder.clone(), - |mut config_iterator| { - let config = config_iterator.next().expect( + .with_window_builder(Some(winit_window_builder.clone())); + + let (window, gl_config) = { + crate::profile_scope!("DisplayBuilder::build"); + + display_builder + .build( + event_loop, + config_template_builder.clone(), + |mut config_iterator| { + let config = config_iterator.next().expect( "failed to find a matching configuration for creating glutin config", ); - log::debug!( - "using the first config from config picker closure. config: {:?}", - &config - ); - config - }, - ) - .map_err(|e| crate::Error::NoGlutinConfigs(config_template_builder.build(), e))?; + log::debug!( + "using the first config from config picker closure. config: {:?}", + &config + ); + config + }, + ) + .map_err(|e| { + crate::Error::NoGlutinConfigs(config_template_builder.build(), e) + })? + }; let gl_display = gl_config.display(); log::debug!( @@ -499,10 +522,15 @@ mod glow_integration { let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new() .with_context_api(glutin::context::ContextApi::Gles(None)) .build(raw_window_handle); - let gl_context = match gl_config - .display() - .create_context(&gl_config, &context_attributes) - { + + let gl_context_result = { + crate::profile_scope!("create_context"); + gl_config + .display() + .create_context(&gl_config, &context_attributes) + }; + + let gl_context = match gl_context_result { Ok(it) => it, Err(err) => { log::warn!("failed to create context using default context attributes {context_attributes:?} due to error: {err}"); @@ -656,6 +684,7 @@ mod glow_integration { native_options: epi::NativeOptions, app_creator: epi::AppCreator, ) -> Self { + crate::profile_function!(); Self { repaint_proxy: Arc::new(egui::mutex::Mutex::new(event_loop.create_proxy())), app_name: app_name.to_owned(), @@ -693,6 +722,7 @@ mod glow_integration { } let gl = unsafe { + crate::profile_scope!("glow::Context::from_loader_function"); 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"); @@ -705,6 +735,7 @@ mod glow_integration { } fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget) -> Result<()> { + crate::profile_function!(); let storage = epi_integration::create_storage( self.native_options .app_id @@ -814,6 +845,7 @@ mod glow_integration { fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { + crate::profile_function!(); running .integration .save(running.app.as_mut(), running.gl_window.window.as_ref()); @@ -823,125 +855,125 @@ mod glow_integration { } fn run_ui_and_paint(&mut self) -> EventResult { - if let Some(running) = &mut self.running { - if running.gl_window.window.is_none() { - return EventResult::Wait; - } + let Some(running) = &mut self.running else { + return EventResult::Wait; + }; - #[cfg(feature = "puffin")] - puffin::GlobalProfiler::lock().new_frame(); - crate::profile_scope!("frame"); + if running.gl_window.window.is_none() { + return EventResult::Wait; + } - let GlowWinitRunning { - gl_window, - gl, - app, - integration, - painter, - } = running; + #[cfg(feature = "puffin")] + puffin::GlobalProfiler::lock().new_frame(); + crate::profile_scope!("frame"); - let window = gl_window.window(); + let GlowWinitRunning { + gl_window, + gl, + app, + integration, + painter, + } = running; - let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); + let window = gl_window.window(); - egui_glow::painter::clear( - gl, - screen_size_in_pixels, - app.clear_color(&integration.egui_ctx.style().visuals), - ); + let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); - let egui::FullOutput { - platform_output, - repaint_after, - textures_delta, - shapes, - } = integration.update(app.as_mut(), window); + egui_glow::painter::clear( + gl, + screen_size_in_pixels, + app.clear_color(&integration.egui_ctx.style().visuals), + ); - integration.handle_platform_output(window, platform_output); + let egui::FullOutput { + platform_output, + repaint_after, + textures_delta, + shapes, + } = integration.update(app.as_mut(), window); - let clipped_primitives = { - crate::profile_scope!("tessellate"); - integration.egui_ctx.tessellate(shapes) - }; + integration.handle_platform_output(window, platform_output); - painter.paint_and_update_textures( - screen_size_in_pixels, - integration.egui_ctx.pixels_per_point(), - &clipped_primitives, - &textures_delta, - ); + let clipped_primitives = { + crate::profile_scope!("tessellate"); + integration.egui_ctx.tessellate(shapes) + }; - let screenshot_requested = &mut integration.frame.output.screenshot_requested; + painter.paint_and_update_textures( + screen_size_in_pixels, + integration.egui_ctx.pixels_per_point(), + &clipped_primitives, + &textures_delta, + ); - if *screenshot_requested { - *screenshot_requested = false; + let screenshot_requested = &mut integration.frame.output.screenshot_requested; + + if *screenshot_requested { + *screenshot_requested = false; + let screenshot = painter.read_screen_rgba(screen_size_in_pixels); + integration.frame.screenshot.set(Some(screenshot)); + } + + integration.post_rendering(app.as_mut(), window); + + { + crate::profile_scope!("swap_buffers"); + gl_window.swap_buffers().unwrap(); + } + + integration.post_present(window); + + #[cfg(feature = "__screenshot")] + // give it time to settle: + if integration.egui_ctx.frame_nr() == 2 { + if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") { + assert!( + path.ends_with(".png"), + "Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}" + ); let screenshot = painter.read_screen_rgba(screen_size_in_pixels); - integration.frame.screenshot.set(Some(screenshot)); + image::save_buffer( + &path, + screenshot.as_raw(), + screenshot.width() as u32, + screenshot.height() as u32, + image::ColorType::Rgba8, + ) + .unwrap_or_else(|err| { + panic!("Failed to save screenshot to {path:?}: {err}"); + }); + eprintln!("Screenshot saved to {path:?}."); + std::process::exit(0); } + } - integration.post_rendering(app.as_mut(), window); - - { - crate::profile_scope!("swap_buffers"); - gl_window.swap_buffers().unwrap(); - } - - integration.post_present(window); - - #[cfg(feature = "__screenshot")] - // give it time to settle: - if integration.egui_ctx.frame_nr() == 2 { - if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") { - assert!( - path.ends_with(".png"), - "Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}" - ); - let screenshot = painter.read_screen_rgba(screen_size_in_pixels); - image::save_buffer( - &path, - screenshot.as_raw(), - screenshot.width() as u32, - screenshot.height() as u32, - image::ColorType::Rgba8, - ) - .unwrap_or_else(|err| { - panic!("Failed to save screenshot to {path:?}: {err}"); - }); - eprintln!("Screenshot saved to {path:?}."); - std::process::exit(0); - } - } - - let control_flow = if integration.should_close() { - EventResult::Exit - } else if repaint_after.is_zero() { - EventResult::RepaintNext - } else if let Some(repaint_after_instant) = - std::time::Instant::now().checked_add(repaint_after) - { - // if repaint_after is something huge and can't be added to Instant, - // we will use `ControlFlow::Wait` instead. - // technically, this might lead to some weird corner cases where the user *WANTS* - // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own - // egui backend impl i guess. - EventResult::RepaintAt(repaint_after_instant) - } else { - EventResult::Wait - }; - - integration.maybe_autosave(app.as_mut(), window); - - 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!("bg_sleep"); - std::thread::sleep(std::time::Duration::from_millis(10)); - } - - control_flow + let control_flow = if integration.should_close() { + EventResult::Exit + } else if repaint_after.is_zero() { + EventResult::RepaintNext + } else if let Some(repaint_after_instant) = + std::time::Instant::now().checked_add(repaint_after) + { + // if repaint_after is something huge and can't be added to Instant, + // we will use `ControlFlow::Wait` instead. + // technically, this might lead to some weird corner cases where the user *WANTS* + // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own + // egui backend impl i guess. + EventResult::RepaintAt(repaint_after_instant) } else { EventResult::Wait + }; + + integration.maybe_autosave(app.as_mut(), window); + + 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!("bg_sleep"); + std::thread::sleep(std::time::Duration::from_millis(10)); } + + control_flow } fn on_event( @@ -1070,14 +1102,14 @@ mod glow_integration { run_and_return(event_loop, glow_eframe) }) } else { - let event_loop = create_event_loop_builder(&mut native_options).build(); + let event_loop = create_event_loop(&mut native_options); let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, glow_eframe); } #[cfg(target_os = "ios")] { - let event_loop = create_event_loop_builder(&mut native_options).build(); + let event_loop = create_event_loop(&mut native_options); let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, glow_eframe); } @@ -1125,6 +1157,7 @@ mod wgpu_integration { native_options: epi::NativeOptions, app_creator: epi::AppCreator, ) -> Self { + crate::profile_function!(); #[cfg(feature = "__screenshot")] assert!( std::env::var("EFRAME_SCREENSHOT_TO").is_err(), @@ -1148,6 +1181,7 @@ mod wgpu_integration { title: &str, native_options: &NativeOptions, ) -> std::result::Result { + crate::profile_function!(); let window_settings = epi_integration::load_window_settings(storage); let window_builder = epi_integration::window_builder(event_loop, title, native_options, window_settings); @@ -1167,6 +1201,7 @@ mod wgpu_integration { ) -> std::result::Result<(), egui_wgpu::WgpuError> { self.window = Some(window); if let Some(running) = &mut self.running { + crate::profile_function!(); pollster::block_on(running.painter.set_window(self.window.as_ref()))?; } Ok(()) @@ -1188,6 +1223,8 @@ mod wgpu_integration { storage: Option>, window: winit::window::Window, ) -> std::result::Result<(), egui_wgpu::WgpuError> { + crate::profile_function!(); + #[allow(unsafe_code, unused_mut, unused_unsafe)] let mut painter = egui_wgpu::winit::Painter::new( self.native_options.wgpu_options.clone(), @@ -1288,6 +1325,7 @@ mod wgpu_integration { fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { + crate::profile_function!(); running .integration .save(running.app.as_mut(), self.window.as_ref()); @@ -1303,76 +1341,76 @@ mod wgpu_integration { } fn run_ui_and_paint(&mut self) -> EventResult { - if let (Some(running), Some(window)) = (&mut self.running, &self.window) { - #[cfg(feature = "puffin")] - puffin::GlobalProfiler::lock().new_frame(); - crate::profile_scope!("frame"); + let (Some(running), Some(window)) = (&mut self.running, &self.window) else { + return EventResult::Wait; + }; - let WgpuWinitRunning { - app, - integration, - painter, - } = running; + #[cfg(feature = "puffin")] + puffin::GlobalProfiler::lock().new_frame(); + crate::profile_scope!("frame"); - let egui::FullOutput { - platform_output, - repaint_after, - textures_delta, - shapes, - } = integration.update(app.as_mut(), window); + let WgpuWinitRunning { + app, + integration, + painter, + } = running; - integration.handle_platform_output(window, platform_output); + let egui::FullOutput { + platform_output, + repaint_after, + textures_delta, + shapes, + } = integration.update(app.as_mut(), window); - let clipped_primitives = { - crate::profile_scope!("tessellate"); - integration.egui_ctx.tessellate(shapes) - }; + integration.handle_platform_output(window, platform_output); - let screenshot_requested = &mut integration.frame.output.screenshot_requested; + let clipped_primitives = { + crate::profile_scope!("tessellate"); + integration.egui_ctx.tessellate(shapes) + }; - let screenshot = painter.paint_and_update_textures( - integration.egui_ctx.pixels_per_point(), - app.clear_color(&integration.egui_ctx.style().visuals), - &clipped_primitives, - &textures_delta, - *screenshot_requested, - ); - *screenshot_requested = false; - integration.frame.screenshot.set(screenshot); + let screenshot_requested = &mut integration.frame.output.screenshot_requested; - integration.post_rendering(app.as_mut(), window); - integration.post_present(window); + let screenshot = painter.paint_and_update_textures( + integration.egui_ctx.pixels_per_point(), + app.clear_color(&integration.egui_ctx.style().visuals), + &clipped_primitives, + &textures_delta, + *screenshot_requested, + ); + *screenshot_requested = false; + integration.frame.screenshot.set(screenshot); - let control_flow = if integration.should_close() { - EventResult::Exit - } else if repaint_after.is_zero() { - EventResult::RepaintNext - } else if let Some(repaint_after_instant) = - std::time::Instant::now().checked_add(repaint_after) - { - // if repaint_after is something huge and can't be added to Instant, - // we will use `ControlFlow::Wait` instead. - // technically, this might lead to some weird corner cases where the user *WANTS* - // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own - // egui backend impl i guess. - EventResult::RepaintAt(repaint_after_instant) - } else { - EventResult::Wait - }; + integration.post_rendering(app.as_mut(), window); + integration.post_present(window); - integration.maybe_autosave(app.as_mut(), window); - - 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!("bg_sleep"); - std::thread::sleep(std::time::Duration::from_millis(10)); - } - - control_flow + let control_flow = if integration.should_close() { + EventResult::Exit + } else if repaint_after.is_zero() { + EventResult::RepaintNext + } else if let Some(repaint_after_instant) = + std::time::Instant::now().checked_add(repaint_after) + { + // if repaint_after is something huge and can't be added to Instant, + // we will use `ControlFlow::Wait` instead. + // technically, this might lead to some weird corner cases where the user *WANTS* + // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own + // egui backend impl i guess. + EventResult::RepaintAt(repaint_after_instant) } else { EventResult::Wait + }; + + integration.maybe_autosave(app.as_mut(), window); + + 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!("bg_sleep"); + std::thread::sleep(std::time::Duration::from_millis(10)); } + + control_flow } fn on_event( @@ -1517,14 +1555,14 @@ mod wgpu_integration { run_and_return(event_loop, wgpu_eframe) }) } else { - let event_loop = create_event_loop_builder(&mut native_options).build(); + let event_loop = create_event_loop(&mut native_options); let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, wgpu_eframe); } #[cfg(target_os = "ios")] { - let event_loop = create_event_loop_builder(&mut native_options).build(); + let event_loop = create_event_loop(&mut native_options); let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, wgpu_eframe); } @@ -1551,3 +1589,67 @@ fn system_theme(window: &winit::window::Window, options: &NativeOptions) -> Opti fn extremely_far_future() -> std::time::Instant { std::time::Instant::now() + std::time::Duration::from_secs(10_000_000_000) } + +// For the puffin profiler! +#[allow(dead_code)] // Only used for profiling +fn short_event_description(event: &winit::event::Event<'_, UserEvent>) -> &'static str { + use winit::event::{DeviceEvent, Event, StartCause, WindowEvent}; + + match event { + Event::Suspended => "Event::Suspended", + Event::Resumed => "Event::Resumed", + Event::MainEventsCleared => "Event::MainEventsCleared", + Event::RedrawRequested(_) => "Event::RedrawRequested", + Event::RedrawEventsCleared => "Event::RedrawEventsCleared", + Event::LoopDestroyed => "Event::LoopDestroyed", + Event::UserEvent(user_event) => match user_event { + UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", + #[cfg(feature = "accesskit")] + UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", + }, + Event::DeviceEvent { event, .. } => match event { + DeviceEvent::Added { .. } => "DeviceEvent::Added", + DeviceEvent::Removed { .. } => "DeviceEvent::Removed", + DeviceEvent::MouseMotion { .. } => "DeviceEvent::MouseMotion", + DeviceEvent::MouseWheel { .. } => "DeviceEvent::MouseWheel", + DeviceEvent::Motion { .. } => "DeviceEvent::Motion", + DeviceEvent::Button { .. } => "DeviceEvent::Button", + DeviceEvent::Key { .. } => "DeviceEvent::Key", + DeviceEvent::Text { .. } => "DeviceEvent::Text", + }, + Event::NewEvents(start_cause) => match start_cause { + StartCause::ResumeTimeReached { .. } => "NewEvents::ResumeTimeReached", + StartCause::WaitCancelled { .. } => "NewEvents::WaitCancelled", + StartCause::Poll => "NewEvents::Poll", + StartCause::Init => "NewEvents::Init", + }, + Event::WindowEvent { event, .. } => match event { + WindowEvent::Resized { .. } => "WindowEvent::Resized", + WindowEvent::Moved { .. } => "WindowEvent::Moved", + WindowEvent::CloseRequested { .. } => "WindowEvent::CloseRequested", + WindowEvent::Destroyed { .. } => "WindowEvent::Destroyed", + WindowEvent::DroppedFile { .. } => "WindowEvent::DroppedFile", + WindowEvent::HoveredFile { .. } => "WindowEvent::HoveredFile", + WindowEvent::HoveredFileCancelled { .. } => "WindowEvent::HoveredFileCancelled", + WindowEvent::ReceivedCharacter { .. } => "WindowEvent::ReceivedCharacter", + WindowEvent::Focused { .. } => "WindowEvent::Focused", + WindowEvent::KeyboardInput { .. } => "WindowEvent::KeyboardInput", + WindowEvent::ModifiersChanged { .. } => "WindowEvent::ModifiersChanged", + WindowEvent::Ime { .. } => "WindowEvent::Ime", + WindowEvent::CursorMoved { .. } => "WindowEvent::CursorMoved", + WindowEvent::CursorEntered { .. } => "WindowEvent::CursorEntered", + WindowEvent::CursorLeft { .. } => "WindowEvent::CursorLeft", + WindowEvent::MouseWheel { .. } => "WindowEvent::MouseWheel", + WindowEvent::MouseInput { .. } => "WindowEvent::MouseInput", + WindowEvent::TouchpadMagnify { .. } => "WindowEvent::TouchpadMagnify", + WindowEvent::SmartMagnify { .. } => "WindowEvent::SmartMagnify", + WindowEvent::TouchpadRotate { .. } => "WindowEvent::TouchpadRotate", + WindowEvent::TouchpadPressure { .. } => "WindowEvent::TouchpadPressure", + WindowEvent::AxisMotion { .. } => "WindowEvent::AxisMotion", + WindowEvent::Touch { .. } => "WindowEvent::Touch", + WindowEvent::ScaleFactorChanged { .. } => "WindowEvent::ScaleFactorChanged", + WindowEvent::ThemeChanged { .. } => "WindowEvent::ThemeChanged", + WindowEvent::Occluded { .. } => "WindowEvent::Occluded", + }, + } +} diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index 7d0a3506..8d9d5912 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -203,22 +203,30 @@ pub fn depth_format_from_bits(depth_buffer: u8, stencil_buffer: u8) -> Option { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] - puffin::profile_function!($($arg)*); - }; -} -pub(crate) use profile_function; +mod profiling_scopes { + #![allow(unused_macros)] + #![allow(unused_imports)] -/// Profiling macro for feature "puffin" -macro_rules! profile_scope { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] - puffin::profile_scope!($($arg)*); - }; + /// 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; } -pub(crate) use profile_scope; + +#[allow(unused_imports)] +pub(crate) use profiling_scopes::*; diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 00500af1..c01336f3 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -137,6 +137,7 @@ impl Painter { render_state: &RenderState, present_mode: wgpu::PresentMode, ) { + crate::profile_function!(); let usage = if surface_state.supports_screenshot { wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST } else { @@ -181,6 +182,7 @@ impl Painter { &mut self, window: Option<&winit::window::Window>, ) -> Result<(), crate::WgpuError> { + crate::profile_function!(); match window { Some(window) => { let surface = unsafe { self.instance.create_surface(&window)? }; @@ -254,6 +256,7 @@ impl Painter { width_in_pixels: u32, height_in_pixels: u32, ) { + crate::profile_function!(); let render_state = self.render_state.as_ref().unwrap(); let surface_state = self.surface_state.as_mut().unwrap(); @@ -309,6 +312,7 @@ impl Painter { } pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) { + crate::profile_function!(); if self.surface_state.is_some() { self.resize_and_generate_depth_texture_view_and_msaa_view( width_in_pixels, diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 8da31e2c..28303519 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -894,26 +894,30 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option { - #[cfg(feature = "puffin")] - puffin::profile_function!($($arg)*); - }; +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 profile_function; - -/// Profiling macro for feature "puffin" -#[allow(unused_macros)] -macro_rules! profile_scope { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - puffin::profile_scope!($($arg)*); - }; -} - -#[allow(unused_imports)] -pub(crate) use profile_scope; +pub(crate) use profiling_scopes::*; diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 65f0aab7..0d669e70 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -640,8 +640,8 @@ mod profiling_scopes { /// Profiling macro for feature "puffin" macro_rules! profile_function { ($($arg: tt)*) => { - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. #[cfg(feature = "puffin")] + #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. puffin::profile_function!($($arg)*); }; } @@ -650,12 +650,13 @@ mod profiling_scopes { /// Profiling macro for feature "puffin" macro_rules! profile_scope { ($($arg: tt)*) => { - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. #[cfg(feature = "puffin")] + #[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::*; diff --git a/crates/egui_demo_app/src/apps/image_viewer.rs b/crates/egui_demo_app/src/apps/image_viewer.rs index 8bae74f0..f1929b91 100644 --- a/crates/egui_demo_app/src/apps/image_viewer.rs +++ b/crates/egui_demo_app/src/apps/image_viewer.rs @@ -143,7 +143,9 @@ impl eframe::App for ImageViewer { if !matches!(self.fit, ImageFit::Exact(_)) { self.fit = ImageFit::Exact(Vec2::splat(128.0)); } - let ImageFit::Exact(size) = &mut self.fit else { unreachable!() }; + let ImageFit::Exact(size) = &mut self.fit else { + unreachable!() + }; ui.add(Slider::new(&mut size.x, 0.0..=2048.0).text("width")); ui.add(Slider::new(&mut size.y, 0.0..=2048.0).text("height")); } @@ -151,7 +153,9 @@ impl eframe::App for ImageViewer { if !matches!(self.fit, ImageFit::Fraction(_)) { self.fit = ImageFit::Fraction(Vec2::splat(1.0)); } - let ImageFit::Fraction(fract) = &mut self.fit else { unreachable!() }; + let ImageFit::Fraction(fract) = &mut self.fit else { + unreachable!() + }; ui.add(Slider::new(&mut fract.x, 0.0..=1.0).text("width")); ui.add(Slider::new(&mut fract.y, 0.0..=1.0).text("height")); } @@ -159,7 +163,9 @@ impl eframe::App for ImageViewer { if !matches!(self.fit, ImageFit::Original(_)) { self.fit = ImageFit::Original(Some(1.0)); } - let ImageFit::Original(Some(scale)) = &mut self.fit else { unreachable!() }; + let ImageFit::Original(Some(scale)) = &mut self.fit else { + unreachable!() + }; ui.add(Slider::new(scale, 0.1..=4.0).text("scale")); } } diff --git a/crates/egui_extras/src/lib.rs b/crates/egui_extras/src/lib.rs index 3b020323..9e918ce1 100644 --- a/crates/egui_extras/src/lib.rs +++ b/crates/egui_extras/src/lib.rs @@ -39,8 +39,8 @@ mod profiling_scopes { /// Profiling macro for feature "puffin" macro_rules! profile_function { ($($arg: tt)*) => { - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. #[cfg(feature = "puffin")] + #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. puffin::profile_function!($($arg)*); }; } @@ -49,8 +49,8 @@ mod profiling_scopes { /// Profiling macro for feature "puffin" macro_rules! profile_scope { ($($arg: tt)*) => { - #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. #[cfg(feature = "puffin")] + #[cfg(not(target_arch = "wasm32"))] // Disabled on web because of the coarse 1ms clock resolution there. puffin::profile_scope!($($arg)*); }; } diff --git a/crates/egui_extras/src/loaders/image_loader.rs b/crates/egui_extras/src/loaders/image_loader.rs index 989efc63..46098332 100644 --- a/crates/egui_extras/src/loaders/image_loader.rs +++ b/crates/egui_extras/src/loaders/image_loader.rs @@ -20,7 +20,7 @@ impl ImageCrateLoader { fn is_supported_uri(uri: &str) -> bool { let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else { // `true` because if there's no extension, assume that we support it - return true + return true; }; ext != "svg" diff --git a/crates/egui_extras/src/loaders/svg_loader.rs b/crates/egui_extras/src/loaders/svg_loader.rs index ea98184f..cb4263c7 100644 --- a/crates/egui_extras/src/loaders/svg_loader.rs +++ b/crates/egui_extras/src/loaders/svg_loader.rs @@ -18,7 +18,9 @@ impl SvgLoader { } fn is_supported(uri: &str) -> bool { - let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else { return false }; + let Some(ext) = Path::new(uri).extension().and_then(|ext| ext.to_str()) else { + return false; + }; ext == "svg" } diff --git a/crates/egui_glow/src/lib.rs b/crates/egui_glow/src/lib.rs index 18d8ff87..4af9e409 100644 --- a/crates/egui_glow/src/lib.rs +++ b/crates/egui_glow/src/lib.rs @@ -112,22 +112,30 @@ pub fn check_for_gl_error_impl(gl: &glow::Context, file: &str, line: u32, contex // --------------------------------------------------------------------------- -/// Profiling macro for feature "puffin" -macro_rules! profile_function { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] - puffin::profile_function!($($arg)*); - }; -} -pub(crate) use profile_function; +mod profiling_scopes { + #![allow(unused_macros)] + #![allow(unused_imports)] -/// Profiling macro for feature "puffin" -macro_rules! profile_scope { - ($($arg: tt)*) => { - #[cfg(feature = "puffin")] - #[cfg(not(target_arch = "wasm32"))] - puffin::profile_scope!($($arg)*); - }; + /// 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; } -pub(crate) use profile_scope; + +#[allow(unused_imports)] +pub(crate) use profiling_scopes::*; diff --git a/crates/egui_glow/src/painter.rs b/crates/egui_glow/src/painter.rs index e6ac79ec..64a8be0b 100644 --- a/crates/egui_glow/src/painter.rs +++ b/crates/egui_glow/src/painter.rs @@ -494,10 +494,13 @@ impl Painter { "Mismatch between texture size and texel count" ); - let data: Vec = image - .srgba_pixels(None) - .flat_map(|a| a.to_array()) - .collect(); + let data: Vec = { + crate::profile_scope!("font -> sRGBA"); + image + .srgba_pixels(None) + .flat_map(|a| a.to_array()) + .collect() + }; self.upload_texture_srgb(delta.pos, image.size, delta.options, &data); } @@ -511,6 +514,7 @@ impl Painter { options: egui::TextureOptions, data: &[u8], ) { + crate::profile_function!(); assert_eq!(data.len(), w * h * 4); assert!( w <= self.max_texture_side && h <= self.max_texture_side, @@ -561,6 +565,7 @@ impl Painter { let level = 0; if let Some([x, y]) = pos { + crate::profile_scope!("gl.tex_sub_image_2d"); self.gl.tex_sub_image_2d( glow::TEXTURE_2D, level, @@ -575,6 +580,7 @@ impl Painter { check_for_gl_error!(&self.gl, "tex_sub_image_2d"); } else { let border = 0; + crate::profile_scope!("gl.tex_image_2d"); self.gl.tex_image_2d( glow::TEXTURE_2D, level, diff --git a/crates/epaint/src/image.rs b/crates/epaint/src/image.rs index dca86f38..0a40fe4a 100644 --- a/crates/epaint/src/image.rs +++ b/crates/epaint/src/image.rs @@ -280,6 +280,7 @@ impl FontImage { /// `gamma` should normally be set to `None`. /// /// If you are having problems with text looking skinny and pixelated, try using a low gamma, e.g. `0.4`. + #[inline] pub fn srgba_pixels( &'_ self, gamma: Option, @@ -338,6 +339,7 @@ impl From for ImageData { } } +#[inline] fn fast_round(r: f32) -> u8 { (r + 0.5).floor() as _ // rust does a saturating cast since 1.45 } diff --git a/examples/puffin_profiler/src/main.rs b/examples/puffin_profiler/src/main.rs index f8d52e91..ab000715 100644 --- a/examples/puffin_profiler/src/main.rs +++ b/examples/puffin_profiler/src/main.rs @@ -56,6 +56,12 @@ fn start_puffin_server() { Ok(puffin_server) => { eprintln!("Run: cargo install puffin_viewer && puffin_viewer --url 127.0.0.1:8585"); + std::process::Command::new("puffin_viewer") + .arg("--url") + .arg("127.0.0.1:8585") + .spawn() + .ok(); + // We can store the server if we want, but in this case we just want // it to keep running. Dropping it closes the server, so let's not drop it! #[allow(clippy::mem_forget)]