Browse Source

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`
pull/3581/head
Emil Ernerfeldt 12 months ago
committed by GitHub
parent
commit
30ee478caf
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      crates/eframe/Cargo.toml
  2. 16
      crates/eframe/src/native/epi_integration.rs
  3. 82
      crates/eframe/src/native/glow_integration.rs
  4. 72
      crates/eframe/src/native/run.rs
  5. 112
      crates/eframe/src/native/wgpu_integration.rs
  6. 15
      crates/eframe/src/native/winit_integration.rs
  7. 41
      crates/egui-wgpu/src/lib.rs
  8. 162
      crates/egui-wgpu/src/renderer.rs
  9. 35
      crates/egui-wgpu/src/winit.rs
  10. 96
      crates/egui-winit/src/lib.rs
  11. 4
      crates/egui-winit/src/window_settings.rs
  12. 6
      crates/egui/src/context.rs
  13. 2
      crates/egui_glow/examples/pure_glow.rs
  14. 4
      crates/egui_glow/src/winit.rs

8
crates/eframe/Cargo.toml

@ -70,7 +70,13 @@ persistence = [
## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you ## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you
## ##
## Only enabled on native, because of the low resolution (1ms) of clocks in browsers. ## 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. ## Enables wayland support and fixes clipboard issue.
wayland = ["egui-winit/wayland"] wayland = ["egui-winit/wayland"]

16
crates/eframe/src/native/epi_integration.rs

@ -47,6 +47,7 @@ pub fn viewport_builder<E>(
#[cfg(not(target_os = "ios"))] #[cfg(not(target_os = "ios"))]
if native_options.centered { if native_options.centered {
crate::profile_scope!("center");
if let Some(monitor) = event_loop.available_monitors().next() { if let Some(monitor) = event_loop.available_monitors().next() {
let monitor_size = monitor.size().to_logical::<f32>(monitor.scale_factor()); let monitor_size = monitor.size().to_logical::<f32>(monitor.scale_factor());
let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 }); 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<E>(event_loop: &EventLoopWindowTarget<E>) -> egui::Vec2 { fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui::Vec2 {
crate::profile_function!();
let mut max_size = egui::Vec2::ZERO; 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::<f32>(monitor.scale_factor()); let size = monitor.size().to_logical::<f32>(monitor.scale_factor());
let size = egui::vec2(size.width, size.height); let size = egui::vec2(size.width, size.height);
max_size = max_size.max(size); max_size = max_size.max(size);
@ -210,14 +218,14 @@ impl EpiIntegration {
self.close self.close
} }
pub fn on_event( pub fn on_window_event(
&mut self, &mut self,
app: &mut dyn epi::App, app: &mut dyn epi::App,
event: &winit::event::WindowEvent<'_>, event: &winit::event::WindowEvent<'_>,
egui_winit: &mut egui_winit::State, egui_winit: &mut egui_winit::State,
viewport_id: ViewportId, viewport_id: ViewportId,
) -> EventResponse { ) -> EventResponse {
crate::profile_function!(); crate::profile_function!(egui_winit::short_window_event_description(event));
use winit::event::{ElementState, MouseButton, WindowEvent}; 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) { pub fn pre_update(&mut self) {

82
crates/eframe/src/native/glow_integration.rs

@ -393,7 +393,7 @@ impl WinitApp for GlowWinitApp {
event_loop: &EventLoopWindowTarget<UserEvent>, event_loop: &EventLoopWindowTarget<UserEvent>,
event: &winit::event::Event<'_, UserEvent>, event: &winit::event::Event<'_, UserEvent>,
) -> Result<EventResult> { ) -> Result<EventResult> {
crate::profile_function!(); crate::profile_function!(winit_integration::short_event_description(event));
Ok(match event { Ok(match event {
winit::event::Event::Resumed => { winit::event::Event::Resumed => {
@ -468,19 +468,20 @@ impl WinitApp for GlowWinitApp {
impl GlowWinitRunning { impl GlowWinitRunning {
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
crate::profile_function!();
let Some(viewport_id) = self let Some(viewport_id) = self
.glutin .glutin
.borrow() .borrow()
.viewport_from_window .viewport_from_window
.get(&window_id) .get(&window_id)
.copied() .copied()
else { else {
return EventResult::Wait; return EventResult::Wait;
}; };
#[cfg(feature = "puffin")] #[cfg(feature = "puffin")]
puffin::GlobalProfiler::lock().new_frame(); puffin::GlobalProfiler::lock().new_frame();
crate::profile_scope!("frame");
{ {
let glutin = self.glutin.borrow(); let glutin = self.glutin.borrow();
@ -565,15 +566,22 @@ impl GlowWinitRunning {
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point); let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
*current_gl_context = Some( {
current_gl_context // TODO: only do this if we actually have multiple viewports
.take() crate::profile_scope!("change_gl_context");
.unwrap()
.make_not_current() let not_current = {
.unwrap() crate::profile_scope!("make_not_current");
.make_current(gl_surface) current_gl_context
.unwrap(), .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(); let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
@ -646,6 +654,8 @@ impl GlowWinitRunning {
window_id: WindowId, window_id: WindowId,
event: &winit::event::WindowEvent<'_>, event: &winit::event::WindowEvent<'_>,
) -> EventResult { ) -> EventResult {
crate::profile_function!(egui_winit::short_window_event_description(event));
let viewport_id = self let viewport_id = self
.glutin .glutin
.borrow() .borrow()
@ -710,7 +720,7 @@ impl GlowWinitRunning {
if let Some(viewport_id) = viewport_id { if let Some(viewport_id) = viewport_id {
let mut glutin = self.glutin.borrow_mut(); let mut glutin = self.glutin.borrow_mut();
if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { 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(), self.app.as_mut(),
event, event,
viewport.egui_winit.as_mut().unwrap(), viewport.egui_winit.as_mut().unwrap(),
@ -1127,11 +1137,11 @@ impl Viewport {
/// Update the stored `ViewportInfo`. /// Update the stored `ViewportInfo`.
fn update_viewport_info(&mut self) { fn update_viewport_info(&mut self) {
let Some(window) = &self.window else { let Some(window) = &self.window else {
return; return;
}; };
let Some(egui_winit) = &self.egui_winit else { let Some(egui_winit) = &self.egui_winit else {
return; return;
}; };
egui_winit.update_viewport_info(&mut self.info, window); egui_winit.update_viewport_info(&mut self.info, window);
} }
} }
@ -1245,15 +1255,15 @@ fn render_immediate_viewport(
let mut glutin = glutin.borrow_mut(); let mut glutin = glutin.borrow_mut();
let Some(viewport) = glutin.viewports.get_mut(&ids.this) else { let Some(viewport) = glutin.viewports.get_mut(&ids.this) else {
return; return;
}; };
viewport.update_viewport_info(); viewport.update_viewport_info();
let Some(winit_state) = &mut viewport.egui_winit else { let Some(winit_state) = &mut viewport.egui_winit else {
return; return;
}; };
let Some(window) = &viewport.window else { let Some(window) = &viewport.window else {
return; return;
}; };
let mut raw_input = winit_state.take_egui_input(window, ids); let mut raw_input = winit_state.take_egui_input(window, ids);
raw_input.viewports = glutin raw_input.viewports = glutin
@ -1290,15 +1300,15 @@ fn render_immediate_viewport(
} = &mut *glutin; } = &mut *glutin;
let Some(viewport) = viewports.get_mut(&ids.this) else { let Some(viewport) = viewports.get_mut(&ids.this) else {
return; return;
}; };
let Some(winit_state) = &mut viewport.egui_winit else { let Some(winit_state) = &mut viewport.egui_winit else {
return; return;
}; };
let (Some(window), Some(gl_surface)) = (&viewport.window, &viewport.gl_surface) else { 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(); let screen_size_in_pixels: [u32; 2] = window.inner_size().into();

72
crates/eframe/src/native/run.rs

@ -11,7 +11,11 @@ use winit::event_loop::{EventLoop, EventLoopBuilder};
use egui::epaint::ahash::HashMap; 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}; 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); let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);
run_and_exit(event_loop, wgpu_eframe); 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",
},
}
}

112
crates/eframe/src/native/wgpu_integration.rs

@ -111,8 +111,8 @@ impl WgpuWinitApp {
fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) { fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
let Some(running) = &mut self.running else { let Some(running) = &mut self.running else {
return; return;
}; };
let mut shared = running.shared.borrow_mut(); let mut shared = running.shared.borrow_mut();
let SharedState { let SharedState {
viewports, viewports,
@ -357,7 +357,7 @@ impl WinitApp for WgpuWinitApp {
event_loop: &EventLoopWindowTarget<UserEvent>, event_loop: &EventLoopWindowTarget<UserEvent>,
event: &winit::event::Event<'_, UserEvent>, event: &winit::event::Event<'_, UserEvent>,
) -> Result<EventResult> { ) -> Result<EventResult> {
crate::profile_function!(); crate::profile_function!(winit_integration::short_event_description(event));
self.build_windows(event_loop); self.build_windows(event_loop);
@ -452,21 +452,21 @@ impl WgpuWinitRunning {
/// This is called both for the root viewport, and all deferred viewports /// This is called both for the root viewport, and all deferred viewports
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult {
crate::profile_function!();
let Some(viewport_id) = self let Some(viewport_id) = self
.shared .shared
.borrow() .borrow()
.viewport_from_window .viewport_from_window
.get(&window_id) .get(&window_id)
.copied() .copied()
else { else {
return EventResult::Wait; return EventResult::Wait;
}; };
#[cfg(feature = "puffin")] #[cfg(feature = "puffin")]
puffin::GlobalProfiler::lock().new_frame(); puffin::GlobalProfiler::lock().new_frame();
crate::profile_scope!("frame");
let WgpuWinitRunning { let WgpuWinitRunning {
app, app,
integration, integration,
@ -474,30 +474,33 @@ impl WgpuWinitRunning {
} = self; } = self;
let (viewport_ui_cb, raw_input) = { let (viewport_ui_cb, raw_input) = {
crate::profile_scope!("Prepare");
let mut shared_lock = shared.borrow_mut(); let mut shared_lock = shared.borrow_mut();
let SharedState { let SharedState {
viewports, painter, .. viewports, painter, ..
} = &mut *shared_lock; } = &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; return EventResult::Wait;
}; };
if viewport_id != ViewportId::ROOT && viewport.viewport_ui_cb.is_none() { if viewport.viewport_ui_cb.is_none() {
// This will only happen if this is an immediate viewport. // 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. // 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(viewport) = viewports.get(&viewport.ids.parent) {
if let Some(window) = viewport.window.as_ref() { if let Some(window) = viewport.window.as_ref() {
return EventResult::RepaintNext(window.id()); return EventResult::RepaintNext(window.id());
}
} }
return EventResult::Wait;
} }
return EventResult::Wait;
} }
let Some(viewport) = viewports.get_mut(&viewport_id) else { let Some(viewport) = viewports.get_mut(&viewport_id) else {
return EventResult::Wait; return EventResult::Wait;
}; };
viewport.update_viewport_info(); viewport.update_viewport_info();
let Viewport { let Viewport {
@ -507,14 +510,19 @@ impl WgpuWinitRunning {
egui_winit, egui_winit,
.. ..
} = viewport; } = viewport;
let viewport_ui_cb = viewport_ui_cb.clone(); let viewport_ui_cb = viewport_ui_cb.clone();
let Some(window) = window else { 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( let mut raw_input = egui_winit.as_mut().unwrap().take_egui_input(
@ -551,17 +559,17 @@ impl WgpuWinitRunning {
} = &mut *shared; } = &mut *shared;
let Some(viewport) = viewports.get_mut(&viewport_id) else { let Some(viewport) = viewports.get_mut(&viewport_id) else {
return EventResult::Wait; return EventResult::Wait;
}; };
let Viewport { let Viewport {
window: Some(window), window: Some(window),
egui_winit: Some(egui_winit), egui_winit: Some(egui_winit),
.. ..
} = viewport } = viewport
else { else {
return EventResult::Wait; return EventResult::Wait;
}; };
integration.post_update(); integration.post_update();
@ -637,6 +645,8 @@ impl WgpuWinitRunning {
window_id: WindowId, window_id: WindowId,
event: &winit::event::WindowEvent<'_>, event: &winit::event::WindowEvent<'_>,
) -> EventResult { ) -> EventResult {
crate::profile_function!(egui_winit::short_window_event_description(event));
let Self { let Self {
integration, integration,
app, app,
@ -705,7 +715,7 @@ impl WgpuWinitRunning {
let event_response = viewport_id.and_then(|viewport_id| { let event_response = viewport_id.and_then(|viewport_id| {
shared.viewports.get_mut(&viewport_id).and_then(|viewport| { shared.viewports.get_mut(&viewport_id).and_then(|viewport| {
viewport.egui_winit.as_mut().map(|egui_winit| { 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`. /// Update the stored `ViewportInfo`.
pub fn update_viewport_info(&mut self) { pub fn update_viewport_info(&mut self) {
crate::profile_function!();
let Some(window) = &self.window else { let Some(window) = &self.window else {
return; return;
}; };
let Some(egui_winit) = &self.egui_winit else { let Some(egui_winit) = &self.egui_winit else {
return; return;
}; };
egui_winit.update_viewport_info(&mut self.info, window); egui_winit.update_viewport_info(&mut self.info, window);
} }
} }
@ -834,10 +845,9 @@ fn render_immediate_viewport(
} }
viewport.update_viewport_info(); viewport.update_viewport_info();
let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit) let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit) else {
else { return;
return; };
};
let mut input = winit_state.take_egui_input(window, ids); let mut input = winit_state.take_egui_input(window, ids);
input.viewports = viewports input.viewports = viewports
@ -873,14 +883,14 @@ fn render_immediate_viewport(
} = &mut *shared; } = &mut *shared;
let Some(viewport) = viewports.get_mut(&ids.this) else { let Some(viewport) = viewports.get_mut(&ids.this) else {
return; return;
}; };
let Some(winit_state) = &mut viewport.egui_winit else { let Some(winit_state) = &mut viewport.egui_winit else {
return; return;
}; };
let Some(window) = &viewport.window else { let Some(window) = &viewport.window else {
return; return;
}; };
if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window))) { if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window))) {
log::error!( log::error!(

15
crates/eframe/src/native/winit_integration.rs

@ -100,3 +100,18 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option<c
None None
} }
} }
/// Short and fast description of an event.
/// Useful for logging and profiling.
pub fn short_event_description(event: &winit::event::Event<'_, UserEvent>) -> &'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),
}
}

