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:
parent
960b01b67a
commit
30ee478caf
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,21 +67,32 @@ impl RenderState {
|
|||
depth_format: Option<wgpu::TextureFormat>,
|
||||
msaa_samples: u32,
|
||||
) -> Result<Self, WgpuError> {
|
||||
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)?;
|
||||
crate::profile_scope!("RenderState::create"); // async yield give bad names using `profile_function`
|
||||
|
||||
let target_format =
|
||||
crate::preferred_framebuffer_format(&surface.get_capabilities(&adapter).formats)?;
|
||||
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 (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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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::<Vertex>() * 1024) as _;
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
|
|
|
|||
|
|
@ -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::<f32>(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::<f32>(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<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 {
|
||||
#![allow(unused_macros)]
|
||||
#![allow(unused_imports)]
|
||||
|
|
|
|||
|
|
@ -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<E>(
|
|||
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.
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue