mirror of
https://github.com/emilk/egui.git
synced 2026-08-30 05:10:03 -04:00
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`
This commit is contained in:
@@ -47,6 +47,7 @@ pub fn viewport_builder<E>(
|
||||
|
||||
#[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::<f32>(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<E>(event_loop: &EventLoopWindowTarget<E>) -> 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::<f32>(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) {
|
||||
|
||||
@@ -393,7 +393,7 @@ impl WinitApp for GlowWinitApp {
|
||||
event_loop: &EventLoopWindowTarget<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
) -> Result<EventResult> {
|
||||
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();
|
||||
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +111,8 @@ impl WgpuWinitApp {
|
||||
|
||||
fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
|
||||
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<UserEvent>,
|
||||
event: &winit::event::Event<'_, UserEvent>,
|
||||
) -> Result<EventResult> {
|
||||
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!(
|
||||
|
||||
@@ -100,3 +100,18 @@ pub fn system_theme(window: &Window, options: &crate::NativeOptions) -> Option<c
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user