From 0915fb298fe1d2dee9602cf0423fa46429639ea5 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 13 Nov 2023 20:40:35 +0100 Subject: [PATCH] Move stuff into `impl WgpuWinitRunning` --- crates/eframe/src/native/run.rs | 475 ++++++++++++++++---------------- 1 file changed, 241 insertions(+), 234 deletions(-) diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index c751a3ef1e4..503ca34d40f 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -1870,114 +1870,6 @@ mod wgpu_integration { /// Wrapped in an `Rc>` so it can be re-entrantly shared via a weak-pointer. shared: Rc>, } - - impl WgpuWinitRunning { - fn set_window(&self, id: ViewportId) -> Result<(), egui_wgpu::WgpuError> { - crate::profile_function!(); - let mut shared = self.shared.borrow_mut(); - let SharedState { - viewports, painter, .. - } = &mut *shared; - if let Some(Viewport { window, .. }) = viewports.get(&id) { - return pollster::block_on(painter.set_window(id, window.as_deref())); - } - Ok(()) - } - - fn on_window_event( - &mut self, - window_id: WindowId, - event: &winit::event::WindowEvent<'_>, - focused_viewport: &mut Option, - ) -> EventResult { - let Self { - integration, - app, - shared, - } = self; - let mut shared = shared.borrow_mut(); - - let viewport_id = shared.viewport_maps.get(&window_id).copied(); - - // On Windows, if a window is resized by the user, it should repaint synchronously, inside the - // event handler. - // - // If this is not done, the compositor will assume that the window does not want to redraw, - // and continue ahead. - // - // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver - // new frames to the compositor in time. - // - // The flickering is technically glutin or glow's fault, but we should be responding properly - // to resizes anyway, as doing so avoids dropping frames. - // - // See: https://github.com/emilk/egui/issues/903 - let mut repaint_asap = false; - - match &event { - winit::event::WindowEvent::Focused(new_focused) => { - *focused_viewport = new_focused.then(|| viewport_id).flatten(); - } - winit::event::WindowEvent::Resized(physical_size) => { - repaint_asap = true; - - // Resize with 0 width and height is used by winit to signal a minimize event on Windows. - // See: https://github.com/rust-windowing/winit/issues/208 - // This solves an issue where the app would panic when minimizing on Windows. - if let Some(viewport_id) = viewport_id { - use std::num::NonZeroU32; - if let (Some(width), Some(height)) = ( - NonZeroU32::new(physical_size.width), - NonZeroU32::new(physical_size.height), - ) { - shared.painter.on_window_resized(viewport_id, width, height); - } - } - } - winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - use std::num::NonZeroU32; - if let (Some(width), Some(height), Some(viewport_id)) = ( - NonZeroU32::new(new_inner_size.width), - NonZeroU32::new(new_inner_size.height), - shared.viewport_maps.get(&window_id).copied(), - ) { - repaint_asap = true; - shared.painter.on_window_resized(viewport_id, width, height); - } - } - winit::event::WindowEvent::CloseRequested if integration.should_close() => { - log::debug!("Received WindowEvent::CloseRequested"); - return EventResult::Exit; - } - _ => {} - }; - - 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) - }) - }) - }); - - if integration.should_close() { - EventResult::Exit - } else if let Some(event_response) = event_response { - if event_response.repaint { - if repaint_asap { - EventResult::RepaintNow(window_id) - } else { - EventResult::RepaintNext(window_id) - } - } else { - EventResult::Wait - } - } else { - EventResult::Wait - } - } - } - struct WgpuWinitApp { repaint_proxy: Arc>>, app_name: String, @@ -2416,30 +2308,154 @@ mod wgpu_integration { fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { - crate::profile_function!(); + running.save_and_destroy(); + } + } - let mut shared = running.shared.borrow_mut(); - if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { - running - .integration - .save(running.app.as_mut(), window.as_deref()); + fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { + if let Some(running) = &mut self.running { + running.run_ui_and_paint(window_id, self.focused_viewport) + } else { + EventResult::Wait + } + } + + fn on_event( + &mut self, + event_loop: &EventLoopWindowTarget, + event: &winit::event::Event<'_, UserEvent>, + ) -> Result { + crate::profile_function!(); + self.build_windows(event_loop); + + Ok(match event { + winit::event::Event::Resumed => { + let running = if let Some(running) = &self.running { + if !running + .shared + .borrow() + .viewports + .contains_key(&ViewportId::ROOT) + { + create_window( + event_loop, + running.integration.frame.storage(), + &self.app_name, + &mut self.native_options, + )?; + running.set_window(ViewportId::ROOT)?; + } + running + } else { + let storage = epi_integration::create_storage( + self.native_options + .app_id + .as_ref() + .unwrap_or(&self.app_name), + ); + let (window, builder) = create_window( + event_loop, + storage.as_deref(), + &self.app_name, + &mut self.native_options, + )?; + self.init_run_state(event_loop, storage, window, builder)? + }; + EventResult::RepaintNow( + running.shared.borrow().viewports[&ViewportId::ROOT] + .window + .as_ref() + .unwrap() + .id(), + ) } - #[cfg(feature = "glow")] - running.app.on_exit(None); + winit::event::Event::Suspended => { + #[cfg(target_os = "android")] + self.drop_window()?; + EventResult::Wait + } - #[cfg(not(feature = "glow"))] - running.app.on_exit(); + winit::event::Event::WindowEvent { event, window_id } => { + if let Some(running) = &mut self.running { + running.on_window_event(*window_id, event, &mut self.focused_viewport) + } else { + EventResult::Wait + } + } - shared.painter.destroy(); + #[cfg(feature = "accesskit")] + winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( + accesskit_winit::ActionRequestEvent { request, window_id }, + )) => { + if let Some(running) = &mut self.running { + let mut shared_lock = running.shared.borrow_mut(); + let SharedState { + viewport_maps, + viewports, + .. + } = &mut *shared_lock; + if let Some(viewport) = viewport_maps + .get(window_id) + .and_then(|id| viewports.get_mut(id)) + { + if let Some(egui_winit) = &mut viewport.egui_winit { + egui_winit.on_accesskit_action_request(request.clone()); + } + } + // As a form of user input, accessibility actions should + // lead to a repaint. + EventResult::RepaintNext(*window_id) + } else { + EventResult::Wait + } + } + _ => EventResult::Wait, + }) + } + + fn viewport_id_from_window_id(&self, id: &WindowId) -> Option { + self.running + .as_ref() + .and_then(|r| r.shared.borrow().viewport_maps.get(id).copied()) + } + } + + impl WgpuWinitRunning { + fn set_window(&self, id: ViewportId) -> Result<(), egui_wgpu::WgpuError> { + crate::profile_function!(); + let mut shared = self.shared.borrow_mut(); + let SharedState { + viewports, painter, .. + } = &mut *shared; + if let Some(Viewport { window, .. }) = viewports.get(&id) { + return pollster::block_on(painter.set_window(id, window.as_deref())); } + Ok(()) } - fn run_ui_and_paint(&mut self, window_id: WindowId) -> EventResult { - let Some(running) = &mut self.running else { - return EventResult::Wait; - }; + fn save_and_destroy(&mut self) { + crate::profile_function!(); + let mut shared = self.shared.borrow_mut(); + if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { + self.integration.save(self.app.as_mut(), window.as_deref()); + } + + #[cfg(feature = "glow")] + self.app.on_exit(None); + + #[cfg(not(feature = "glow"))] + self.app.on_exit(); + + shared.painter.destroy(); + } + + fn run_ui_and_paint( + &mut self, + window_id: WindowId, + focused_viewport: Option, + ) -> EventResult { #[cfg(feature = "puffin")] puffin::GlobalProfiler::lock().new_frame(); crate::profile_scope!("frame"); @@ -2448,7 +2464,7 @@ mod wgpu_integration { app, integration, shared, - } = running; + } = self; let (viewport_ui_cb, raw_input) = { let mut shared_lock = shared.borrow_mut(); @@ -2624,13 +2640,11 @@ mod wgpu_integration { viewport.window = None; viewport.egui_winit = None; } - } else if let Some(Viewport { - window: Some(window), - .. - }) = viewports.get(&id_pair.this) - { - let is_viewport_focused = false; // TODO - process_viewport_commands(commands, window, is_viewport_focused); + } else if let Some(viewport) = viewports.get(&id_pair.this) { + if let Some(window) = &viewport.window { + let is_viewport_focused = focused_viewport == Some(viewport_id); + process_viewport_commands(commands, window, is_viewport_focused); + } } } else { viewports.insert( @@ -2649,8 +2663,11 @@ mod wgpu_integration { } for (viewport_id, command) in viewport_commands { - if let Some(window) = viewports.get(&viewport_id).and_then(|v| v.window.as_ref()) { - let is_viewport_focused = self.focused_viewport == Some(viewport_id); + 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( std::iter::once(command), window, @@ -2664,22 +2681,19 @@ mod wgpu_integration { viewport_maps.retain(|_, id| active_viewports_ids.contains(id)); painter.gc_viewports(&active_viewports_ids); - let Some(Viewport { - window: Some(window), - .. - }) = viewport_maps + let window = viewport_maps .get(&window_id) .and_then(|id| viewports.get(id)) - else { - return EventResult::Wait; - }; - integration.maybe_autosave(app.as_mut(), Some(window)); - - if window.is_minimized() == Some(true) { - // On Mac, a minimized Window uses up all CPU: - // https://github.com/emilk/egui/issues/325 - crate::profile_scope!("minimized_sleep"); - std::thread::sleep(std::time::Duration::from_millis(10)); + .and_then(|vp| vp.window.as_ref()); + integration.maybe_autosave(app.as_mut(), window.map(|w| w.as_ref())); + + if let Some(window) = window { + if window.is_minimized() == Some(true) { + // On Mac, a minimized Window uses up all CPU: + // https://github.com/emilk/egui/issues/325 + crate::profile_scope!("minimized_sleep"); + std::thread::sleep(std::time::Duration::from_millis(10)); + } } if integration.should_close() { @@ -2689,104 +2703,97 @@ mod wgpu_integration { } } - fn on_event( + fn on_window_event( &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, UserEvent>, - ) -> Result { - crate::profile_function!(); - self.build_windows(event_loop); + window_id: WindowId, + event: &winit::event::WindowEvent<'_>, + focused_viewport: &mut Option, + ) -> EventResult { + let Self { + integration, + app, + shared, + } = self; + let mut shared = shared.borrow_mut(); - Ok(match event { - winit::event::Event::Resumed => { - let running = if let Some(running) = &self.running { - if !running - .shared - .borrow() - .viewports - .contains_key(&ViewportId::ROOT) - { - create_window( - event_loop, - running.integration.frame.storage(), - &self.app_name, - &mut self.native_options, - )?; - running.set_window(ViewportId::ROOT)?; - } - running - } else { - let storage = epi_integration::create_storage( - self.native_options - .app_id - .as_ref() - .unwrap_or(&self.app_name), - ); - let (window, builder) = create_window( - event_loop, - storage.as_deref(), - &self.app_name, - &mut self.native_options, - )?; - self.init_run_state(event_loop, storage, window, builder)? - }; - EventResult::RepaintNow( - running.shared.borrow().viewports[&ViewportId::ROOT] - .window - .as_ref() - .unwrap() - .id(), - ) - } + let viewport_id = shared.viewport_maps.get(&window_id).copied(); - winit::event::Event::Suspended => { - #[cfg(target_os = "android")] - self.drop_window()?; - EventResult::Wait + // On Windows, if a window is resized by the user, it should repaint synchronously, inside the + // event handler. + // + // If this is not done, the compositor will assume that the window does not want to redraw, + // and continue ahead. + // + // In eframe's case, that causes the window to rapidly flicker, as it struggles to deliver + // new frames to the compositor in time. + // + // The flickering is technically glutin or glow's fault, but we should be responding properly + // to resizes anyway, as doing so avoids dropping frames. + // + // See: https://github.com/emilk/egui/issues/903 + let mut repaint_asap = false; + + match &event { + winit::event::WindowEvent::Focused(new_focused) => { + *focused_viewport = new_focused.then(|| viewport_id).flatten(); } + winit::event::WindowEvent::Resized(physical_size) => { + repaint_asap = true; - winit::event::Event::WindowEvent { event, window_id } => { - if let Some(running) = &mut self.running { - running.on_window_event(*window_id, event, &mut self.focused_viewport) - } else { - EventResult::Wait + // Resize with 0 width and height is used by winit to signal a minimize event on Windows. + // See: https://github.com/rust-windowing/winit/issues/208 + // This solves an issue where the app would panic when minimizing on Windows. + if let Some(viewport_id) = viewport_id { + use std::num::NonZeroU32; + if let (Some(width), Some(height)) = ( + NonZeroU32::new(physical_size.width), + NonZeroU32::new(physical_size.height), + ) { + shared.painter.on_window_resized(viewport_id, width, height); + } } } + winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + use std::num::NonZeroU32; + if let (Some(width), Some(height), Some(viewport_id)) = ( + NonZeroU32::new(new_inner_size.width), + NonZeroU32::new(new_inner_size.height), + shared.viewport_maps.get(&window_id).copied(), + ) { + repaint_asap = true; + shared.painter.on_window_resized(viewport_id, width, height); + } + } + winit::event::WindowEvent::CloseRequested if integration.should_close() => { + log::debug!("Received WindowEvent::CloseRequested"); + return EventResult::Exit; + } + _ => {} + }; - #[cfg(feature = "accesskit")] - winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( - accesskit_winit::ActionRequestEvent { request, window_id }, - )) => { - if let Some(running) = &mut self.running { - let mut shared_lock = running.shared.borrow_mut(); - let SharedState { - viewport_maps, - viewports, - .. - } = &mut *shared_lock; - if let Some(viewport) = viewport_maps - .get(window_id) - .and_then(|id| viewports.get_mut(id)) - { - if let Some(egui_winit) = &mut viewport.egui_winit { - egui_winit.on_accesskit_action_request(request.clone()); - } - } - // As a form of user input, accessibility actions should - // lead to a repaint. - EventResult::RepaintNext(*window_id) + 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) + }) + }) + }); + + if integration.should_close() { + EventResult::Exit + } else if let Some(event_response) = event_response { + if event_response.repaint { + if repaint_asap { + EventResult::RepaintNow(window_id) } else { - EventResult::Wait + EventResult::RepaintNext(window_id) } + } else { + EventResult::Wait } - _ => EventResult::Wait, - }) - } - - fn viewport_id_from_window_id(&self, id: &WindowId) -> Option { - self.running - .as_ref() - .and_then(|r| r.shared.borrow().viewport_maps.get(id).copied()) + } else { + EventResult::Wait + } } }