From 30ee478cafeb84fbc8f46d5ba6a03834c9e0750c Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 19 Nov 2023 21:14:13 +0100 Subject: [PATCH] Fix egui-wgpu performance regression (#3580) Introduced in the recent multi-viewports work, we accidentally recreated the wgpu surfaces every frame. This is now fixed. I found this while improving the profiling of `eframe` --- crates/eframe/Cargo.toml | 8 +- crates/eframe/src/native/epi_integration.rs | 16 +- crates/eframe/src/native/glow_integration.rs | 82 +++++---- crates/eframe/src/native/run.rs | 72 +------- crates/eframe/src/native/wgpu_integration.rs | 112 ++++++------ crates/eframe/src/native/winit_integration.rs | 15 ++ crates/egui-wgpu/src/lib.rs | 41 +++-- crates/egui-wgpu/src/renderer.rs | 162 ++++++++++-------- crates/egui-wgpu/src/winit.rs | 35 ++-- crates/egui-winit/src/lib.rs | 96 +++++++++-- crates/egui-winit/src/window_settings.rs | 4 + crates/egui/src/context.rs | 6 +- crates/egui_glow/examples/pure_glow.rs | 2 +- crates/egui_glow/src/winit.rs | 4 +- 14 files changed, 374 insertions(+), 281 deletions(-) diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 0faca5602..f59c897db 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -70,7 +70,13 @@ persistence = [ ## `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"] +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"] diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index b6b52291d..4333431ca 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -47,6 +47,7 @@ pub fn viewport_builder( #[cfg(not(target_os = "ios"))] if native_options.centered { + crate::profile_scope!("center"); if let Some(monitor) = event_loop.available_monitors().next() { let monitor_size = monitor.size().to_logical::(monitor.scale_factor()); let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 }); @@ -76,9 +77,16 @@ pub fn apply_window_settings( } fn largest_monitor_point_size(event_loop: &EventLoopWindowTarget) -> egui::Vec2 { + crate::profile_function!(); + let mut max_size = egui::Vec2::ZERO; - for monitor in event_loop.available_monitors() { + let available_monitors = { + crate::profile_scope!("available_monitors"); + event_loop.available_monitors() + }; + + for monitor in available_monitors { let size = monitor.size().to_logical::(monitor.scale_factor()); let size = egui::vec2(size.width, size.height); max_size = max_size.max(size); @@ -210,14 +218,14 @@ impl EpiIntegration { self.close } - pub fn on_event( + pub fn on_window_event( &mut self, app: &mut dyn epi::App, event: &winit::event::WindowEvent<'_>, egui_winit: &mut egui_winit::State, viewport_id: ViewportId, ) -> EventResponse { - crate::profile_function!(); + crate::profile_function!(egui_winit::short_window_event_description(event)); use winit::event::{ElementState, MouseButton, WindowEvent}; @@ -247,7 +255,7 @@ impl EpiIntegration { _ => {} } - egui_winit.on_event(&self.egui_ctx, event, viewport_id) + egui_winit.on_window_event(&self.egui_ctx, event, viewport_id) } pub fn pre_update(&mut self) { diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 90af6284e..1a059ca2a 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -393,7 +393,7 @@ impl WinitApp for GlowWinitApp { event_loop: &EventLoopWindowTarget, event: &winit::event::Event<'_, UserEvent>, ) -> Result { - crate::profile_function!(); + crate::profile_function!(winit_integration::short_event_description(event)); Ok(match event { winit::event::Event::Resumed => { @@ -468,19 +468,20 @@ impl WinitApp for GlowWinitApp { impl GlowWinitRunning { fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + crate::profile_function!(); + let Some(viewport_id) = self - .glutin - .borrow() - .viewport_from_window - .get(&window_id) - .copied() - else { - return EventResult::Wait; - }; + .glutin + .borrow() + .viewport_from_window + .get(&window_id) + .copied() + else { + return EventResult::Wait; + }; #[cfg(feature = "puffin")] puffin::GlobalProfiler::lock().new_frame(); - crate::profile_scope!("frame"); { let glutin = self.glutin.borrow(); @@ -565,15 +566,22 @@ impl GlowWinitRunning { let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); - *current_gl_context = Some( - current_gl_context - .take() - .unwrap() - .make_not_current() - .unwrap() - .make_current(gl_surface) - .unwrap(), - ); + { + // TODO: only do this if we actually have multiple viewports + crate::profile_scope!("change_gl_context"); + + let not_current = { + crate::profile_scope!("make_not_current"); + current_gl_context + .take() + .unwrap() + .make_not_current() + .unwrap() + }; + + crate::profile_scope!("make_current"); + *current_gl_context = Some(not_current.make_current(gl_surface).unwrap()); + } let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); @@ -646,6 +654,8 @@ impl GlowWinitRunning { window_id: WindowId, event: &winit::event::WindowEvent<'_>, ) -> EventResult { + crate::profile_function!(egui_winit::short_window_event_description(event)); + let viewport_id = self .glutin .borrow() @@ -710,7 +720,7 @@ impl GlowWinitRunning { if let Some(viewport_id) = viewport_id { let mut glutin = self.glutin.borrow_mut(); if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { - break 'res self.integration.on_event( + break 'res self.integration.on_window_event( self.app.as_mut(), event, viewport.egui_winit.as_mut().unwrap(), @@ -1127,11 +1137,11 @@ impl Viewport { /// Update the stored `ViewportInfo`. fn update_viewport_info(&mut self) { let Some(window) = &self.window else { - return; - }; + return; + }; let Some(egui_winit) = &self.egui_winit else { - return; - }; + return; + }; egui_winit.update_viewport_info(&mut self.info, window); } } @@ -1245,15 +1255,15 @@ fn render_immediate_viewport( let mut glutin = glutin.borrow_mut(); let Some(viewport) = glutin.viewports.get_mut(&ids.this) else { - return; - }; + return; + }; viewport.update_viewport_info(); let Some(winit_state) = &mut viewport.egui_winit else { - return; - }; + return; + }; let Some(window) = &viewport.window else { - return; - }; + return; + }; let mut raw_input = winit_state.take_egui_input(window, ids); raw_input.viewports = glutin @@ -1290,15 +1300,15 @@ fn render_immediate_viewport( } = &mut *glutin; let Some(viewport) = viewports.get_mut(&ids.this) else { - return; - }; + return; + }; let Some(winit_state) = &mut viewport.egui_winit else { - return; - }; + return; + }; let (Some(window), Some(gl_surface)) = (&viewport.window, &viewport.gl_surface) else { - return; - }; + return; + }; let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index bd71fcd73..6e16806ca 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -11,7 +11,11 @@ use winit::event_loop::{EventLoop, EventLoopBuilder}; use egui::epaint::ahash::HashMap; -use crate::{epi, native::winit_integration::EventResult, Result}; +use crate::{ + epi, + native::winit_integration::{short_event_description, EventResult}, + Result, +}; use super::winit_integration::{UserEvent, WinitApp}; @@ -397,69 +401,3 @@ pub fn run_wgpu( let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, wgpu_eframe); } - -// ---------------------------------------------------------------------------- - -// 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/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 825fdcdca..c0f0ba5f9 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -111,8 +111,8 @@ impl WgpuWinitApp { fn build_windows(&mut self, event_loop: &EventLoopWindowTarget) { let Some(running) = &mut self.running else { - return; - }; + return; + }; let mut shared = running.shared.borrow_mut(); let SharedState { viewports, @@ -357,7 +357,7 @@ impl WinitApp for WgpuWinitApp { event_loop: &EventLoopWindowTarget, event: &winit::event::Event<'_, UserEvent>, ) -> Result { - crate::profile_function!(); + crate::profile_function!(winit_integration::short_event_description(event)); self.build_windows(event_loop); @@ -452,21 +452,21 @@ impl WgpuWinitRunning { /// This is called both for the root viewport, and all deferred viewports fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + crate::profile_function!(); + let Some(viewport_id) = self - .shared - .borrow() - .viewport_from_window - .get(&window_id) - .copied() - else { - return EventResult::Wait; - }; + .shared + .borrow() + .viewport_from_window + .get(&window_id) + .copied() + else { + return EventResult::Wait; + }; #[cfg(feature = "puffin")] puffin::GlobalProfiler::lock().new_frame(); - crate::profile_scope!("frame"); - let WgpuWinitRunning { app, integration, @@ -474,30 +474,33 @@ impl WgpuWinitRunning { } = self; let (viewport_ui_cb, raw_input) = { + crate::profile_scope!("Prepare"); let mut shared_lock = shared.borrow_mut(); let SharedState { viewports, painter, .. } = &mut *shared_lock; - let Some(viewport) = viewports.get(&viewport_id) else { + if viewport_id != ViewportId::ROOT { + let Some(viewport) = viewports.get(&viewport_id) else { return EventResult::Wait; }; - if viewport_id != ViewportId::ROOT && viewport.viewport_ui_cb.is_none() { - // This will only happen if this is an immediate viewport. - // That means that the viewport cannot be rendered by itself and needs his parent to be rendered. - if let Some(viewport) = viewports.get(&viewport.ids.parent) { - if let Some(window) = viewport.window.as_ref() { - return EventResult::RepaintNext(window.id()); + if viewport.viewport_ui_cb.is_none() { + // This will only happen if this is an immediate viewport. + // That means that the viewport cannot be rendered by itself and needs his parent to be rendered. + if let Some(viewport) = viewports.get(&viewport.ids.parent) { + if let Some(window) = viewport.window.as_ref() { + return EventResult::RepaintNext(window.id()); + } } + return EventResult::Wait; } - return EventResult::Wait; } let Some(viewport) = viewports.get_mut(&viewport_id) else { - return EventResult::Wait; - }; + return EventResult::Wait; + }; viewport.update_viewport_info(); let Viewport { @@ -507,14 +510,19 @@ impl WgpuWinitRunning { egui_winit, .. } = viewport; + let viewport_ui_cb = viewport_ui_cb.clone(); let Some(window) = window else { - return EventResult::Wait; - }; + return EventResult::Wait; + }; - if let Err(err) = pollster::block_on(painter.set_window(viewport_id, Some(window))) { - log::warn!("Failed to set window: {err}"); + { + crate::profile_scope!("set_window"); + if let Err(err) = pollster::block_on(painter.set_window(viewport_id, Some(window))) + { + log::warn!("Failed to set window: {err}"); + } } let mut raw_input = egui_winit.as_mut().unwrap().take_egui_input( @@ -551,17 +559,17 @@ impl WgpuWinitRunning { } = &mut *shared; let Some(viewport) = viewports.get_mut(&viewport_id) else { - return EventResult::Wait; - }; + return EventResult::Wait; + }; let Viewport { - window: Some(window), - egui_winit: Some(egui_winit), - .. - } = viewport - else { - return EventResult::Wait; - }; + window: Some(window), + egui_winit: Some(egui_winit), + .. + } = viewport + else { + return EventResult::Wait; + }; integration.post_update(); @@ -637,6 +645,8 @@ impl WgpuWinitRunning { window_id: WindowId, event: &winit::event::WindowEvent<'_>, ) -> EventResult { + crate::profile_function!(egui_winit::short_window_event_description(event)); + let Self { integration, app, @@ -705,7 +715,7 @@ impl WgpuWinitRunning { let event_response = viewport_id.and_then(|viewport_id| { shared.viewports.get_mut(&viewport_id).and_then(|viewport| { viewport.egui_winit.as_mut().map(|egui_winit| { - integration.on_event(app.as_mut(), event, egui_winit, viewport_id) + integration.on_window_event(app.as_mut(), event, egui_winit, viewport_id) }) }) }); @@ -769,12 +779,13 @@ impl Viewport { /// Update the stored `ViewportInfo`. pub fn update_viewport_info(&mut self) { + crate::profile_function!(); let Some(window) = &self.window else { - return; - }; + return; + }; let Some(egui_winit) = &self.egui_winit else { - return; - }; + return; + }; egui_winit.update_viewport_info(&mut self.info, window); } } @@ -834,10 +845,9 @@ fn render_immediate_viewport( } viewport.update_viewport_info(); - let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit) - else { - return; - }; + let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit) else { + return; + }; let mut input = winit_state.take_egui_input(window, ids); input.viewports = viewports @@ -873,14 +883,14 @@ fn render_immediate_viewport( } = &mut *shared; let Some(viewport) = viewports.get_mut(&ids.this) else { - return; - }; + return; + }; let Some(winit_state) = &mut viewport.egui_winit else { - return; - }; + return; + }; let Some(window) = &viewport.window else { - return; - }; + return; + }; if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window))) { log::error!( diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index 96a98f70e..0f4ec5e76 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -100,3 +100,18 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option) -> &'static str { + use winit::event::Event; + + match event { + Event::UserEvent(user_event) => match user_event { + UserEvent::RequestRepaint { .. } => "UserEvent::RequestRepaint", + #[cfg(feature = "accesskit")] + UserEvent::AccessKitActionRequest(_) => "UserEvent::AccessKitActionRequest", + }, + _ => egui_winit::short_generic_event_description(event), + } +} diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index a46e185e8..46e97d0a9 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -67,21 +67,32 @@ impl RenderState { depth_format: Option, msaa_samples: u32, ) -> Result { - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: config.power_preference, - compatible_surface: Some(surface), - force_fallback_adapter: false, - }) - .await - .ok_or(WgpuError::NoSuitableAdapterFound)?; - - let target_format = - crate::preferred_framebuffer_format(&surface.get_capabilities(&adapter).formats)?; - - let (device, queue) = adapter - .request_device(&(*config.device_descriptor)(&adapter), None) - .await?; + crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function` + + let adapter = { + crate::profile_scope!("request_adapter"); + instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: config.power_preference, + compatible_surface: Some(surface), + force_fallback_adapter: false, + }) + .await + .ok_or(WgpuError::NoSuitableAdapterFound)? + }; + + let capabilities = { + crate::profile_scope!("get_capabilities"); + surface.get_capabilities(&adapter).formats + }; + let target_format = crate::preferred_framebuffer_format(&capabilities)?; + + let (device, queue) = { + crate::profile_scope!("request_device"); + adapter + .request_device(&(*config.device_descriptor)(&adapter), None) + .await? + }; let renderer = Renderer::new(&device, target_format, depth_format, msaa_samples); diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 62d969833..b4bc91fd7 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -189,7 +189,10 @@ impl Renderer { label: Some("egui"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))), }; - let module = device.create_shader_module(shader); + let module = { + crate::profile_scope!("create_shader_module"); + device.create_shader_module(shader) + }; let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("egui_uniform_buffer"), @@ -200,7 +203,8 @@ impl Renderer { usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, }); - let uniform_bind_group_layout = + let uniform_bind_group_layout = { + crate::profile_scope!("create_bind_group_layout"); device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("egui_uniform_bind_group_layout"), entries: &[wgpu::BindGroupLayoutEntry { @@ -213,22 +217,27 @@ impl Renderer { }, count: None, }], - }); + }) + }; - let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { - label: Some("egui_uniform_bind_group"), - layout: &uniform_bind_group_layout, - entries: &[wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { - buffer: &uniform_buffer, - offset: 0, - size: None, - }), - }], - }); + let uniform_bind_group = { + crate::profile_scope!("create_bind_group"); + device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("egui_uniform_bind_group"), + layout: &uniform_bind_group_layout, + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { + buffer: &uniform_buffer, + offset: 0, + size: None, + }), + }], + }) + }; - let texture_bind_group_layout = + let texture_bind_group_layout = { + crate::profile_scope!("create_bind_group_layout"); device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { label: Some("egui_texture_bind_group_layout"), entries: &[ @@ -249,7 +258,8 @@ impl Renderer { count: None, }, ], - }); + }) + }; let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { label: Some("egui_pipeline_layout"), @@ -265,64 +275,68 @@ impl Renderer { bias: wgpu::DepthBiasState::default(), }); - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("egui_pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - entry_point: "vs_main", - module: &module, - buffers: &[wgpu::VertexBufferLayout { - array_stride: 5 * 4, - step_mode: wgpu::VertexStepMode::Vertex, - // 0: vec2 position - // 1: vec2 texture coordinates - // 2: uint color - attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32], - }], - }, - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - unclipped_depth: false, - conservative: false, - cull_mode: None, - front_face: wgpu::FrontFace::default(), - polygon_mode: wgpu::PolygonMode::default(), - strip_index_format: None, - }, - depth_stencil, - multisample: wgpu::MultisampleState { - alpha_to_coverage_enabled: false, - count: msaa_samples, - mask: !0, - }, - - fragment: Some(wgpu::FragmentState { - module: &module, - entry_point: if output_color_format.is_srgb() { - log::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_color_format); - "fs_main_linear_framebuffer" - } else { - "fs_main_gamma_framebuffer" // this is what we prefer + let pipeline = { + crate::profile_scope!("create_render_pipeline"); + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("egui_pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + entry_point: "vs_main", + module: &module, + buffers: &[wgpu::VertexBufferLayout { + array_stride: 5 * 4, + step_mode: wgpu::VertexStepMode::Vertex, + // 0: vec2 position + // 1: vec2 texture coordinates + // 2: uint color + attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32], + }], }, - targets: &[Some(wgpu::ColorTargetState { - format: output_color_format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::OneMinusDstAlpha, - dst_factor: wgpu::BlendFactor::One, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - multiview: None, - }); + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + unclipped_depth: false, + conservative: false, + cull_mode: None, + front_face: wgpu::FrontFace::default(), + polygon_mode: wgpu::PolygonMode::default(), + strip_index_format: None, + }, + depth_stencil, + multisample: wgpu::MultisampleState { + alpha_to_coverage_enabled: false, + count: msaa_samples, + mask: !0, + }, + + fragment: Some(wgpu::FragmentState { + module: &module, + entry_point: if output_color_format.is_srgb() { + log::warn!("Detected a linear (sRGBA aware) framebuffer {:?}. egui prefers Rgba8Unorm or Bgra8Unorm", output_color_format); + "fs_main_linear_framebuffer" + } else { + "fs_main_gamma_framebuffer" // this is what we prefer + }, + targets: &[Some(wgpu::ColorTargetState { + format: output_color_format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::OneMinusDstAlpha, + dst_factor: wgpu::BlendFactor::One, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + } + ) + }; const VERTEX_BUFFER_START_CAPACITY: wgpu::BufferAddress = (std::mem::size_of::() * 1024) as _; diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 68803a24b..faa28d045 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -143,6 +143,7 @@ impl Painter { present_mode: wgpu::PresentMode, ) { crate::profile_function!(); + let usage = if surface_state.supports_screenshot { wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST } else { @@ -188,12 +189,15 @@ impl Painter { viewport_id: ViewportId, window: Option<&winit::window::Window>, ) -> Result<(), crate::WgpuError> { - crate::profile_function!(); + crate::profile_scope!("Painter::set_window"); // profle_function gives bad names for async functions if let Some(window) = window { let size = window.inner_size(); if self.surfaces.get(&viewport_id).is_none() { - let surface = unsafe { self.instance.create_surface(&window)? }; + let surface = unsafe { + crate::profile_scope!("create_surface"); + self.instance.create_surface(&window)? + }; let render_state = if let Some(render_state) = &self.render_state { render_state @@ -241,19 +245,24 @@ impl Painter { supports_screenshot, }, ); - } - let Some(width) = NonZeroU32::new(size.width) else { - log::debug!("The window width was zero; skipping generate textures"); - return Ok(()); - }; - let Some(height) = NonZeroU32::new(size.height) else { - log::debug!("The window height was zero; skipping generate textures"); - return Ok(()); - }; - self.resize_and_generate_depth_texture_view_and_msaa_view(viewport_id, width, height); + let Some(width) = NonZeroU32::new(size.width) else { + log::debug!("The window width was zero; skipping generate textures"); + return Ok(()); + }; + let Some(height) = NonZeroU32::new(size.height) else { + log::debug!("The window height was zero; skipping generate textures"); + return Ok(()); + }; + + self.resize_and_generate_depth_texture_view_and_msaa_view( + viewport_id, + width, + height, + ); + } } else { - log::warn!("All surfaces was deleted!"); + log::warn!("No window - clearing all surfaces"); self.surfaces.clear(); } Ok(()) diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 1f05a9fc7..e5eb664c1 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -174,14 +174,14 @@ impl State { } /// The current input state. - /// This is changed by [`Self::on_event`] and cleared by [`Self::take_egui_input`]. + /// This is changed by [`Self::on_window_event`] and cleared by [`Self::take_egui_input`]. #[inline] pub fn egui_input(&self) -> &egui::RawInput { &self.egui_input } /// The current input state. - /// This is changed by [`Self::on_event`] and cleared by [`Self::take_egui_input`]. + /// This is changed by [`Self::on_window_event`] and cleared by [`Self::take_egui_input`]. #[inline] pub fn egui_input_mut(&mut self) -> &mut egui::RawInput { &mut self.egui_input @@ -233,13 +233,13 @@ impl State { /// Call this when there is a new event. /// /// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_egui_input`]. - pub fn on_event( + pub fn on_window_event( &mut self, egui_ctx: &egui::Context, event: &winit::event::WindowEvent<'_>, viewport_id: ViewportId, ) -> EventResponse { - crate::profile_function!(); + crate::profile_function!(short_window_event_description(event)); use winit::event::WindowEvent; match event { @@ -822,16 +822,14 @@ fn update_viewport_info(viewport_info: &mut ViewportInfo, window: &Window, pixel let inner_rect = inner_rect_px.map(|r| r / pixels_per_point); let outer_rect = outer_rect_px.map(|r| r / pixels_per_point); - let monitor = window.current_monitor().is_some(); - let monitor_size = if monitor { - let size = window - .current_monitor() - .unwrap() - .size() - .to_logical::(pixels_per_point.into()); - Some(egui::vec2(size.width, size.height)) - } else { - None + let monitor_size = { + crate::profile_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)) + } else { + None + } }; viewport_info.title = Some(window.title()); @@ -1339,6 +1337,76 @@ pub fn apply_viewport_builder_to_new_window(window: &Window, builder: &ViewportB // --------------------------------------------------------------------------- +/// Short and fast description of an event. +/// Useful for logging and profiling. +pub fn short_generic_event_description(event: &winit::event::Event<'_, T>) -> &'static str { + use winit::event::{DeviceEvent, Event, StartCause}; + + 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(_) => "UserEvent", + 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, .. } => short_window_event_description(event), + } +} + +/// Short and fast description of an event. +/// Useful for logging and profiling. +pub fn short_window_event_description(event: &winit::event::WindowEvent<'_>) -> &'static str { + use winit::event::WindowEvent; + + 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", + } +} + +// --------------------------------------------------------------------------- + mod profiling_scopes { #![allow(unused_macros)] #![allow(unused_imports)] diff --git a/crates/egui-winit/src/window_settings.rs b/crates/egui-winit/src/window_settings.rs index 021b4c3d7..3659d9586 100644 --- a/crates/egui-winit/src/window_settings.rs +++ b/crates/egui-winit/src/window_settings.rs @@ -52,6 +52,8 @@ impl WindowSettings { &self, mut viewport_builder: ViewportBuilder, ) -> ViewportBuilder { + crate::profile_function!(); + // `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere // See [`winit::window::WindowBuilder::with_position`] for details. let pos_px = if cfg!(target_os = "macos") { @@ -127,6 +129,8 @@ fn clamp_pos_to_monitors( window_size_pts: egui::Vec2, position_px: &mut egui::Pos2, ) { + crate::profile_function!(); + let monitors = event_loop.available_monitors(); // default to primary monitor, in case the correct monitor was disconnected. diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 5c72e86a0..2266ff95d 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -2391,7 +2391,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!(); + crate::profile_function!(uri); let loaders = self.loaders(); let bytes_loaders = loaders.bytes.lock(); @@ -2428,7 +2428,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!(); + crate::profile_function!(uri); let loaders = self.loaders(); let image_loaders = loaders.image.lock(); @@ -2471,7 +2471,7 @@ impl Context { texture_options: TextureOptions, size_hint: load::SizeHint, ) -> load::TextureLoadResult { - crate::profile_function!(); + crate::profile_function!(uri); let loaders = self.loaders(); let texture_loaders = loaders.texture.lock(); diff --git a/crates/egui_glow/examples/pure_glow.rs b/crates/egui_glow/examples/pure_glow.rs index 6392cd302..5df4d23ec 100644 --- a/crates/egui_glow/examples/pure_glow.rs +++ b/crates/egui_glow/examples/pure_glow.rs @@ -235,7 +235,7 @@ fn main() { gl_window.resize(**new_inner_size); } - let event_response = egui_glow.on_event(&event); + let event_response = egui_glow.on_window_event(&event); if event_response.repaint { gl_window.window().request_redraw(); diff --git a/crates/egui_glow/src/winit.rs b/crates/egui_glow/src/winit.rs index 23eaa8992..d1347944c 100644 --- a/crates/egui_glow/src/winit.rs +++ b/crates/egui_glow/src/winit.rs @@ -52,9 +52,9 @@ impl EguiGlow { } } - pub fn on_event(&mut self, event: &winit::event::WindowEvent<'_>) -> EventResponse { + pub fn on_window_event(&mut self, event: &winit::event::WindowEvent<'_>) -> EventResponse { self.egui_winit - .on_event(&self.egui_ctx, event, ViewportId::ROOT) + .on_window_event(&self.egui_ctx, event, ViewportId::ROOT) } /// Call [`Self::paint`] later to paint.