From 6daba880294e0c3e2b78e10f0bf463dcae6ebec9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2025 22:37:42 +0100 Subject: Initialize `Compositor` lazily in `winit` shell ... and get rid of the ghost boot window! --- winit/src/program.rs | 287 ++++++++++++++++++++++++++------------------------- 1 file changed, 144 insertions(+), 143 deletions(-) (limited to 'winit') diff --git a/winit/src/program.rs b/winit/src/program.rs index cc19a4e0..a236bb95 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -192,7 +192,6 @@ where runtime.enter(|| program.subscription().map(Action::Output)), )); - let (boot_sender, boot_receiver) = oneshot::channel(); let (event_sender, event_receiver) = mpsc::unbounded(); let (control_sender, control_receiver) = mpsc::unbounded(); @@ -201,133 +200,49 @@ where runtime, proxy.clone(), debug, - boot_receiver, event_receiver, control_sender, is_daemon, + graphics_settings, + settings.fonts, )); let context = task::Context::from_waker(task::noop_waker_ref()); - struct Runner { + struct Runner { instance: std::pin::Pin>, context: task::Context<'static>, id: Option, - boot: Option>, sender: mpsc::UnboundedSender>>, receiver: mpsc::UnboundedReceiver, error: Option, - #[cfg(target_arch = "wasm32")] - is_booted: std::rc::Rc>, #[cfg(target_arch = "wasm32")] canvas: Option, } - struct BootConfig { - sender: oneshot::Sender>, - fonts: Vec>, - graphics_settings: graphics::Settings, - } - let runner = Runner { instance, context, id: settings.id, - boot: Some(BootConfig { - sender: boot_sender, - fonts: settings.fonts, - graphics_settings, - }), sender: event_sender, receiver: control_receiver, error: None, - #[cfg(target_arch = "wasm32")] - is_booted: std::rc::Rc::new(std::cell::RefCell::new(false)), #[cfg(target_arch = "wasm32")] canvas: None, }; - impl winit::application::ApplicationHandler> - for Runner + impl winit::application::ApplicationHandler> + for Runner where Message: std::fmt::Debug, F: Future, - C: Compositor + 'static, { - fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { - let Some(BootConfig { - sender, - fonts, - graphics_settings, - }) = self.boot.take() - else { - return; - }; - - let window = { - let attributes = winit::window::WindowAttributes::default(); - - #[cfg(target_os = "windows")] - let attributes = { - use winit::platform::windows::WindowAttributesExtWindows; - attributes.with_drag_and_drop(false) - }; - - match event_loop.create_window(attributes.with_visible(false)) { - Ok(window) => Arc::new(window), - Err(error) => { - self.error = Some(Error::WindowCreationFailed(error)); - event_loop.exit(); - return; - } - } - }; - - #[cfg(target_arch = "wasm32")] - { - use winit::platform::web::WindowExtWebSys; - self.canvas = window.canvas(); - } - - let finish_boot = async move { - let mut compositor = - C::new(graphics_settings, window.clone()).await?; - - for font in fonts { - compositor.load_font(font); - } - - sender - .send(Boot { compositor }) - .ok() - .expect("Send boot event"); - - Ok::<_, graphics::Error>(()) - }; - - #[cfg(not(target_arch = "wasm32"))] - if let Err(error) = - crate::futures::futures::executor::block_on(finish_boot) - { - self.error = Some(Error::GraphicsCreationFailed(error)); - event_loop.exit(); - } - - #[cfg(target_arch = "wasm32")] - { - let is_booted = self.is_booted.clone(); - - wasm_bindgen_futures::spawn_local(async move { - finish_boot.await.expect("Finish boot!"); - - *is_booted.borrow_mut() = true; - }); - - event_loop - .set_control_flow(winit::event_loop::ControlFlow::Poll); - } + fn resumed( + &mut self, + _event_loop: &winit::event_loop::ActiveEventLoop, + ) { } fn new_events( @@ -335,15 +250,6 @@ where event_loop: &winit::event_loop::ActiveEventLoop, cause: winit::event::StartCause, ) { - if self.boot.is_some() { - return; - } - - #[cfg(target_arch = "wasm32")] - if !*self.is_booted.borrow() { - return; - } - self.process_event( event_loop, Event::EventLoopAwakened(winit::event::Event::NewEvents(cause)), @@ -422,11 +328,6 @@ where &mut self, event_loop: &winit::event_loop::ActiveEventLoop, ) { - #[cfg(target_arch = "wasm32")] - if !*self.is_booted.borrow() { - return; - } - self.process_event( event_loop, Event::EventLoopAwakened(winit::event::Event::AboutToWait), @@ -434,10 +335,9 @@ where } } - impl Runner + impl Runner where F: Future, - C: Compositor, { fn process_event( &mut self, @@ -577,7 +477,7 @@ where event_loop, Event::WindowCreated { id, - window, + window: Arc::new(window), exit_on_close_request, make_visible: visible, on_open, @@ -587,6 +487,10 @@ where Control::Exit => { event_loop.exit(); } + Control::Crash(error) => { + self.error = Some(error); + event_loop.exit(); + } }, _ => { break; @@ -618,15 +522,11 @@ where } } -struct Boot { - compositor: C, -} - #[derive(Debug)] enum Event { WindowCreated { id: window::Id, - window: winit::window::Window, + window: Arc, exit_on_close_request: bool, make_visible: bool, on_open: oneshot::Sender, @@ -638,6 +538,7 @@ enum Event { enum Control { ChangeFlow(winit::event_loop::ControlFlow), Exit, + Crash(Error), CreateWindow { id: window::Id, settings: window::Settings, @@ -652,10 +553,11 @@ async fn run_instance( mut runtime: Runtime, Action>, mut proxy: Proxy, mut debug: Debug, - boot: oneshot::Receiver>, mut event_receiver: mpsc::UnboundedReceiver>>, mut control_sender: mpsc::UnboundedSender, is_daemon: bool, + graphics_settings: graphics::Settings, + default_fonts: Vec>, ) where P: Program + 'static, C: Compositor + 'static, @@ -664,11 +566,10 @@ async fn run_instance( use winit::event; use winit::event_loop::ControlFlow; - let Boot { mut compositor } = boot.await.expect("Receive boot"); - let mut window_manager = WindowManager::new(); let mut is_window_opening = !is_daemon; + let mut compositor: Option = None; let mut events = Vec::new(); let mut messages = Vec::new(); let mut actions = 0; @@ -676,12 +577,46 @@ async fn run_instance( let mut ui_caches = FxHashMap::default(); let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); + let mut compositor_receiver: Option< + oneshot::Receiver< + Result<(C, Event>), graphics::Error>, + >, + > = None; debug.startup_finished(); loop { + let event = if compositor_receiver.is_some() { + let compositor_receiver = + compositor_receiver.take().expect("Waiting for compositor"); + + match compositor_receiver.await.ok() { + Some(Ok((new_compositor, event))) => { + compositor = Some(new_compositor); + + Some(event) + } + Some(Err(error)) => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed(error), + )) + .expect("Send control action"); + break; + } + None => { + control_sender + .start_send(Control::Crash( + Error::GraphicsCreationFailed( + graphics::Error::NoAvailablePixelFormat, + ), + )) + .expect("Send control action"); + break; + } + } // Empty the queue if possible - let event = if let Ok(event) = event_receiver.try_next() { + } else if let Ok(event) = event_receiver.try_next() { event } else { event_receiver.next().await @@ -699,11 +634,63 @@ async fn run_instance( make_visible, on_open, } => { + if compositor.is_none() { + let (compositor_sender, new_compositor_receiver) = + oneshot::channel(); + + compositor_receiver = Some(new_compositor_receiver); + + let create_compositor = { + let default_fonts = default_fonts.clone(); + + async move { + let mut compositor = + C::new(graphics_settings, window.clone()).await; + + if let Ok(compositor) = &mut compositor { + for font in default_fonts { + compositor.load_font(font.clone()); + } + } + + compositor_sender + .send(compositor.map(|compositor| { + ( + compositor, + Event::WindowCreated { + id, + window, + exit_on_close_request, + make_visible, + on_open, + }, + ) + })) + .ok() + .expect("Send compositor"); + } + }; + + #[cfg(not(target_arch = "wasm32"))] + crate::futures::futures::executor::block_on( + create_compositor, + ); + + #[cfg(target_arch = "wasm32")] + { + wasm_bindgen_futures::spawn_local(create_compositor); + } + + continue; + } + let window = window_manager.insert( id, - Arc::new(window), + window, &program, - &mut compositor, + compositor + .as_mut() + .expect("Compositor must be initialized"), exit_on_close_request, ); @@ -797,6 +784,10 @@ async fn run_instance( event: event::WindowEvent::RedrawRequested, .. } => { + let Some(compositor) = &mut compositor else { + continue; + }; + let Some((id, window)) = window_manager.get_mut_alias(id) else { @@ -1195,7 +1186,7 @@ fn update( fn run_action( action: Action, program: &P, - compositor: &mut C, + compositor: &mut Option, events: &mut Vec<(window::Id, core::Event)>, messages: &mut Vec, clipboard: &mut Clipboard, @@ -1263,6 +1254,10 @@ fn run_action( core::Event::Window(core::window::Event::Closed), )); } + + if window_manager.is_empty() { + *compositor = None; + } } window::Action::GetOldest(channel) => { let id = @@ -1439,18 +1434,20 @@ fn run_action( } window::Action::Screenshot(id, channel) => { if let Some(window) = window_manager.get_mut(id) { - let bytes = compositor.screenshot( - &mut window.renderer, - window.state.viewport(), - window.state.background_color(), - &debug.overlay(), - ); + if let Some(compositor) = compositor { + let bytes = compositor.screenshot( + &mut window.renderer, + window.state.viewport(), + window.state.background_color(), + &debug.overlay(), + ); - let _ = channel.send(core::window::Screenshot::new( - bytes, - window.state.physical_size(), - window.state.viewport().scale_factor(), - )); + let _ = channel.send(core::window::Screenshot::new( + bytes, + window.state.physical_size(), + window.state.viewport().scale_factor(), + )); + } } } window::Action::EnableMousePassthrough(id) => { @@ -1468,14 +1465,16 @@ fn run_action( system::Action::QueryInformation(_channel) => { #[cfg(feature = "system")] { - let graphics_info = compositor.fetch_information(); + if let Some(compositor) = compositor { + let graphics_info = compositor.fetch_information(); - let _ = std::thread::spawn(move || { - let information = - crate::system::information(graphics_info); + let _ = std::thread::spawn(move || { + let information = + crate::system::information(graphics_info); - let _ = _channel.send(information); - }); + let _ = _channel.send(information); + }); + } } } }, @@ -1499,10 +1498,12 @@ fn run_action( } } Action::LoadFont { bytes, channel } => { - // TODO: Error handling (?) - compositor.load_font(bytes.clone()); + if let Some(compositor) = compositor { + // TODO: Error handling (?) + compositor.load_font(bytes.clone()); - let _ = channel.send(Ok(())); + let _ = channel.send(Ok(())); + } } Action::Exit => { control_sender -- cgit From 2086fc0d6b48bc3c8237d0bbdeac6d2d010ca7ae Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 6 Jan 2025 22:48:09 +0100 Subject: Simplify type annotations in `winit::program` --- winit/src/program.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'winit') diff --git a/winit/src/program.rs b/winit/src/program.rs index a236bb95..653d10bb 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -569,7 +569,7 @@ async fn run_instance( let mut window_manager = WindowManager::new(); let mut is_window_opening = !is_daemon; - let mut compositor: Option = None; + let mut compositor = None; let mut events = Vec::new(); let mut messages = Vec::new(); let mut actions = 0; @@ -577,11 +577,7 @@ async fn run_instance( let mut ui_caches = FxHashMap::default(); let mut user_interfaces = ManuallyDrop::new(FxHashMap::default()); let mut clipboard = Clipboard::unconnected(); - let mut compositor_receiver: Option< - oneshot::Receiver< - Result<(C, Event>), graphics::Error>, - >, - > = None; + let mut compositor_receiver: Option> = None; debug.startup_finished(); -- cgit From 8b3b554de2b9853a922b285082a0d1ca6f864ddb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Jan 2025 11:16:03 +0000 Subject: Panic instead of erroring when compositor channel unexpectedly closes --- winit/src/program.rs | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) (limited to 'winit') diff --git a/winit/src/program.rs b/winit/src/program.rs index 653d10bb..0f1ea042 100644 --- a/winit/src/program.rs +++ b/winit/src/program.rs @@ -586,13 +586,13 @@ async fn run_instance( let compositor_receiver = compositor_receiver.take().expect("Waiting for compositor"); - match compositor_receiver.await.ok() { - Some(Ok((new_compositor, event))) => { + match compositor_receiver.await { + Ok(Ok((new_compositor, event))) => { compositor = Some(new_compositor); Some(event) } - Some(Err(error)) => { + Ok(Err(error)) => { control_sender .start_send(Control::Crash( Error::GraphicsCreationFailed(error), @@ -600,15 +600,8 @@ async fn run_instance( .expect("Send control action"); break; } - None => { - control_sender - .start_send(Control::Crash( - Error::GraphicsCreationFailed( - graphics::Error::NoAvailablePixelFormat, - ), - )) - .expect("Send control action"); - break; + Err(error) => { + panic!("Compositor initialization failed: {error}") } } // Empty the queue if possible -- cgit