diff --git a/crates/bevy_window/src/raw_handle.rs b/crates/bevy_window/src/raw_handle.rs index eb9382590c..0b799a1142 100644 --- a/crates/bevy_window/src/raw_handle.rs +++ b/crates/bevy_window/src/raw_handle.rs @@ -5,6 +5,38 @@ use raw_window_handle::{ DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle, RawWindowHandle, WindowHandle, }; +use std::{any::Any, marker::PhantomData, ops::Deref, sync::Arc}; + +/// A wrapper over a window. +/// +/// This allows us to extend the lifetime of the window, so it doesn't get eagerly dropped while a +/// pipelined renderer still has frames in flight that need to draw to it. +/// +/// This is achieved by storing a shared reference to the window in the [`RawHandleWrapper`], +/// which gets picked up by the renderer during extraction. +#[derive(Debug)] +pub struct WindowWrapper { + reference: Arc, + ty: PhantomData, +} + +impl WindowWrapper { + /// Creates a `WindowWrapper` from a window. + pub fn new(window: W) -> WindowWrapper { + WindowWrapper { + reference: Arc::new(window), + ty: PhantomData, + } + } +} + +impl Deref for WindowWrapper { + type Target = W; + + fn deref(&self) -> &Self::Target { + self.reference.downcast_ref::().unwrap() + } +} /// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads. /// @@ -13,6 +45,7 @@ use raw_window_handle::{ /// thread-safe. #[derive(Debug, Clone, Component)] pub struct RawHandleWrapper { + _window: Arc, /// Raw handle to a window. pub window_handle: RawWindowHandle, /// Raw handle to the display server. @@ -20,6 +53,17 @@ pub struct RawHandleWrapper { } impl RawHandleWrapper { + /// Creates a `RawHandleWrapper` from a `WindowWrapper`. + pub fn new( + window: &WindowWrapper, + ) -> Result { + Ok(RawHandleWrapper { + _window: window.reference.clone(), + window_handle: window.window_handle()?.as_raw(), + display_handle: window.display_handle()?.as_raw(), + }) + } + /// Returns a [`HasWindowHandle`] + [`HasDisplayHandle`] impl, which exposes [`WindowHandle`] and [`DisplayHandle`]. /// /// # Safety diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 9507eabaeb..cd13584c85 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -700,7 +700,6 @@ fn handle_winit_event( .world_mut() .query_filtered::<(Entity, &Window), (With, Without)>(); if let Ok((entity, window)) = query.get_single(app.world()) { - use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; let window = window.clone(); let ( @@ -720,10 +719,7 @@ fn handle_winit_event( &accessibility_requested, ); - let wrapper = RawHandleWrapper { - window_handle: winit_window.window_handle().unwrap().as_raw(), - display_handle: winit_window.display_handle().unwrap().as_raw(), - }; + let wrapper = RawHandleWrapper::new(winit_window).unwrap(); app.world_mut().entity_mut(entity).insert(wrapper); } diff --git a/crates/bevy_winit/src/system.rs b/crates/bevy_winit/src/system.rs index e195bf9534..87f52066b9 100644 --- a/crates/bevy_winit/src/system.rs +++ b/crates/bevy_winit/src/system.rs @@ -11,7 +11,6 @@ use bevy_window::{ RawHandleWrapper, Window, WindowClosed, WindowCreated, WindowMode, WindowResized, }; -use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use winit::{ dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize}, event_loop::EventLoopWindowTarget, @@ -75,10 +74,7 @@ pub fn create_windows( .set_scale_factor(winit_window.scale_factor() as f32); commands .entity(entity) - .insert(RawHandleWrapper { - window_handle: winit_window.window_handle().unwrap().as_raw(), - display_handle: winit_window.display_handle().unwrap().as_raw(), - }) + .insert(RawHandleWrapper::new(winit_window).unwrap()) .insert(CachedWindow { window: window.clone(), }); diff --git a/crates/bevy_winit/src/winit_windows.rs b/crates/bevy_winit/src/winit_windows.rs index e34f5b0fa5..ecea140f73 100644 --- a/crates/bevy_winit/src/winit_windows.rs +++ b/crates/bevy_winit/src/winit_windows.rs @@ -3,7 +3,9 @@ use bevy_ecs::entity::Entity; use bevy_ecs::entity::EntityHashMap; use bevy_utils::{tracing::warn, HashMap}; -use bevy_window::{CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution}; +use bevy_window::{ + CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution, WindowWrapper, +}; use winit::{ dpi::{LogicalSize, PhysicalPosition}, @@ -20,7 +22,7 @@ use crate::{ #[derive(Debug, Default)] pub struct WinitWindows { /// Stores [`winit`] windows by window identifier. - pub windows: HashMap, + pub windows: HashMap>, /// Maps entities to `winit` window identifiers. pub entity_to_winit: EntityHashMap, /// Maps `winit` window identifiers to entities. @@ -41,7 +43,7 @@ impl WinitWindows { adapters: &mut AccessKitAdapters, handlers: &mut WinitActionHandlers, accessibility_requested: &AccessibilityRequested, - ) -> &winit::window::Window { + ) -> &WindowWrapper { let mut winit_window_builder = winit::window::WindowBuilder::new(); // Due to a UIA limitation, winit windows need to be invisible for the @@ -240,12 +242,12 @@ impl WinitWindows { self.windows .entry(winit_window.id()) - .insert(winit_window) + .insert(WindowWrapper::new(winit_window)) .into_mut() } /// Get the winit window that is associated with our entity. - pub fn get_window(&self, entity: Entity) -> Option<&winit::window::Window> { + pub fn get_window(&self, entity: Entity) -> Option<&WindowWrapper> { self.entity_to_winit .get(&entity) .and_then(|winit_id| self.windows.get(winit_id)) @@ -261,7 +263,10 @@ impl WinitWindows { /// Remove a window from winit. /// /// This should mostly just be called when the window is closing. - pub fn remove_window(&mut self, entity: Entity) -> Option { + pub fn remove_window( + &mut self, + entity: Entity, + ) -> Option> { let winit_id = self.entity_to_winit.remove(&entity)?; self.winit_to_entity.remove(&winit_id); self.windows.remove(&winit_id)