Skip to content

Commit 5a01357

Browse files
committed
Introduce a WindowWrapper to extend the lifetime of the window when using pipelined rendering
1 parent 62f2a73 commit 5a01357

File tree

3 files changed

+56
-11
lines changed

3 files changed

+56
-11
lines changed

crates/bevy_window/src/raw_handle.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,38 @@ use raw_window_handle::{
55
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
66
RawWindowHandle, WindowHandle,
77
};
8+
use std::{any::Any, marker::PhantomData, ops::Deref, sync::Arc};
9+
10+
/// A wrapper over a window.
11+
///
12+
/// This allows us to extend the lifetime of the window, so it doesn't get eagerly dropped while a
13+
/// pipelined renderer still has frames in flight that need to draw to it.
14+
///
15+
/// This is achieved by storing a shared reference to the window in the [`RawHandleWrapper`],
16+
/// which gets picked up by the renderer during extraction.
17+
#[derive(Debug)]
18+
pub struct WindowWrapper<W> {
19+
reference: Arc<dyn Any + Send + Sync>,
20+
ty: PhantomData<W>,
21+
}
22+
23+
impl<W: Send + Sync + 'static> WindowWrapper<W> {
24+
/// Creates a `WindowWrapper` from a window.
25+
pub fn new(window: W) -> WindowWrapper<W> {
26+
WindowWrapper {
27+
reference: Arc::new(window),
28+
ty: PhantomData,
29+
}
30+
}
31+
}
32+
33+
impl<W: 'static> Deref for WindowWrapper<W> {
34+
type Target = W;
35+
36+
fn deref(&self) -> &Self::Target {
37+
self.reference.downcast_ref::<W>().unwrap()
38+
}
39+
}
840

941
/// A wrapper over [`RawWindowHandle`] and [`RawDisplayHandle`] that allows us to safely pass it across threads.
1042
///
@@ -13,13 +45,25 @@ use raw_window_handle::{
1345
/// thread-safe.
1446
#[derive(Debug, Clone, Component)]
1547
pub struct RawHandleWrapper {
48+
_window: Arc<dyn Any + Send + Sync>,
1649
/// Raw handle to a window.
1750
pub window_handle: RawWindowHandle,
1851
/// Raw handle to the display server.
1952
pub display_handle: RawDisplayHandle,
2053
}
2154

2255
impl RawHandleWrapper {
56+
/// Creates a `RawHandleWrapper` from a `WindowWrapper`.
57+
pub fn new<W: HasWindowHandle + HasDisplayHandle + 'static>(
58+
window: &WindowWrapper<W>,
59+
) -> Result<RawHandleWrapper, HandleError> {
60+
Ok(RawHandleWrapper {
61+
_window: window.reference.clone(),
62+
window_handle: window.window_handle()?.as_raw(),
63+
display_handle: window.display_handle()?.as_raw(),
64+
})
65+
}
66+
2367
/// Returns a [`HasWindowHandle`] + [`HasDisplayHandle`] impl, which exposes [`WindowHandle`] and [`DisplayHandle`].
2468
///
2569
/// # Safety

crates/bevy_winit/src/system.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ use bevy_window::{
1111
RawHandleWrapper, Window, WindowClosed, WindowCreated, WindowMode, WindowResized,
1212
};
1313

14-
use raw_window_handle::{HasDisplayHandle, HasWindowHandle};
1514
use winit::{
1615
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize},
1716
event_loop::EventLoopWindowTarget,
@@ -75,10 +74,7 @@ pub fn create_windows<F: QueryFilter + 'static>(
7574
.set_scale_factor(winit_window.scale_factor() as f32);
7675
commands
7776
.entity(entity)
78-
.insert(RawHandleWrapper {
79-
window_handle: winit_window.window_handle().unwrap().as_raw(),
80-
display_handle: winit_window.display_handle().unwrap().as_raw(),
81-
})
77+
.insert(RawHandleWrapper::new(winit_window).unwrap())
8278
.insert(CachedWindow {
8379
window: window.clone(),
8480
});

crates/bevy_winit/src/winit_windows.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ use bevy_ecs::entity::Entity;
33

44
use bevy_ecs::entity::EntityHashMap;
55
use bevy_utils::{tracing::warn, HashMap};
6-
use bevy_window::{CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution};
6+
use bevy_window::{
7+
CursorGrabMode, Window, WindowMode, WindowPosition, WindowResolution, WindowWrapper,
8+
};
79

810
use winit::{
911
dpi::{LogicalSize, PhysicalPosition},
@@ -20,7 +22,7 @@ use crate::{
2022
#[derive(Debug, Default)]
2123
pub struct WinitWindows {
2224
/// Stores [`winit`] windows by window identifier.
23-
pub windows: HashMap<winit::window::WindowId, winit::window::Window>,
25+
pub windows: HashMap<winit::window::WindowId, WindowWrapper<winit::window::Window>>,
2426
/// Maps entities to `winit` window identifiers.
2527
pub entity_to_winit: EntityHashMap<winit::window::WindowId>,
2628
/// Maps `winit` window identifiers to entities.
@@ -41,7 +43,7 @@ impl WinitWindows {
4143
adapters: &mut AccessKitAdapters,
4244
handlers: &mut WinitActionHandlers,
4345
accessibility_requested: &AccessibilityRequested,
44-
) -> &winit::window::Window {
46+
) -> &WindowWrapper<winit::window::Window> {
4547
let mut winit_window_builder = winit::window::WindowBuilder::new();
4648

4749
// Due to a UIA limitation, winit windows need to be invisible for the
@@ -240,12 +242,12 @@ impl WinitWindows {
240242

241243
self.windows
242244
.entry(winit_window.id())
243-
.insert(winit_window)
245+
.insert(WindowWrapper::new(winit_window))
244246
.into_mut()
245247
}
246248

247249
/// Get the winit window that is associated with our entity.
248-
pub fn get_window(&self, entity: Entity) -> Option<&winit::window::Window> {
250+
pub fn get_window(&self, entity: Entity) -> Option<&WindowWrapper<winit::window::Window>> {
249251
self.entity_to_winit
250252
.get(&entity)
251253
.and_then(|winit_id| self.windows.get(winit_id))
@@ -261,7 +263,10 @@ impl WinitWindows {
261263
/// Remove a window from winit.
262264
///
263265
/// This should mostly just be called when the window is closing.
264-
pub fn remove_window(&mut self, entity: Entity) -> Option<winit::window::Window> {
266+
pub fn remove_window(
267+
&mut self,
268+
entity: Entity,
269+
) -> Option<WindowWrapper<winit::window::Window>> {
265270
let winit_id = self.entity_to_winit.remove(&entity)?;
266271
self.winit_to_entity.remove(&winit_id);
267272
self.windows.remove(&winit_id)

0 commit comments

Comments
 (0)