From fb07dc1bd78db554dc0128dbc6871ca7ab5ad0b9 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Wed, 15 Nov 2023 18:43:52 +0100 Subject: [PATCH] Move the viewport commands into the per-viewport output --- crates/eframe/src/native/run.rs | 70 ++++++++++++++++----------------- crates/egui/src/context.rs | 49 ++++++++++++----------- crates/egui/src/data/output.rs | 25 +++++++----- crates/egui/src/viewport.rs | 23 +++++++++++ 4 files changed, 98 insertions(+), 69 deletions(-) diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 148db00c988..12a08b0271e 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -564,7 +564,6 @@ mod glow_integration { shapes, pixels_per_point, viewports: viewports_out, - viewport_commands, } = full_output; let GlutinWindowContext { @@ -649,19 +648,6 @@ mod glow_integration { glutin.process_viewport_updates(viewports_out, focused_viewport); - for (viewport_id, command) in viewport_commands { - if let Some(viewport) = glutin.viewports.get(&viewport_id) { - if let Some(window) = &viewport.window { - let is_viewport_focused = focused_viewport == Some(viewport_id); - egui_winit::process_viewport_commands( - [command], - window, - is_viewport_focused, - ); - } - } - } - if integration.should_close() { EventResult::Exit } else { @@ -1169,7 +1155,7 @@ mod glow_integration { fn process_viewport_updates( &mut self, - viewport_output: Vec, + viewport_output: ViewportIdMap, focused_viewport: Option, ) { crate::profile_function!(); @@ -1177,13 +1163,17 @@ mod glow_integration { let mut active_viewports_ids = ViewportIdSet::default(); active_viewports_ids.insert(ViewportId::ROOT); - for ViewportOutput { - ids, - builder, - viewport_ui_cb, - } in viewport_output + for ( + viewport_id, + ViewportOutput { + ids, + builder, + viewport_ui_cb, + commands, + }, + ) in viewport_output { - active_viewports_ids.insert(ids.this); + active_viewports_ids.insert(viewport_id); initialize_or_update_viewport( &mut self.viewports, @@ -1192,6 +1182,17 @@ mod glow_integration { viewport_ui_cb, focused_viewport, ); + + if let Some(viewport) = self.viewports.get(&viewport_id) { + if let Some(window) = &viewport.window { + let is_viewport_focused = focused_viewport == Some(viewport_id); + egui_winit::process_viewport_commands( + commands, + window, + is_viewport_focused, + ); + } + } } // GC old viewports @@ -2455,10 +2456,7 @@ mod wgpu_integration { let raw_input = egui_winit.as_mut().unwrap().take_egui_input( window, - ViewportIdPair { - this: viewport_id, - parent: ids.parent, - }, + ViewportIdPair::from_self_and_parent(viewport_id, ids.parent), ); integration.pre_update(window); @@ -2504,7 +2502,6 @@ mod wgpu_integration { shapes, pixels_per_point, viewports: out_viewports, - viewport_commands, } = full_output; integration.handle_platform_output(window, viewport_id, platform_output, egui_winit); @@ -2532,13 +2529,17 @@ mod wgpu_integration { active_viewports_ids.insert(ViewportId::ROOT); // Add new viewports, and update existing ones: - for ViewportOutput { - ids, - builder, - viewport_ui_cb, - } in out_viewports + for ( + viewport_id, + ViewportOutput { + ids, + builder, + viewport_ui_cb, + commands, + }, + ) in out_viewports { - active_viewports_ids.insert(ids.this); + active_viewports_ids.insert(viewport_id); initialize_or_update_viewport( viewports, @@ -2547,16 +2548,13 @@ mod wgpu_integration { viewport_ui_cb, focused_viewport, ); - } - // Handle viewport commands: - for (viewport_id, command) in viewport_commands { if let Some(window) = viewports .get(&viewport_id) .and_then(|vp| vp.window.as_ref()) { let is_viewport_focused = focused_viewport == Some(viewport_id); - egui_winit::process_viewport_commands([command], window, is_viewport_focused); + egui_winit::process_viewport_commands(commands, window, is_viewport_focused); } } diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index e9cef9d7248..e632a14e764 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -146,6 +146,7 @@ struct ViewportState { // The output of a frame: graphics: GraphicLayers, output: PlatformOutput, + commands: Vec, } /// Per-viewport state related to repaint scheduling. @@ -185,7 +186,6 @@ struct ContextImpl { viewport_parents: ViewportIdMap, viewports: ViewportIdMap, - viewport_commands: Vec<(ViewportId, ViewportCommand)>, embed_viewports: bool, @@ -1563,24 +1563,36 @@ impl ContextImpl { true }); + // This is used to resume the last frame! + self.viewport_stack.pop(); + + // The last viewport is not necessarily the root viewport, + // just the top _immediate_ viewport. + let is_last = self.viewport_stack.is_empty(); + let out_viewports = self .viewports - .iter() + .iter_mut() .map(|(&id, viewport)| { let parent = *self.viewport_parents.entry(id).or_default(); - ViewportOutput { - ids: ViewportIdPair { this: id, parent }, - builder: viewport.builder.clone(), - viewport_ui_cb: viewport.viewport_ui_cb.clone(), - } + let commands = if is_last { + std::mem::take(&mut viewport.commands) + } else { + vec![] + }; + + ( + id, + ViewportOutput { + ids: ViewportIdPair::from_self_and_parent(id, parent), + builder: viewport.builder.clone(), + viewport_ui_cb: viewport.viewport_ui_cb.clone(), + commands, + }, + ) }) .collect(); - // This is used to resume the last frame! - self.viewport_stack.pop(); - - let is_last = self.viewport_stack.is_empty(); - if is_last { // Remove dead viewports: self.viewports.retain(|id, _| all_viewport_ids.contains(id)); @@ -1597,12 +1609,6 @@ impl ContextImpl { shapes, pixels_per_point, viewports: out_viewports, - // We should not process viewport commands when we are a sync viewport, because that will cause a deadlock for egui backend - viewport_commands: if is_last { - std::mem::take(&mut self.viewport_commands) - } else { - Vec::new() - }, } } } @@ -2518,7 +2524,7 @@ impl Context { /// Send a command to a speicfic viewport. pub fn viewport_command_for(&self, id: ViewportId, command: ViewportCommand) { - self.write(|ctx| ctx.viewport_commands.push((id, command))); + self.write(|ctx| ctx.viewport_for(id).commands.push(command)); } /// This creates a new native window, if possible. @@ -2615,10 +2621,7 @@ impl Context { viewport.used = true; viewport.viewport_ui_cb = None; // it is immediate - ViewportIdPair { - this: new_viewport_id, - parent: parent_viewport_id, - } + ViewportIdPair::from_self_and_parent(new_viewport_id, parent_viewport_id) }); let mut out = None; diff --git a/crates/egui/src/data/output.rs b/crates/egui/src/data/output.rs index a382fdbf364..99dc49153ed 100644 --- a/crates/egui/src/data/output.rs +++ b/crates/egui/src/data/output.rs @@ -1,6 +1,6 @@ //! All the data egui returns to the backend at the end of each frame. -use crate::{ViewportCommand, ViewportId, ViewportOutput, WidgetType}; +use crate::{ViewportIdMap, ViewportOutput, WidgetType}; /// What egui emits each frame from [`crate::Context::run`]. /// @@ -26,11 +26,8 @@ pub struct FullOutput { /// You can pass this to [`crate::Context::tessellate`] together with [`Self::shapes`]. pub pixels_per_point: f32, - /// All the active viewports, excluding the root. - pub viewports: Vec, - - /// Commands sent to different viewports. - pub viewport_commands: Vec<(ViewportId, ViewportCommand)>, + /// All the active viewports, including the root. + pub viewports: ViewportIdMap, } impl FullOutput { @@ -41,16 +38,24 @@ impl FullOutput { textures_delta, shapes, pixels_per_point, - mut viewports, - mut viewport_commands, + viewports, } = newer; self.platform_output.append(platform_output); self.textures_delta.append(textures_delta); self.shapes = shapes; // Only paint the latest self.pixels_per_point = pixels_per_point; // Use latest - self.viewports.append(&mut viewports); - self.viewport_commands.append(&mut viewport_commands); + + for (id, new_viewport) in viewports { + match self.viewports.entry(id) { + std::collections::hash_map::Entry::Vacant(entry) => { + entry.insert(new_viewport); + } + std::collections::hash_map::Entry::Occupied(mut entry) => { + entry.get_mut().append(new_viewport); + } + } + } } } diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 612c7fd98e5..c2fdb9a4eaf 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -83,6 +83,11 @@ impl ViewportIdPair { this: ViewportId::ROOT, parent: ViewportId::ROOT, }; + + #[inline] + pub fn from_self_and_parent(this: ViewportId, parent: ViewportId) -> Self { + Self { this, parent } + } } /// The user-code that shows the ui in the viewport, used for deferred viewports. @@ -669,12 +674,30 @@ pub struct ViewportOutput { /// /// `None` for immediate viewports and the ROOT viewport. pub viewport_ui_cb: Option>, + + /// Commands to change the viewport, e.g. window title and size. + pub commands: Vec, } impl ViewportOutput { pub fn id(&self) -> ViewportId { self.ids.this } + + /// Add on new output. + pub fn append(&mut self, newer: Self) { + let Self { + ids, + builder, + viewport_ui_cb, + mut commands, + } = newer; + + self.ids = ids; + self.builder.patch(&builder); + self.viewport_ui_cb = viewport_ui_cb; + self.commands.append(&mut commands); + } } /// Viewport for immediate rendering.