1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 06:10:06 -04:00

"Final" touch-ups on wgpu integration

This commit is contained in:
Emil Ernerfeldt
2023-11-14 07:58:04 +01:00
parent 274cdea620
commit aad7ed23d2
5 changed files with 171 additions and 207 deletions

View File

@@ -1,5 +1,9 @@
//! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`]. //! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`].
//! When making changes to one you often also want to apply it to the other. //! When making changes to one you often also want to apply it to the other.
//!
//! This is also very complex code, and not very pretty.
//! There is a bunch of improvements we could do,
//! like removing a bunch of `unwraps`.
use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant}; use std::{cell::RefCell, rc::Rc, sync::Arc, time::Instant};
@@ -96,8 +100,6 @@ trait WinitApp {
fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId>; fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId>;
fn viewport_id_from_window_id(&self, id: &WindowId) -> Option<ViewportId>;
fn save_and_destroy(&mut self); fn save_and_destroy(&mut self);
fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult; fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult;
@@ -492,12 +494,7 @@ mod glow_integration {
window_id: WindowId, window_id: WindowId,
focused_viewport: Option<ViewportId>, focused_viewport: Option<ViewportId>,
) -> EventResult { ) -> EventResult {
let Some(viewport_id) = self let Some(viewport_id) = self.glutin.borrow().viewport_from_window.get(&window_id).copied()
.glutin
.borrow()
.viewport_maps
.get(&window_id)
.copied()
else { else {
return EventResult::Wait; return EventResult::Wait;
}; };
@@ -511,10 +508,10 @@ mod glow_integration {
let viewport = &glutin.viewports[&viewport_id]; let viewport = &glutin.viewports[&viewport_id];
let is_immediate = viewport.viewport_ui_cb.is_none(); let is_immediate = viewport.viewport_ui_cb.is_none();
if is_immediate && viewport_id != ViewportId::ROOT { if is_immediate && viewport_id != ViewportId::ROOT {
// 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(parent_viewport) = glutin.viewports.get(&viewport.ids.parent) { if let Some(parent_viewport) = glutin.viewports.get(&viewport.ids.parent) {
if let Some(window) = parent_viewport.window.as_ref() { if let Some(window) = parent_viewport.window.as_ref() {
// 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.
return EventResult::RepaintNext(window.id()); return EventResult::RepaintNext(window.id());
} }
} }
@@ -553,6 +550,9 @@ mod glow_integration {
.. ..
} = self; } = self;
let mut glutin = glutin.borrow_mut();
let mut painter = painter.borrow_mut();
let egui::FullOutput { let egui::FullOutput {
platform_output, platform_output,
textures_delta, textures_delta,
@@ -561,7 +561,6 @@ mod glow_integration {
viewport_commands, viewport_commands,
} = full_output; } = full_output;
let mut glutin = glutin.borrow_mut();
let GlutinWindowContext { let GlutinWindowContext {
viewports, viewports,
current_gl_context, current_gl_context,
@@ -569,41 +568,36 @@ mod glow_integration {
} = &mut *glutin; } = &mut *glutin;
let viewport = viewports.get_mut(&viewport_id).unwrap(); let viewport = viewports.get_mut(&viewport_id).unwrap();
let window = viewport.window.as_ref().unwrap(); let window = viewport.window.as_ref().unwrap();
let gl_surface = viewport.gl_surface.as_ref().unwrap();
let egui_winit = viewport.egui_winit.as_mut().unwrap();
integration.post_update(app.as_mut(), window); integration.post_update(app.as_mut(), window);
integration.handle_platform_output( integration.handle_platform_output(window, viewport_id, platform_output, egui_winit);
window,
viewport_id,
platform_output,
viewport.egui_winit.as_mut().unwrap(),
);
let clipped_primitives = integration.egui_ctx.tessellate(shapes);
if let Some(gl_surface) = &viewport.gl_surface {
*current_gl_context = Some(
current_gl_context
.take()
.unwrap()
.make_not_current()
.unwrap()
.make_current(gl_surface)
.unwrap(),
);
}
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
painter.borrow().clear(
screen_size_in_pixels,
app.clear_color(&integration.egui_ctx.style().visuals),
);
let pixels_per_point = integration let pixels_per_point = integration
.egui_ctx .egui_ctx
.input_for(viewport_id, |i| i.pixels_per_point()); .input_for(viewport_id, |i| i.pixels_per_point());
painter.borrow_mut().paint_and_update_textures( 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(),
);
let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
painter.clear(
screen_size_in_pixels,
app.clear_color(&integration.egui_ctx.style().visuals),
);
painter.paint_and_update_textures(
screen_size_in_pixels, screen_size_in_pixels,
pixels_per_point, pixels_per_point,
&clipped_primitives, &clipped_primitives,
@@ -612,39 +606,32 @@ mod glow_integration {
{ {
let screenshot_requested = &mut integration.frame.output.screenshot_requested; let screenshot_requested = &mut integration.frame.output.screenshot_requested;
if *screenshot_requested { if *screenshot_requested {
*screenshot_requested = false; *screenshot_requested = false;
let screenshot = painter.borrow().read_screen_rgba(screen_size_in_pixels); let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
integration.frame.screenshot.set(Some(screenshot)); integration.frame.screenshot.set(Some(screenshot));
} }
integration.post_rendering(app.as_mut(), window);
integration.post_rendering(app.as_mut(), viewport.window.as_ref().unwrap());
} }
{ {
crate::profile_scope!("swap_buffers"); crate::profile_scope!("swap_buffers");
if let Err(err) = viewport if let Err(err) = gl_surface.swap_buffers(
.gl_surface current_gl_context
.as_ref() .as_ref()
.expect("failed to get surface to swap buffers") .expect("failed to get current context to swap buffers"),
.swap_buffers( ) {
current_gl_context
.as_ref()
.expect("failed to get current context to swap buffers"),
)
{
log::error!("swap_buffers failed: {err}"); log::error!("swap_buffers failed: {err}");
} }
} }
integration.post_present(viewport.window.as_ref().unwrap()); integration.post_present(window);
// give it time to settle: // give it time to settle:
#[cfg(feature = "__screenshot")] #[cfg(feature = "__screenshot")]
if integration.egui_ctx.frame_nr() == 2 { if integration.egui_ctx.frame_nr() == 2 {
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") { if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
save_screeshot_and_exit(&path, painter, screen_size_in_pixels); save_screeshot_and_exit(&path, &painter, screen_size_in_pixels);
} }
} }
@@ -682,14 +669,14 @@ mod glow_integration {
fn save_screeshot_and_exit( fn save_screeshot_and_exit(
path: &str, path: &str,
painter: &mut Rc<RefCell<egui_glow::Painter>>, painter: &egui_glow::Painter,
screen_size_in_pixels: [u32; 2], screen_size_in_pixels: [u32; 2],
) { ) {
assert!( assert!(
path.ends_with(".png"), path.ends_with(".png"),
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}" "Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
); );
let screenshot = painter.borrow().read_screen_rgba(screen_size_in_pixels); let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
image::save_buffer( image::save_buffer(
path, path,
screenshot.as_raw(), screenshot.as_raw(),
@@ -742,7 +729,7 @@ mod glow_integration {
not_current_gl_context: Option<glutin::context::NotCurrentContext>, not_current_gl_context: Option<glutin::context::NotCurrentContext>,
viewports: ViewportIdMap<Viewport>, viewports: ViewportIdMap<Viewport>,
viewport_maps: HashMap<WindowId, ViewportId>, viewport_from_window: HashMap<WindowId, ViewportId>,
window_maps: ViewportIdMap<WindowId>, window_maps: ViewportIdMap<WindowId>,
} }
@@ -859,10 +846,10 @@ mod glow_integration {
}; };
let not_current_gl_context = Some(gl_context); let not_current_gl_context = Some(gl_context);
let mut viewport_maps = HashMap::default(); let mut viewport_from_window = HashMap::default();
let mut window_maps = ViewportIdMap::default(); let mut window_maps = ViewportIdMap::default();
if let Some(window) = &window { if let Some(window) = &window {
viewport_maps.insert(window.id(), ViewportId::ROOT); viewport_from_window.insert(window.id(), ViewportId::ROOT);
window_maps.insert(ViewportId::ROOT, window.id()); window_maps.insert(ViewportId::ROOT, window.id());
} }
@@ -890,7 +877,7 @@ mod glow_integration {
current_gl_context: None, current_gl_context: None,
not_current_gl_context, not_current_gl_context,
viewports, viewports,
viewport_maps, viewport_from_window,
max_texture_side: None, max_texture_side: None,
window_maps, window_maps,
}; };
@@ -998,7 +985,8 @@ mod glow_integration {
viewport.gl_surface = Some(gl_surface); viewport.gl_surface = Some(gl_surface);
self.current_gl_context = Some(current_gl_context); self.current_gl_context = Some(current_gl_context);
self.viewport_maps.insert(window.id(), viewport.ids.this); self.viewport_from_window
.insert(window.id(), viewport.ids.this);
self.window_maps.insert(viewport.ids.this, window.id()); self.window_maps.insert(viewport.ids.this, window.id());
} }
viewport.window = Some(window); viewport.window = Some(window);
@@ -1102,7 +1090,7 @@ mod glow_integration {
// GC old viewports // GC old viewports
self.viewports self.viewports
.retain(|id, _| active_viewports_ids.contains(id)); .retain(|id, _| active_viewports_ids.contains(id));
self.viewport_maps self.viewport_from_window
.retain(|_, id| active_viewports_ids.contains(id)); .retain(|_, id| active_viewports_ids.contains(id));
self.window_maps self.window_maps
.retain(|id, _| active_viewports_ids.contains(id)); .retain(|id, _| active_viewports_ids.contains(id));
@@ -1479,7 +1467,8 @@ mod glow_integration {
let screen_size_in_pixels: [u32; 2] = window.inner_size().into(); let screen_size_in_pixels: [u32; 2] = window.inner_size().into();
let clipped_primitives = egui_ctx.tessellate(output.shapes); let pixels_per_point = egui_ctx.input_for(ids.this, |i| i.pixels_per_point());
let clipped_primitives = egui_ctx.tessellate(output.shapes, pixels_per_point);
let mut painter = painter.borrow_mut(); let mut painter = painter.borrow_mut();
@@ -1507,7 +1496,6 @@ mod glow_integration {
let gl = &painter.gl().clone(); let gl = &painter.gl().clone();
egui_glow::painter::clear(gl, screen_size_in_pixels, [0.0, 0.0, 0.0, 0.0]); egui_glow::painter::clear(gl, screen_size_in_pixels, [0.0, 0.0, 0.0, 0.0]);
let pixels_per_point = egui_ctx.input_for(ids.this, |i| i.pixels_per_point());
painter.paint_and_update_textures( painter.paint_and_update_textures(
screen_size_in_pixels, screen_size_in_pixels,
pixels_per_point, pixels_per_point,
@@ -1540,7 +1528,9 @@ mod glow_integration {
fn is_focused(&self, window_id: WindowId) -> bool { fn is_focused(&self, window_id: WindowId) -> bool {
if let Some(focused_viewport) = self.focused_viewport { if let Some(focused_viewport) = self.focused_viewport {
if let Some(running) = self.running.as_ref() { if let Some(running) = self.running.as_ref() {
if let Some(window_id) = running.glutin.borrow().viewport_maps.get(&window_id) { if let Some(window_id) =
running.glutin.borrow().viewport_from_window.get(&window_id)
{
return focused_viewport == *window_id; return focused_viewport == *window_id;
} }
} }
@@ -1555,7 +1545,7 @@ mod glow_integration {
fn window(&self, window_id: WindowId) -> Option<Rc<Window>> { fn window(&self, window_id: WindowId) -> Option<Rc<Window>> {
let running = self.running.as_ref()?; let running = self.running.as_ref()?;
let glutin = running.glutin.borrow(); let glutin = running.glutin.borrow();
let viewport_id = *glutin.viewport_maps.get(&window_id)?; let viewport_id = *glutin.viewport_from_window.get(&window_id)?;
if let Some(viewport) = glutin.viewports.get(&viewport_id) { if let Some(viewport) = glutin.viewports.get(&viewport_id) {
viewport.window.clone() viewport.window.clone()
} else { } else {
@@ -1569,12 +1559,6 @@ mod glow_integration {
.and_then(|r| r.glutin.borrow().window_maps.get(&id).copied()) .and_then(|r| r.glutin.borrow().window_maps.get(&id).copied())
} }
fn viewport_id_from_window_id(&self, id: &WindowId) -> Option<ViewportId> {
self.running
.as_ref()
.and_then(|r| r.glutin.borrow().viewport_maps.get(id).copied())
}
fn save_and_destroy(&mut self) { fn save_and_destroy(&mut self) {
if let Some(mut running) = self.running.take() { if let Some(mut running) = self.running.take() {
crate::profile_function!(); crate::profile_function!();
@@ -1670,7 +1654,7 @@ mod glow_integration {
running running
.glutin .glutin
.borrow_mut() .borrow_mut()
.viewport_maps .viewport_from_window
.get(window_id) .get(window_id)
.copied() .copied()
}) })
@@ -1684,7 +1668,7 @@ mod glow_integration {
// This solves an issue where the app would panic when minimizing on Windows. // This solves an issue where the app would panic when minimizing on Windows.
let glutin = &mut *running.glutin.borrow_mut(); let glutin = &mut *running.glutin.borrow_mut();
if 0 < physical_size.width && 0 < physical_size.height { if 0 < physical_size.width && 0 < physical_size.height {
if let Some(id) = glutin.viewport_maps.get(window_id) { if let Some(id) = glutin.viewport_from_window.get(window_id) {
glutin.resize(*id, *physical_size); glutin.resize(*id, *physical_size);
} }
} }
@@ -1695,7 +1679,7 @@ mod glow_integration {
} => { } => {
let glutin = &mut *running.glutin.borrow_mut(); let glutin = &mut *running.glutin.borrow_mut();
repaint_asap = true; repaint_asap = true;
if let Some(id) = glutin.viewport_maps.get(window_id) { if let Some(id) = glutin.viewport_from_window.get(window_id) {
glutin.resize(*id, **new_inner_size); glutin.resize(*id, **new_inner_size);
} }
} }
@@ -1703,7 +1687,7 @@ mod glow_integration {
if running if running
.glutin .glutin
.borrow() .borrow()
.viewport_maps .viewport_from_window
.get(window_id) .get(window_id)
.map_or(false, |id| *id == ViewportId::ROOT) .map_or(false, |id| *id == ViewportId::ROOT)
&& running.integration.should_close() => && running.integration.should_close() =>
@@ -1716,7 +1700,8 @@ mod glow_integration {
let event_response = 'res: { let event_response = 'res: {
let mut glutin = running.glutin.borrow_mut(); let mut glutin = running.glutin.borrow_mut();
if let Some(viewport_id) = glutin.viewport_maps.get(window_id).copied() if let Some(viewport_id) =
glutin.viewport_from_window.get(window_id).copied()
{ {
if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) {
break 'res running.integration.on_event( break 'res running.integration.on_event(
@@ -1758,7 +1743,9 @@ mod glow_integration {
crate::profile_scope!("on_accesskit_action_request"); crate::profile_scope!("on_accesskit_action_request");
let mut glutin = running.glutin.borrow_mut(); let mut glutin = running.glutin.borrow_mut();
if let Some(viewport_id) = glutin.viewport_maps.get(window_id).copied() { if let Some(viewport_id) =
glutin.viewport_from_window.get(window_id).copied()
{
if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) { if let Some(viewport) = glutin.viewports.get_mut(&viewport_id) {
viewport viewport
.egui_winit .egui_winit
@@ -1823,8 +1810,11 @@ mod wgpu_integration {
/// `None` for sync viewports. /// `None` for sync viewports.
viewport_ui_cb: Option<Arc<ViewportUiCallback>>, viewport_ui_cb: Option<Arc<ViewportUiCallback>>,
// `window` and `egui_winit` are initialized together. /// Window surface state that's initialized when the app starts running via a Resumed event
/// and on Android will also be destroyed if the application is paused.
window: Option<Rc<Window>>, window: Option<Rc<Window>>,
/// `window` and `egui_winit` are initialized together.
egui_winit: Option<egui_winit::State>, egui_winit: Option<egui_winit::State>,
} }
@@ -1837,13 +1827,15 @@ mod wgpu_integration {
) { ) {
crate::profile_function!(); crate::profile_function!();
let id = self.ids.this; let viewport_id = self.ids.this;
if let Ok(new_window) = create_winit_window_builder(&self.builder).build(event_loop) { if let Ok(new_window) = create_winit_window_builder(&self.builder).build(event_loop) {
windows_id.insert(new_window.id(), id); windows_id.insert(new_window.id(), viewport_id);
if let Err(err) = pollster::block_on(painter.set_window(id, Some(&new_window))) { if let Err(err) =
log::error!("on set_window: viewport_id {id:?} {err}"); pollster::block_on(painter.set_window(viewport_id, Some(&new_window)))
{
log::error!("on set_window: viewport_id {viewport_id:?} {err}");
} }
self.egui_winit = Some(egui_winit::State::new( self.egui_winit = Some(egui_winit::State::new(
@@ -1865,7 +1857,7 @@ mod wgpu_integration {
pub struct SharedState { pub struct SharedState {
viewports: Viewports, viewports: Viewports,
painter: egui_wgpu::winit::Painter, painter: egui_wgpu::winit::Painter,
viewport_maps: HashMap<WindowId, ViewportId>, viewport_from_window: HashMap<WindowId, ViewportId>,
} }
/// State that is initialized when the application is first starts running via /// State that is initialized when the application is first starts running via
@@ -1873,20 +1865,25 @@ mod wgpu_integration {
/// initialized once the application has an associated `SurfaceView`. /// initialized once the application has an associated `SurfaceView`.
struct WgpuWinitRunning { struct WgpuWinitRunning {
integration: epi_integration::EpiIntegration, integration: epi_integration::EpiIntegration,
/// The users application.
app: Box<dyn epi::App>, app: Box<dyn epi::App>,
/// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer. /// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.
shared: Rc<RefCell<SharedState>>, shared: Rc<RefCell<SharedState>>,
} }
struct WgpuWinitApp { struct WgpuWinitApp {
repaint_proxy: Arc<Mutex<EventLoopProxy<UserEvent>>>, repaint_proxy: Arc<Mutex<EventLoopProxy<UserEvent>>>,
app_name: String, app_name: String,
native_options: epi::NativeOptions, native_options: epi::NativeOptions,
/// Set at initialization, then taken and set to `None` in `init_run_state`.
app_creator: Option<epi::AppCreator>, app_creator: Option<epi::AppCreator>,
/// Set when we are actually up and running.
running: Option<WgpuWinitRunning>, running: Option<WgpuWinitRunning>,
/// Window surface state that's initialized when the app starts running via a Resumed event
/// and on Android will also be destroyed if the application is paused.
focused_viewport: Option<ViewportId>, focused_viewport: Option<ViewportId>,
} }
@@ -1898,6 +1895,7 @@ mod wgpu_integration {
app_creator: epi::AppCreator, app_creator: epi::AppCreator,
) -> Self { ) -> Self {
crate::profile_function!(); crate::profile_function!();
#[cfg(feature = "__screenshot")] #[cfg(feature = "__screenshot")]
assert!( assert!(
std::env::var("EFRAME_SCREENSHOT_TO").is_err(), std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
@@ -1922,12 +1920,12 @@ mod wgpu_integration {
let SharedState { let SharedState {
viewports, viewports,
painter, painter,
viewport_maps, viewport_from_window,
} = &mut *shared; } = &mut *shared;
for viewport in viewports.values_mut() { for viewport in viewports.values_mut() {
if viewport.window.is_none() { if viewport.window.is_none() {
viewport.init_window(viewport_maps, painter, event_loop); viewport.init_window(viewport_from_window, painter, event_loop);
} }
} }
} }
@@ -2034,8 +2032,8 @@ mod wgpu_integration {
integration.warm_up(app.as_mut(), &window, &mut egui_winit); integration.warm_up(app.as_mut(), &window, &mut egui_winit);
} }
let mut viewport_maps = HashMap::default(); let mut viewport_from_window = HashMap::default();
viewport_maps.insert(window.id(), ViewportId::ROOT); viewport_from_window.insert(window.id(), ViewportId::ROOT);
let mut viewports = Viewports::default(); let mut viewports = Viewports::default();
viewports.insert( viewports.insert(
@@ -2050,7 +2048,7 @@ mod wgpu_integration {
); );
let shared = Rc::new(RefCell::new(SharedState { let shared = Rc::new(RefCell::new(SharedState {
viewport_maps, viewport_from_window,
viewports, viewports,
painter, painter,
})); }));
@@ -2127,22 +2125,18 @@ mod wgpu_integration {
} = immediate_viewport; } = immediate_viewport;
let input = { let input = {
let mut shared = shared.borrow_mut();
let SharedState { let SharedState {
viewports, viewports,
painter, painter,
viewport_maps, viewport_from_window,
} = &mut *shared; } = &mut *shared.borrow_mut();
let viewport = initialize_or_update_viewport(viewports, ids, builder, None, None); let viewport = initialize_or_update_viewport(viewports, ids, builder, None, None);
if viewport.window.is_none() { if viewport.window.is_none() {
viewport.init_window(viewport_maps, painter, event_loop); viewport.init_window(viewport_from_window, painter, event_loop);
} }
let Some(winit_state) = &mut viewport.egui_winit else { let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit) else {
return;
};
let Some(window) = &viewport.window else {
return; return;
}; };
@@ -2153,7 +2147,8 @@ mod wgpu_integration {
// ------------------------------------------ // ------------------------------------------
// Run the user code, which could re-entrantly call this function again (!) // Run the user code, which could re-entrantly call this function again (!).
// Make sure no locks are held during this call.
let output = egui_ctx.run(input, |ctx| { let output = egui_ctx.run(input, |ctx| {
viewport_ui_cb(ctx); viewport_ui_cb(ctx);
}); });
@@ -2183,7 +2178,7 @@ mod wgpu_integration {
} }
let pixels_per_point = egui_ctx.input_for(ids.this, |i| i.pixels_per_point()); let pixels_per_point = egui_ctx.input_for(ids.this, |i| i.pixels_per_point());
let clipped_primitives = egui_ctx.tessellate(output.shapes); let clipped_primitives = egui_ctx.tessellate(output.shapes, pixels_per_point);
painter.paint_and_update_textures( painter.paint_and_update_textures(
ids.this, ids.this,
pixels_per_point, pixels_per_point,
@@ -2204,12 +2199,15 @@ mod wgpu_integration {
} }
fn is_focused(&self, window_id: WindowId) -> bool { fn is_focused(&self, window_id: WindowId) -> bool {
if let Some(focus) = self.focused_viewport { let viewport_id = self.running.as_ref().and_then(|r| {
self.viewport_id_from_window_id(&window_id) r.shared
.map_or(false, |i| i == focus) .borrow()
} else { .viewport_from_window
false .get(&window_id)
} .copied()
});
self.focused_viewport.is_some() && self.focused_viewport == viewport_id
} }
fn integration(&self) -> Option<&EpiIntegration> { fn integration(&self) -> Option<&EpiIntegration> {
@@ -2222,7 +2220,7 @@ mod wgpu_integration {
.and_then(|r| { .and_then(|r| {
let shared = r.shared.borrow(); let shared = r.shared.borrow();
shared shared
.viewport_maps .viewport_from_window
.get(&window_id) .get(&window_id)
.and_then(|id| shared.viewports.get(id).map(|v| v.window.clone())) .and_then(|id| shared.viewports.get(id).map(|v| v.window.clone()))
}) })
@@ -2230,13 +2228,17 @@ mod wgpu_integration {
} }
fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId> { fn window_id_from_viewport_id(&self, id: ViewportId) -> Option<WindowId> {
self.running.as_ref().and_then(|r| { Some(
r.shared self.running
.as_ref()?
.shared
.borrow() .borrow()
.viewports .viewports
.get(&id) .get(&id)?
.and_then(|v| v.window.as_ref().map(|w| w.id())) .window
}) .as_ref()?
.id(),
)
} }
fn save_and_destroy(&mut self) { fn save_and_destroy(&mut self) {
@@ -2295,6 +2297,7 @@ mod wgpu_integration {
)?; )?;
self.init_run_state(event_loop, storage, window, builder)? self.init_run_state(event_loop, storage, window, builder)?
}; };
EventResult::RepaintNow( EventResult::RepaintNow(
running.shared.borrow().viewports[&ViewportId::ROOT] running.shared.borrow().viewports[&ViewportId::ROOT]
.window .window
@@ -2325,11 +2328,11 @@ mod wgpu_integration {
if let Some(running) = &mut self.running { if let Some(running) = &mut self.running {
let mut shared_lock = running.shared.borrow_mut(); let mut shared_lock = running.shared.borrow_mut();
let SharedState { let SharedState {
viewport_maps, viewport_from_window,
viewports, viewports,
.. ..
} = &mut *shared_lock; } = &mut *shared_lock;
if let Some(viewport) = viewport_maps if let Some(viewport) = viewport_from_window
.get(window_id) .get(window_id)
.and_then(|id| viewports.get_mut(id)) .and_then(|id| viewports.get_mut(id))
{ {
@@ -2347,12 +2350,6 @@ mod wgpu_integration {
_ => EventResult::Wait, _ => EventResult::Wait,
}) })
} }
fn viewport_id_from_window_id(&self, id: &WindowId) -> Option<ViewportId> {
self.running
.as_ref()
.and_then(|r| r.shared.borrow().viewport_maps.get(id).copied())
}
} }
impl WgpuWinitRunning { impl WgpuWinitRunning {
@@ -2391,6 +2388,11 @@ mod wgpu_integration {
window_id: WindowId, window_id: WindowId,
focused_viewport: Option<ViewportId>, focused_viewport: Option<ViewportId>,
) -> EventResult { ) -> EventResult {
let Some(viewport_id) = self.shared.borrow().viewport_from_window.get(&window_id).copied()
else {
return EventResult::Wait;
};
#[cfg(feature = "puffin")] #[cfg(feature = "puffin")]
puffin::GlobalProfiler::lock().new_frame(); puffin::GlobalProfiler::lock().new_frame();
@@ -2406,21 +2408,16 @@ mod wgpu_integration {
let mut shared_lock = shared.borrow_mut(); let mut shared_lock = shared.borrow_mut();
let SharedState { let SharedState {
viewports, viewports, painter, ..
painter,
viewport_maps,
} = &mut *shared_lock; } = &mut *shared_lock;
let Some(viewport_id) = viewport_maps.get(&window_id).copied() else {
return EventResult::Wait;
};
let Some(viewport) = viewports.get(&viewport_id) else { let Some(viewport) = viewports.get(&viewport_id) else {
return EventResult::Wait; return EventResult::Wait;
}; };
// This is used to not render a viewport if is sync
if viewport_id != ViewportId::ROOT && viewport.viewport_ui_cb.is_none() { 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(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());
@@ -2477,22 +2474,19 @@ mod wgpu_integration {
let SharedState { let SharedState {
viewports, viewports,
painter, painter,
viewport_maps, viewport_from_window,
} = &mut *shared; } = &mut *shared;
let Some(viewport_id) = viewport_maps.get(&window_id).copied() else {
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;
}; };
let Viewport { let Viewport {
window, egui_winit, .. window: Some(window),
} = viewport; egui_winit: Some(egui_winit),
..
let Some(window) = window else { } = viewport
else {
return EventResult::Wait; return EventResult::Wait;
}; };
@@ -2502,35 +2496,31 @@ mod wgpu_integration {
platform_output, platform_output,
textures_delta, textures_delta,
shapes, shapes,
viewports: mut out_viewports, viewports: out_viewports,
viewport_commands, viewport_commands,
} = full_output; } = full_output;
integration.handle_platform_output( integration.handle_platform_output(window, viewport_id, platform_output, egui_winit);
window,
viewport_id,
platform_output,
egui_winit.as_mut().unwrap(),
);
let clipped_primitives = integration.egui_ctx.tessellate(shapes); {
let pixels_per_point = integration
.egui_ctx
.input_for(viewport_id, |i| i.pixels_per_point());
let screenshot_requested = &mut integration.frame.output.screenshot_requested; let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
let pixels_per_point = integration let screenshot_requested = &mut integration.frame.output.screenshot_requested;
.egui_ctx let screenshot = painter.paint_and_update_textures(
.input_for(viewport_id, |i| i.pixels_per_point()); viewport_id,
pixels_per_point,
let screenshot = painter.paint_and_update_textures( app.clear_color(&integration.egui_ctx.style().visuals),
viewport_id, &clipped_primitives,
pixels_per_point, &textures_delta,
app.clear_color(&integration.egui_ctx.style().visuals), *screenshot_requested,
&clipped_primitives, );
&textures_delta, *screenshot_requested = false;
*screenshot_requested, integration.frame.screenshot.set(screenshot);
); }
*screenshot_requested = false;
integration.frame.screenshot.set(screenshot);
integration.post_rendering(app.as_mut(), window); integration.post_rendering(app.as_mut(), window);
integration.post_present(window); integration.post_present(window);
@@ -2538,24 +2528,6 @@ mod wgpu_integration {
let mut active_viewports_ids = ViewportIdSet::default(); let mut active_viewports_ids = ViewportIdSet::default();
active_viewports_ids.insert(ViewportId::ROOT); active_viewports_ids.insert(ViewportId::ROOT);
// Update exisitng viewports:
out_viewports.retain_mut(
|ViewportOutput {
ids: ViewportIdPair { this, parent },
viewport_ui_cb,
..
}| {
if let Some(viewport) = viewports.get_mut(this) {
viewport.viewport_ui_cb = viewport_ui_cb.clone();
viewport.ids.parent = *parent;
active_viewports_ids.insert(*this);
false
} else {
true
}
},
);
// Add new viewports, and update existing ones: // Add new viewports, and update existing ones:
for ViewportOutput { for ViewportOutput {
ids, ids,
@@ -2574,6 +2546,7 @@ mod wgpu_integration {
); );
} }
// Handle viewport commands:
for (viewport_id, command) in viewport_commands { for (viewport_id, command) in viewport_commands {
if let Some(window) = viewports if let Some(window) = viewports
.get(&viewport_id) .get(&viewport_id)
@@ -2590,10 +2563,10 @@ mod wgpu_integration {
// Prune dead viewports: // Prune dead viewports:
viewports.retain(|id, _| active_viewports_ids.contains(id)); viewports.retain(|id, _| active_viewports_ids.contains(id));
viewport_maps.retain(|_, id| active_viewports_ids.contains(id)); viewport_from_window.retain(|_, id| active_viewports_ids.contains(id));
painter.gc_viewports(&active_viewports_ids); painter.gc_viewports(&active_viewports_ids);
let window = viewport_maps let window = viewport_from_window
.get(&window_id) .get(&window_id)
.and_then(|id| viewports.get(id)) .and_then(|id| viewports.get(id))
.and_then(|vp| vp.window.as_ref()); .and_then(|vp| vp.window.as_ref());
@@ -2629,7 +2602,7 @@ mod wgpu_integration {
} = self; } = self;
let mut shared = shared.borrow_mut(); let mut shared = shared.borrow_mut();
let viewport_id = shared.viewport_maps.get(&window_id).copied(); let viewport_id = shared.viewport_from_window.get(&window_id).copied();
// On Windows, if a window is resized by the user, it should repaint synchronously, inside the // On Windows, if a window is resized by the user, it should repaint synchronously, inside the
// event handler. // event handler.
@@ -2671,7 +2644,7 @@ mod wgpu_integration {
if let (Some(width), Some(height), Some(viewport_id)) = ( if let (Some(width), Some(height), Some(viewport_id)) = (
NonZeroU32::new(new_inner_size.width), NonZeroU32::new(new_inner_size.width),
NonZeroU32::new(new_inner_size.height), NonZeroU32::new(new_inner_size.height),
shared.viewport_maps.get(&window_id).copied(), shared.viewport_from_window.get(&window_id).copied(),
) { ) {
repaint_asap = true; repaint_asap = true;
shared.painter.on_window_resized(viewport_id, width, height); shared.painter.on_window_resized(viewport_id, width, height);

View File

@@ -1631,22 +1631,12 @@ impl Context {
}) })
} }
/// Tessellate the given shapes into triangle meshes.
///
/// Will use the `pixels_per_point` of the last viewport for feathering (anti-aliasing).
pub fn tessellate(&self, shapes: Vec<ClippedShape>) -> Vec<ClippedPrimitive> {
let last_viewport = self.read(|ctx| ctx.last_viewport);
// here we expect that we are the only user of context, since frame is ended
let pixels_per_point = self.input_for(last_viewport, |i| i.pixels_per_point());
self.tessellate_with_pixels_per_point(shapes, pixels_per_point)
}
/// Tessellate the given shapes into triangle meshes. /// Tessellate the given shapes into triangle meshes.
/// ///
/// `pixels_per_point` is used for feathering (anti-aliasing). /// `pixels_per_point` is used for feathering (anti-aliasing).
pub fn tessellate_with_pixels_per_point( /// You can use [`Self::pixels_per_point`] for this,
/// or whatever is appropiate for your viewport.
pub fn tessellate(
&self, &self,
shapes: Vec<ClippedShape>, shapes: Vec<ClippedShape>,
pixels_per_point: f32, pixels_per_point: f32,

View File

@@ -9,6 +9,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
{ {
let ctx = egui::Context::default(); let ctx = egui::Context::default();
let mut demo_windows = egui_demo_lib::DemoWindows::default(); let mut demo_windows = egui_demo_lib::DemoWindows::default();
let pixels_per_point = 1.0;
// The most end-to-end benchmark. // The most end-to-end benchmark.
c.bench_function("demo_with_tessellate__realistic", |b| { c.bench_function("demo_with_tessellate__realistic", |b| {
@@ -16,7 +17,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
let full_output = ctx.run(RawInput::default(), |ctx| { let full_output = ctx.run(RawInput::default(), |ctx| {
demo_windows.ui(ctx); demo_windows.ui(ctx);
}); });
ctx.tessellate(full_output.shapes) ctx.tessellate(full_output.shapes, pixels_per_point)
}); });
}); });
@@ -32,7 +33,7 @@ pub fn criterion_benchmark(c: &mut Criterion) {
demo_windows.ui(ctx); demo_windows.ui(ctx);
}); });
c.bench_function("demo_only_tessellate", |b| { c.bench_function("demo_only_tessellate", |b| {
b.iter(|| ctx.tessellate(full_output.shapes.clone())); b.iter(|| ctx.tessellate(full_output.shapes.clone(), pixels_per_point));
}); });
} }

View File

@@ -71,13 +71,14 @@ fn test_egui_e2e() {
let mut demo_windows = crate::DemoWindows::default(); let mut demo_windows = crate::DemoWindows::default();
let ctx = egui::Context::default(); let ctx = egui::Context::default();
let raw_input = egui::RawInput::default(); let raw_input = egui::RawInput::default();
let pixels_per_point = 1.0;
const NUM_FRAMES: usize = 5; const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
let full_output = ctx.run(raw_input.clone(), |ctx| { let full_output = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx); demo_windows.ui(ctx);
}); });
let clipped_primitives = ctx.tessellate(full_output.shapes); let clipped_primitives = ctx.tessellate(full_output.shapes, pixels_per_point);
assert!(!clipped_primitives.is_empty()); assert!(!clipped_primitives.is_empty());
} }
} }
@@ -90,13 +91,14 @@ fn test_egui_zero_window_size() {
screen_rect: Some(egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2::ZERO)), screen_rect: Some(egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2::ZERO)),
..Default::default() ..Default::default()
}; };
let pixels_per_point = 1.0;
const NUM_FRAMES: usize = 5; const NUM_FRAMES: usize = 5;
for _ in 0..NUM_FRAMES { for _ in 0..NUM_FRAMES {
let full_output = ctx.run(raw_input.clone(), |ctx| { let full_output = ctx.run(raw_input.clone(), |ctx| {
demo_windows.ui(ctx); demo_windows.ui(ctx);
}); });
let clipped_primitives = ctx.tessellate(full_output.shapes); let clipped_primitives = ctx.tessellate(full_output.shapes, pixels_per_point);
assert!( assert!(
clipped_primitives.is_empty(), clipped_primitives.is_empty(),
"There should be nothing to show, has at least one primitive with clip_rect: {:?}", "There should be nothing to show, has at least one primitive with clip_rect: {:?}",

View File

@@ -88,13 +88,11 @@ impl EguiGlow {
self.painter.set_texture(id, &image_delta); self.painter.set_texture(id, &image_delta);
} }
let clipped_primitives = self.egui_ctx.tessellate(shapes); let pixels_per_point = self.egui_ctx.pixels_per_point();
let clipped_primitives = self.egui_ctx.tessellate(shapes, pixels_per_point);
let dimensions: [u32; 2] = window.inner_size().into(); let dimensions: [u32; 2] = window.inner_size().into();
self.painter.paint_primitives( self.painter
dimensions, .paint_primitives(dimensions, pixels_per_point, &clipped_primitives);
self.egui_ctx.pixels_per_point(),
&clipped_primitives,
);
for id in textures_delta.free.drain(..) { for id in textures_delta.free.drain(..) {
self.painter.free_texture(id); self.painter.free_texture(id);