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

wgpu implementation for viewport sync

This commit is contained in:
Konkitoman
2023-08-06 10:11:57 +03:00
parent 860160ed05
commit 085724b685
2 changed files with 224 additions and 66 deletions

View File

@@ -1696,23 +1696,56 @@ mod wgpu_integration {
use super::*; use super::*;
/// State that is initialized when the application is first starts running via #[derive(Clone)]
/// a Resumed event. On Android this ensures that any graphics state is only pub struct Windows(
/// initialized once the application has an associated `SurfaceView`. Arc<
struct WgpuWinitRunning { RwLock<
painter: egui_wgpu::winit::Painter, HashMap<
integration: Arc<RwLock<epi_integration::EpiIntegration>>,
app: Box<dyn epi::App>,
windows: HashMap<
u64, u64,
( (
Option<Arc<RwLock<winit::window::Window>>>, Option<Arc<RwLock<winit::window::Window>>>,
Option<egui_winit::State>, Arc<RwLock<Option<egui_winit::State>>>,
Option<Arc<Box<ViewportRender>>>, Option<Arc<Box<ViewportRender>>>,
u64, u64,
ViewportBuilder, ViewportBuilder,
), ),
>, >,
>,
>,
);
unsafe impl Send for Windows {}
unsafe impl Sync for Windows {}
impl std::ops::Deref for Windows {
type Target = Arc<
RwLock<
HashMap<
u64,
(
Option<Arc<RwLock<winit::window::Window>>>,
Arc<RwLock<Option<egui_winit::State>>>,
Option<Arc<Box<ViewportRender>>>,
u64,
ViewportBuilder,
),
>,
>,
>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
/// State that is initialized when the application is first starts running via
/// a Resumed event. On Android this ensures that any graphics state is only
/// initialized once the application has an associated `SurfaceView`.
struct WgpuWinitRunning {
painter: Arc<RwLock<egui_wgpu::winit::Painter>>,
integration: Arc<RwLock<epi_integration::EpiIntegration>>,
app: Box<dyn epi::App>,
windows: Windows,
windows_id: HashMap<winit::window::WindowId, u64>, windows_id: HashMap<winit::window::WindowId, u64>,
} }
@@ -1725,7 +1758,7 @@ mod wgpu_integration {
/// Window surface state that's initialized when the app starts running via a Resumed event /// 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. /// and on Android will also be destroyed if the application is paused.
is_focused: Option<u64>, is_focused: Arc<RwLock<Option<u64>>>,
} }
impl WgpuWinitApp { impl WgpuWinitApp {
@@ -1747,7 +1780,7 @@ mod wgpu_integration {
native_options, native_options,
running: None, running: None,
app_creator: Some(app_creator), app_creator: Some(app_creator),
is_focused: Some(0), is_focused: Arc::new(RwLock::new(Some(0))),
} }
} }
@@ -1769,15 +1802,17 @@ mod wgpu_integration {
fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) { fn build_windows(&mut self, event_loop: &EventLoopWindowTarget<UserEvent>) {
let Some(running) = &mut self.running else {return}; let Some(running) = &mut self.running else {return};
for (id, (window, state, _, _, builder)) in running.windows.iter_mut() { for (id, (window, state, _, _, builder)) in running.windows.write().iter_mut() {
if window.is_none() { if window.is_none() {
if let Ok(new_window) = create_winit_window_builder(&builder).build(event_loop) if let Ok(new_window) = create_winit_window_builder(&builder).build(event_loop)
{ {
running.windows_id.insert(new_window.id(), *id); running.windows_id.insert(new_window.id(), *id);
pollster::block_on(running.painter.set_window(*id, Some(&new_window))); pollster::block_on(
running.painter.write().set_window(*id, Some(&new_window)),
);
*window = Some(Arc::new(RwLock::new(new_window))); *window = Some(Arc::new(RwLock::new(new_window)));
*state = Some(egui_winit::State::new(event_loop)); *state.write() = Some(egui_winit::State::new(event_loop));
} }
} }
} }
@@ -1785,9 +1820,9 @@ mod wgpu_integration {
fn set_window(&mut self, id: u64) -> std::result::Result<(), egui_wgpu::WgpuError> { fn set_window(&mut self, id: u64) -> std::result::Result<(), egui_wgpu::WgpuError> {
if let Some(running) = &mut self.running { if let Some(running) = &mut self.running {
if let Some((window, _, _, _, _)) = running.windows.get(&id) { if let Some((window, _, _, _, _)) = running.windows.read().get(&id) {
window.as_ref().map(|w| { window.as_ref().map(|w| {
pollster::block_on(running.painter.set_window(id, Some(&w.read()))) pollster::block_on(running.painter.write().set_window(id, Some(&w.read())))
}); });
} }
} }
@@ -1895,18 +1930,107 @@ mod wgpu_integration {
let mut windows_id = HashMap::default(); let mut windows_id = HashMap::default();
windows_id.insert(window.id(), 0); windows_id.insert(window.id(), 0);
let mut windows = HashMap::default(); let windows = Windows(Arc::new(RwLock::new(HashMap::default())));
windows.insert( windows.write().insert(
0, 0,
( (
Some(Arc::new(RwLock::new(window))), Some(Arc::new(RwLock::new(window))),
Some(state), Arc::new(RwLock::new(Some(state))),
None, None,
0, 0,
builder, builder,
), ),
); );
let _windows = windows.clone();
let egui_ctx = integration.egui_ctx.clone();
let focused = self.is_focused.clone();
let time = integration.beagining;
let painter = Arc::new(RwLock::new(painter));
let _painter = painter.clone();
integration.egui_ctx.set_render_sync_callback(
move |viewport_builder, viewport_id, parent_viewport_id, render| {
'try_render: {
let window = _windows.read().get(&viewport_id).cloned();
if let Some(window) = window {
let output;
{
if let Some(winit_state) = &mut *window.1.write() {
if let Some(win) = window.0.clone() {
let win = win.read();
let mut input = winit_state.take_egui_input(&win);
input.time = Some(time.elapsed().as_secs_f64());
output = egui_ctx.run(
input,
viewport_id,
parent_viewport_id,
|ctx| {
render(ctx, viewport_id, parent_viewport_id);
},
);
pollster::block_on(
_painter.write().set_window(viewport_id, Some(&win)),
);
let clipped_primitives = egui_ctx.tessellate(output.shapes);
_painter.write().paint_and_update_textures(
viewport_id,
egui_ctx.pixels_per_point(),
[0.0, 0.0, 0.0, 0.0],
&clipped_primitives,
&output.textures_delta,
false,
);
winit_state.handle_platform_output(
&win,
&egui_ctx,
output.platform_output,
);
} else {
break 'try_render;
}
} else {
break 'try_render;
}
}
let mut viewports = output.viewports;
let mut active_viewports_ids = vec![0];
viewports.retain_mut(|(id, parent, builder, render)| {
if let Some(w) = _windows.write().get_mut(id) {
w.2 = render.clone();
w.3 = *parent;
active_viewports_ids.push(*id);
return false;
} else {
true
}
});
for (id, parent, builder, render) in viewports {
_windows.write().insert(
id,
(None, Arc::new(RwLock::new(None)), render, parent, builder),
);
active_viewports_ids.push(id);
}
egui_winit::process_viewport_commands(
output.viewport_commands,
*focused.read(),
|id| _windows.read().get(&id).and_then(|w| w.0.clone()),
);
return;
}
}
render(&egui_ctx, 0, 0);
},
);
self.running = Some(WgpuWinitRunning { self.running = Some(WgpuWinitRunning {
painter, painter,
integration: Arc::new(RwLock::new(integration)), integration: Arc::new(RwLock::new(integration)),
@@ -1927,7 +2051,7 @@ mod wgpu_integration {
} }
fn is_focused(&self, window_id: winit::window::WindowId) -> bool { fn is_focused(&self, window_id: winit::window::WindowId) -> bool {
if let Some(focus) = self.is_focused { if let Some(focus) = self.is_focused.read().clone() {
self.get_window_id(&window_id) self.get_window_id(&window_id)
.map(|i| i == focus) .map(|i| i == focus)
.unwrap_or(false) .unwrap_or(false)
@@ -1946,21 +2070,26 @@ mod wgpu_integration {
) -> Option<Arc<RwLock<winit::window::Window>>> { ) -> Option<Arc<RwLock<winit::window::Window>>> {
self.running self.running
.as_ref() .as_ref()
.and_then(|r| r.windows_id.get(&window_id).map(|id| r.windows.get(id))) .and_then(|r| {
r.windows_id
.get(&window_id)
.and_then(|id| r.windows.read().get(id).map(|w| w.0.clone()))
})
.flatten() .flatten()
.and_then(|w| w.0.clone())
} }
fn get_window_winit_id(&self, id: u64) -> Option<winit::window::WindowId> { fn get_window_winit_id(&self, id: u64) -> Option<winit::window::WindowId> {
self.running self.running.as_ref().and_then(|r| {
.as_ref() r.windows
.and_then(|r| r.windows.get(&id)) .read()
.get(&id)
.and_then(|w| w.0.as_ref().map(|w| w.read().id())) .and_then(|w| w.0.as_ref().map(|w| w.read().id()))
})
} }
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() {
if let Some((Some(window), _, _, _, _)) = running.windows.get(&0) { if let Some((Some(window), _, _, _, _)) = running.windows.read().get(&0) {
running running
.integration .integration
.write() .write()
@@ -1973,7 +2102,7 @@ mod wgpu_integration {
#[cfg(not(feature = "glow"))] #[cfg(not(feature = "glow"))]
running.app.on_exit(); running.app.on_exit();
running.painter.destroy(); running.painter.write().destroy();
} }
} }
@@ -2000,10 +2129,19 @@ mod wgpu_integration {
viewport_commands, viewport_commands,
}; };
{ {
let Some((viewport_id, (Some(window), Some(state), render, parent_viewport_id, _))) = windows_id.get(&window_id).and_then(|id|(windows.get_mut(id).map(|w|(*id, w)))) else{return vec![]}; let Some((viewport_id, (Some(window), state, render, parent_viewport_id, _))) = windows_id.get(&window_id).and_then(|id|(windows.read().get(id).map(|w|(*id, w.clone())))) else{return vec![]};
if viewport_id != 0 && render.is_none() {
if let Some(window) = running.windows.read().get(&parent_viewport_id) {
window.0.as_ref().map(|w| w.read().request_redraw());
}
return vec![];
}
let _ = let _ = pollster::block_on(
pollster::block_on(painter.set_window(viewport_id, Some(&window.read()))); painter
.write()
.set_window(viewport_id, Some(&window.read())),
);
egui::FullOutput { egui::FullOutput {
platform_output, platform_output,
@@ -2015,16 +2153,16 @@ mod wgpu_integration {
} = integration.write().update( } = integration.write().update(
app.as_mut(), app.as_mut(),
&*window.read(), &*window.read(),
state, state.write().as_mut().unwrap(),
render.clone(), render.clone(),
viewport_id, viewport_id,
*parent_viewport_id, parent_viewport_id,
); );
integration.write().handle_platform_output( integration.write().handle_platform_output(
&window.read(), &window.read(),
platform_output, platform_output,
state, state.write().as_mut().unwrap(),
); );
let clipped_primitives = { let clipped_primitives = {
@@ -2035,7 +2173,7 @@ mod wgpu_integration {
let integration = &mut *integration.write(); let integration = &mut *integration.write();
let screenshot_requested = &mut integration.frame.output.screenshot_requested; let screenshot_requested = &mut integration.frame.output.screenshot_requested;
let screenshot = painter.paint_and_update_textures( let screenshot = painter.write().paint_and_update_textures(
viewport_id, viewport_id,
integration.egui_ctx.pixels_per_point(), integration.egui_ctx.pixels_per_point(),
app.clear_color(&integration.egui_ctx.style().visuals), app.clear_color(&integration.egui_ctx.style().visuals),
@@ -2053,7 +2191,7 @@ mod wgpu_integration {
let mut active_viewports_ids = vec![0]; let mut active_viewports_ids = vec![0];
viewports.retain_mut(|(id, parent, builder, render)| { viewports.retain_mut(|(id, parent, builder, render)| {
if let Some(w) = windows.get_mut(id) { if let Some(w) = windows.write().get_mut(id) {
w.2 = render.clone(); w.2 = render.clone();
w.3 = *parent; w.3 = *parent;
active_viewports_ids.push(*id); active_viewports_ids.push(*id);
@@ -2064,26 +2202,33 @@ mod wgpu_integration {
}); });
for (id, parent, builder, render) in viewports { for (id, parent, builder, render) in viewports {
windows.insert(id, (None, None, render, parent, builder)); windows.write().insert(
id,
(None, Arc::new(RwLock::new(None)), render, parent, builder),
);
active_viewports_ids.push(id); active_viewports_ids.push(id);
} }
egui_winit::process_viewport_commands( egui_winit::process_viewport_commands(
viewport_commands, viewport_commands,
self.is_focused, *self.is_focused.read(),
|viewport_id| windows.get(&viewport_id).and_then(|w| w.0.clone()), |viewport_id| windows.read().get(&viewport_id).and_then(|w| w.0.clone()),
); );
windows.retain(|id, _| active_viewports_ids.contains(id)); windows
.write()
.retain(|id, _| active_viewports_ids.contains(id));
windows_id.retain(|_, id| active_viewports_ids.contains(id)); windows_id.retain(|_, id| active_viewports_ids.contains(id));
painter.clean_surfaces(active_viewports_ids); painter.write().clean_surfaces(active_viewports_ids);
let mut control_flow = vec![EventResult::Wait]; let mut control_flow = vec![EventResult::Wait];
for repaint_after in repaint_after { for repaint_after in repaint_after {
control_flow.push(if integration.read().should_close() { control_flow.push(if integration.read().should_close() {
EventResult::Exit EventResult::Exit
} else if repaint_after.1.is_zero() { } else if repaint_after.1.is_zero() {
if let Some((Some(window), _, _, _, _)) = windows.get(&repaint_after.0) { if let Some((Some(window), _, _, _, _)) =
windows.read().get(&repaint_after.0)
{
EventResult::RepaintNext(window.read().id()) EventResult::RepaintNext(window.read().id())
} else { } else {
EventResult::Wait EventResult::Wait
@@ -2096,7 +2241,9 @@ mod wgpu_integration {
// technically, this might lead to some weird corner cases where the user *WANTS* // technically, this might lead to some weird corner cases where the user *WANTS*
// winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own // winit to use `WaitUntil(MAX_INSTANT)` explicitly. they can roll their own
// egui backend impl i guess. // egui backend impl i guess.
if let Some((Some(window), _, _, _, _)) = windows.get(&repaint_after.0) { if let Some((Some(window), _, _, _, _)) =
windows.read().get(&repaint_after.0)
{
EventResult::RepaintAt(window.read().id(), repaint_after_instant) EventResult::RepaintAt(window.read().id(), repaint_after_instant)
} else { } else {
EventResult::Wait EventResult::Wait
@@ -2106,7 +2253,7 @@ mod wgpu_integration {
}); });
} }
let Some((_, (Some(window), _, _, _, _))) = windows_id.get(&window_id).and_then(|id|(windows.get_mut(id).map(|w|(*id, w)))) else{return vec![]}; let Some((_, (Some(window), _, _, _, _))) = windows_id.get(&window_id).and_then(|id|(windows.read().get(id).map(|w|(*id, w.clone())))) else{return vec![]};
integration integration
.write() .write()
.maybe_autosave(app.as_mut(), &window.read()); .maybe_autosave(app.as_mut(), &window.read());
@@ -2133,7 +2280,7 @@ mod wgpu_integration {
Ok(match event { Ok(match event {
winit::event::Event::Resumed => { winit::event::Event::Resumed => {
if let Some(running) = &self.running { if let Some(running) = &self.running {
if running.windows.get(&0).is_none() { if running.windows.read().get(&0).is_none() {
let window = Self::create_window( let window = Self::create_window(
event_loop, event_loop,
running.integration.read().frame.storage(), running.integration.read().frame.storage(),
@@ -2162,6 +2309,7 @@ mod wgpu_integration {
.as_ref() .as_ref()
.unwrap() .unwrap()
.windows .windows
.read()
.get(&0) .get(&0)
.unwrap() .unwrap()
.0 .0
@@ -2197,7 +2345,8 @@ mod wgpu_integration {
match &event { match &event {
winit::event::WindowEvent::Focused(new_focused) => { winit::event::WindowEvent::Focused(new_focused) => {
self.is_focused = new_focused.then(|| viewport_id).flatten(); *self.is_focused.write() =
new_focused.then(|| viewport_id).flatten();
} }
winit::event::WindowEvent::Resized(physical_size) => { winit::event::WindowEvent::Resized(physical_size) => {
repaint_asap = true; repaint_asap = true;
@@ -2209,7 +2358,7 @@ mod wgpu_integration {
running.windows_id.get(window_id).cloned() running.windows_id.get(window_id).cloned()
{ {
if physical_size.width > 0 && physical_size.height > 0 { if physical_size.width > 0 && physical_size.height > 0 {
running.painter.on_window_resized( running.painter.write().on_window_resized(
viewport_id, viewport_id,
physical_size.width, physical_size.width,
physical_size.height, physical_size.height,
@@ -2225,7 +2374,7 @@ mod wgpu_integration {
running.windows_id.get(window_id).cloned() running.windows_id.get(window_id).cloned()
{ {
repaint_asap = true; repaint_asap = true;
running.painter.on_window_resized( running.painter.write().on_window_resized(
viewport_id, viewport_id,
new_inner_size.width, new_inner_size.width,
new_inner_size.height, new_inner_size.height,
@@ -2241,11 +2390,12 @@ mod wgpu_integration {
_ => {} _ => {}
}; };
let event_response = if let Some((id, (_, Some(state), _, _, _))) = running let event_response = if let Some((id, (_, state, _, _, _))) = running
.windows_id .windows_id
.get(window_id) .get(window_id)
.and_then(|id| running.windows.get_mut(id).map(|w| (*id, w))) .and_then(|id| running.windows.read().get(id).map(|w| (*id, w.clone())))
{ {
if let Some(state) = &mut *state.write() {
Some(running.integration.write().on_event( Some(running.integration.write().on_event(
running.app.as_mut(), running.app.as_mut(),
event, event,
@@ -2255,6 +2405,9 @@ mod wgpu_integration {
)) ))
} else { } else {
None None
}
} else {
None
}; };
if running.integration.read().should_close() { if running.integration.read().should_close() {
@@ -2281,17 +2434,19 @@ mod wgpu_integration {
accesskit_winit::ActionRequestEvent { request, window_id }, accesskit_winit::ActionRequestEvent { request, window_id },
)) => { )) => {
if let Some(running) = &mut self.running { if let Some(running) = &mut self.running {
if let Some((_, Some(state), _, _, _)) = running if let Some((_, state, _, _, _)) = running
.windows_id .windows_id
.get(window_id) .get(window_id)
.and_then(|id| running.windows.get_mut(id)) .and_then(|id| running.windows.read().get(id).cloned())
{ {
if let Some(state) = &mut *state.write() {
running.integration.write().on_accesskit_action_request( running.integration.write().on_accesskit_action_request(
request.clone(), request.clone(),
window_id, window_id,
state, state,
); );
} }
}
// As a form of user input, accessibility actions should // As a form of user input, accessibility actions should
// lead to a repaint. // lead to a repaint.
EventResult::RepaintNext(*window_id) EventResult::RepaintNext(*window_id)

View File

@@ -88,6 +88,9 @@ pub struct Painter {
surfaces: HashMap<u64, SurfaceState>, surfaces: HashMap<u64, SurfaceState>,
} }
unsafe impl Send for Painter {}
unsafe impl Sync for Painter {}
impl Painter { impl Painter {
/// Manages [`wgpu`] state, including surface state, required to render egui. /// Manages [`wgpu`] state, including surface state, required to render egui.
/// ///