41
crates/egui-wgpu/src/lib.rs

@ -67,21 +67,32 @@ impl RenderState {
depth_format: Option<wgpu::TextureFormat>, depth_format: Option<wgpu::TextureFormat>,
msaa_samples: u32, msaa_samples: u32,
) -> Result<Self, WgpuError> { ) -> Result<Self, WgpuError> {
let adapter = instance crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function`
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: config.power_preference, let adapter = {
compatible_surface: Some(surface), crate::profile_scope!("request_adapter");
force_fallback_adapter: false, instance
}) .request_adapter(&wgpu::RequestAdapterOptions {
.await power_preference: config.power_preference,
.ok_or(WgpuError::NoSuitableAdapterFound)?; compatible_surface: Some(surface),
force_fallback_adapter: false,
let target_format = })
crate::preferred_framebuffer_format(&surface.get_capabilities(&adapter).formats)?; .await
.ok_or(WgpuError::NoSuitableAdapterFound)?
let (device, queue) = adapter };
.request_device(&(*config.device_descriptor)(&adapter), None)
.await?; 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); let renderer = Renderer::new(&device, target_format, depth_format, msaa_samples);

162
crates/egui-wgpu/src/renderer.rs

@ -189,7 +189,10 @@ impl Renderer {
label: Some("egui"), label: Some("egui"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("egui.wgsl"))), 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 { let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("egui_uniform_buffer"), label: Some("egui_uniform_buffer"),
@ -200,7 +203,8 @@ impl Renderer {
usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 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 { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_uniform_bind_group_layout"), label: Some("egui_uniform_bind_group_layout"),
entries: &[wgpu::BindGroupLayoutEntry { entries: &[wgpu::BindGroupLayoutEntry {
@ -213,22 +217,27 @@ impl Renderer {
}, },
count: None, count: None,
}], }],
}); })
};
let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { let uniform_bind_group = {
label: Some("egui_uniform_bind_group"), crate::profile_scope!("create_bind_group");
layout: &uniform_bind_group_layout, device.create_bind_group(&wgpu::BindGroupDescriptor {
entries: &[wgpu::BindGroupEntry { label: Some("egui_uniform_bind_group"),
binding: 0, layout: &uniform_bind_group_layout,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { entries: &[wgpu::BindGroupEntry {
buffer: &uniform_buffer, binding: 0,
offset: 0, resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
size: None, 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 { device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_texture_bind_group_layout"), label: Some("egui_texture_bind_group_layout"),
entries: &[ entries: &[
@ -249,7 +258,8 @@ impl Renderer {
count: None, count: None,
}, },
], ],
}); })
};
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("egui_pipeline_layout"), label: Some("egui_pipeline_layout"),
@ -265,64 +275,68 @@ impl Renderer {
bias: wgpu::DepthBiasState::default(), bias: wgpu::DepthBiasState::default(),
}); });
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { let pipeline = {
label: Some("egui_pipeline"), crate::profile_scope!("create_render_pipeline");
layout: Some(&pipeline_layout), device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
vertex: wgpu::VertexState { label: Some("egui_pipeline"),
entry_point: "vs_main", layout: Some(&pipeline_layout),
module: &module, vertex: wgpu::VertexState {
buffers: &[wgpu::VertexBufferLayout { entry_point: "vs_main",
array_stride: 5 * 4, module: &module,
step_mode: wgpu::VertexStepMode::Vertex, buffers: &[wgpu::VertexBufferLayout {
// 0: vec2 position array_stride: 5 * 4,
// 1: vec2 texture coordinates step_mode: wgpu::VertexStepMode::Vertex,
// 2: uint color // 0: vec2 position
attributes: &wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x2, 2 => Uint32], // 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
}, },
targets: &[Some(wgpu::ColorTargetState { primitive: wgpu::PrimitiveState {
format: output_color_format, topology: wgpu::PrimitiveTopology::TriangleList,
blend: Some(wgpu::BlendState { unclipped_depth: false,
color: wgpu::BlendComponent { conservative: false,
src_factor: wgpu::BlendFactor::One, cull_mode: None,
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, front_face: wgpu::FrontFace::default(),
operation: wgpu::BlendOperation::Add, polygon_mode: wgpu::PolygonMode::default(),
}, strip_index_format: None,
alpha: wgpu::BlendComponent { },
src_factor: wgpu::BlendFactor::OneMinusDstAlpha, depth_stencil,
dst_factor: wgpu::BlendFactor::One, multisample: wgpu::MultisampleState {
operation: wgpu::BlendOperation::Add, alpha_to_coverage_enabled: false,
}, count: msaa_samples,
}), mask: !0,
write_mask: wgpu::ColorWrites::ALL, },
})],
}), fragment: Some(wgpu::FragmentState {
multiview: None, 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 = const VERTEX_BUFFER_START_CAPACITY: wgpu::BufferAddress =
(std::mem::size_of::<Vertex>() * 1024) as _; (std::mem::size_of::<Vertex>() * 1024) as _;

35
crates/egui-wgpu/src/winit.rs

@ -143,6 +143,7 @@ impl Painter {
present_mode: wgpu::PresentMode, present_mode: wgpu::PresentMode,
) { ) {
crate::profile_function!(); crate::profile_function!();
let usage = if surface_state.supports_screenshot { let usage = if surface_state.supports_screenshot {
wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_DST
} else { } else {
@ -188,12 +189,15 @@ impl Painter {
viewport_id: ViewportId, viewport_id: ViewportId,
window: Option<&winit::window::Window>, window: Option<&winit::window::Window>,
) -> Result<(), crate::WgpuError> { ) -> 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 { if let Some(window) = window {
let size = window.inner_size(); let size = window.inner_size();
if self.surfaces.get(&viewport_id).is_none() { 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 { let render_state = if let Some(render_state) = &self.render_state {
render_state render_state
@ -241,19 +245,24 @@ impl Painter {
supports_screenshot, supports_screenshot,
}, },
); );
}
let Some(width) = NonZeroU32::new(size.width) else { let Some(width) = NonZeroU32::new(size.width) else {
log::debug!("The window width was zero; skipping generate textures"); log::debug!("The window width was zero; skipping generate textures");
return Ok(()); return Ok(());
}; };
let Some(height) = NonZeroU32::new(size.height) else { let Some(height) = NonZeroU32::new(size.height) else {
log::debug!("The window height was zero; skipping generate textures"); log::debug!("The window height was zero; skipping generate textures");
return Ok(()); return Ok(());
}; };
self.resize_and_generate_depth_texture_view_and_msaa_view(viewport_id, width, height);
self.resize_and_generate_depth_texture_view_and_msaa_view(
viewport_id,
width,
height,
);
}
} else { } else {
log::warn!("All surfaces was deleted!"); log::warn!("No window - clearing all surfaces");
self.surfaces.clear(); self.surfaces.clear();
} }
Ok(()) Ok(())

96
crates/egui-winit/src/lib.rs

@ -174,14 +174,14 @@ impl State {
} }
/// The current input 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] #[inline]
pub fn egui_input(&self) -> &egui::RawInput { pub fn egui_input(&self) -> &egui::RawInput {
&self.egui_input &self.egui_input
} }
/// The current input 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] #[inline]
pub fn egui_input_mut(&mut self) -> &mut egui::RawInput { pub fn egui_input_mut(&mut self) -> &mut egui::RawInput {
&mut self.egui_input &mut self.egui_input
@ -233,13 +233,13 @@ impl State {
/// Call this when there is a new event. /// 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`]. /// 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, &mut self,
egui_ctx: &egui::Context, egui_ctx: &egui::Context,
event: &winit::event::WindowEvent<'_>, event: &winit::event::WindowEvent<'_>,
viewport_id: ViewportId, viewport_id: ViewportId,
) -> EventResponse { ) -> EventResponse {
crate::profile_function!(); crate::profile_function!(short_window_event_description(event));
use winit::event::WindowEvent; use winit::event::WindowEvent;
match event { 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 inner_rect = inner_rect_px.map(|r| r / pixels_per_point);
let outer_rect = outer_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 = {
let monitor_size = if monitor { crate::profile_scope!("monitor_size");
let size = window if let Some(monitor) = window.current_monitor() {
.current_monitor() let size = monitor.size().to_logical::<f32>(pixels_per_point.into());
.unwrap() Some(egui::vec2(size.width, size.height))
.size() } else {
.to_logical::<f32>(pixels_per_point.into()); None
Some(egui::vec2(size.width, size.height)) }
} else {
None
}; };
viewport_info.title = Some(window.title()); 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<T>(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 { mod profiling_scopes {
#![allow(unused_macros)] #![allow(unused_macros)]
#![allow(unused_imports)] #![allow(unused_imports)]

4
crates/egui-winit/src/window_settings.rs

@ -52,6 +52,8 @@ impl WindowSettings {
&self, &self,
mut viewport_builder: ViewportBuilder, mut viewport_builder: ViewportBuilder,
) -> ViewportBuilder { ) -> ViewportBuilder {
crate::profile_function!();
// `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere // `WindowBuilder::with_position` expects inner position in Macos, and outer position elsewhere
// See [`winit::window::WindowBuilder::with_position`] for details. // See [`winit::window::WindowBuilder::with_position`] for details.
let pos_px = if cfg!(target_os = "macos") { let pos_px = if cfg!(target_os = "macos") {
@ -127,6 +129,8 @@ fn clamp_pos_to_monitors<E>(
window_size_pts: egui::Vec2, window_size_pts: egui::Vec2,
position_px: &mut egui::Pos2, position_px: &mut egui::Pos2,
) { ) {
crate::profile_function!();
let monitors = event_loop.available_monitors(); let monitors = event_loop.available_monitors();
// default to primary monitor, in case the correct monitor was disconnected. // default to primary monitor, in case the correct monitor was disconnected.

6
crates/egui/src/context.rs

@ -2391,7 +2391,7 @@ impl Context {
/// [not_supported]: crate::load::LoadError::NotSupported /// [not_supported]: crate::load::LoadError::NotSupported
/// [custom]: crate::load::LoadError::Loading /// [custom]: crate::load::LoadError::Loading
pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult { pub fn try_load_bytes(&self, uri: &str) -> load::BytesLoadResult {
crate::profile_function!(); crate::profile_function!(uri);
let loaders = self.loaders(); let loaders = self.loaders();
let bytes_loaders = loaders.bytes.lock(); let bytes_loaders = loaders.bytes.lock();
@ -2428,7 +2428,7 @@ impl Context {
/// [not_supported]: crate::load::LoadError::NotSupported /// [not_supported]: crate::load::LoadError::NotSupported
/// [custom]: crate::load::LoadError::Loading /// [custom]: crate::load::LoadError::Loading
pub fn try_load_image(&self, uri: &str, size_hint: load::SizeHint) -> load::ImageLoadResult { 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 loaders = self.loaders();
let image_loaders = loaders.image.lock(); let image_loaders = loaders.image.lock();
@ -2471,7 +2471,7 @@ impl Context {
texture_options: TextureOptions, texture_options: TextureOptions,
size_hint: load::SizeHint, size_hint: load::SizeHint,
) -> load::TextureLoadResult { ) -> load::TextureLoadResult {
crate::profile_function!(); crate::profile_function!(uri);
let loaders = self.loaders(); let loaders = self.loaders();
let texture_loaders = loaders.texture.lock(); let texture_loaders = loaders.texture.lock();

2
crates/egui_glow/examples/pure_glow.rs

@ -235,7 +235,7 @@ fn main() {
gl_window.resize(**new_inner_size); 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 { if event_response.repaint {
gl_window.window().request_redraw(); gl_window.window().request_redraw();

4
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 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. /// Call [`Self::paint`] later to paint.

Loading…
Cancel
Save