mirror of
https://github.com/bevyengine/bevy
synced 2024-11-21 20:23:28 +00:00
Introduce a WindowWrapper
to extend the lifetime of the window when using pipelined rendering (#12978)
# Objective A `RawWindowHandle` is only valid as long as the window it was retrieved from is alive. Extend the lifetime of the window, so that the `RawWindowHandle` doesn't outlive it, and bevy doesn't crash when closing a window a pipelined renderer is drawing to. - Fix #11236 - Fix #11150 - Fix #11734 - Alternative to / Closes #12524 ## Solution Introduce a `WindowWrapper` that takes ownership of the window. Require it to be used when constructing a `RawHandleWrapper`. This forces windowing backends to store their window in this wrapper. The `WindowWrapper` is implemented by storing the window in an `Arc<dyn Any + Send + Sync>`. We use dynamic dispatch here because we later want the `RawHandleWrapper` to be able dynamically hold a reference to any windowing backend's window. But alas, the `WindowWrapper` itself is still practically invisible to windowing backends, because it implements `Deref` to the underlying window, by storing its type in a `PhantomData`. --- ## Changelog ### Added - Added `WindowWrapper`, which windowing backends are now required to use to store their underlying window. ### Fixed - Fixed a safety problem which caused crashes when closing bevy windows when using pipelined rendering. ## Migration Guide - Windowing backends now need to store their window in the new `WindowWrapper`.
This commit is contained in:
parent
f1db525f14
commit
9973f0c8a3
4 changed files with 57 additions and 16 deletions
|
@ -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<W> {
|
||||
reference: Arc<dyn Any + Send + Sync>,
|
||||
ty: PhantomData<W>,
|
||||
}
|
||||
|
||||
impl<W: Send + Sync + 'static> WindowWrapper<W> {
|
||||
/// Creates a `WindowWrapper` from a window.
|
||||
pub fn new(window: W) -> WindowWrapper<W> {
|
||||
WindowWrapper {
|
||||
reference: Arc::new(window),
|
||||
ty: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: 'static> Deref for WindowWrapper<W> {
|
||||
type Target = W;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.reference.downcast_ref::<W>().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<dyn Any + Send + Sync>,
|
||||
/// 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<W: HasWindowHandle + HasDisplayHandle + 'static>(
|
||||
window: &WindowWrapper<W>,
|
||||
) -> Result<RawHandleWrapper, HandleError> {
|
||||
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
|
||||
|
|
|
@ -700,7 +700,6 @@ fn handle_winit_event(
|
|||
.world_mut()
|
||||
.query_filtered::<(Entity, &Window), (With<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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<F: QueryFilter + 'static>(
|
|||
.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(),
|
||||
});
|
||||
|
|
|
@ -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<winit::window::WindowId, winit::window::Window>,
|
||||
pub windows: HashMap<winit::window::WindowId, WindowWrapper<winit::window::Window>>,
|
||||
/// Maps entities to `winit` window identifiers.
|
||||
pub entity_to_winit: EntityHashMap<winit::window::WindowId>,
|
||||
/// 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<winit::window::Window> {
|
||||
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<winit::window::Window>> {
|
||||
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<winit::window::Window> {
|
||||
pub fn remove_window(
|
||||
&mut self,
|
||||
entity: Entity,
|
||||
) -> Option<WindowWrapper<winit::window::Window>> {
|
||||
let winit_id = self.entity_to_winit.remove(&entity)?;
|
||||
self.winit_to_entity.remove(&winit_id);
|
||||
self.windows.remove(&winit_id)
|
||||
|
|
Loading…
Reference in a new issue