Make WinitWindows non send (#4027)

# Objective

- Fixes #4010, as well as any similar issues in this class.
- Winit functions used outside of the main thread can cause the application to unexpectedly hang.

## Solution

- Make the `WinitWindows` resource `!Send`.
- This ensures that any systems that use `WinitWindows` must either be exclusive (run on the main thread), or the resource is explicitly marked with the `NonSend` parameter in user systems.
This commit is contained in:
Aevyrie 2022-02-24 01:40:02 +00:00
parent 81d57e129b
commit a2d49f4a69
2 changed files with 9 additions and 5 deletions

View file

@ -32,7 +32,7 @@ pub struct WinitPlugin;
impl Plugin for WinitPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<WinitWindows>()
app.init_non_send_resource::<WinitWindows>()
.set_runner(winit_runner)
.add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system());
let event_loop = EventLoop::new();
@ -43,7 +43,7 @@ impl Plugin for WinitPlugin {
fn change_window(world: &mut World) {
let world = world.cell();
let winit_windows = world.get_resource::<WinitWindows>().unwrap();
let winit_windows = world.get_non_send::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
for bevy_window in windows.iter_mut() {
@ -264,7 +264,7 @@ pub fn winit_runner_with(mut app: App) {
..
} => {
let world = app.world.cell();
let winit_windows = world.get_resource_mut::<WinitWindows>().unwrap();
let winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let window_id =
if let Some(window_id) = winit_windows.get_window_id(winit_window_id) {
@ -525,7 +525,7 @@ fn handle_create_window_events(
create_window_event_reader: &mut ManualEventReader<CreateWindow>,
) {
let world = world.cell();
let mut winit_windows = world.get_resource_mut::<WinitWindows>().unwrap();
let mut winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let create_window_events = world.get_resource::<Events<CreateWindow>>().unwrap();
let mut window_created_events = world.get_resource_mut::<Events<WindowCreated>>().unwrap();
@ -544,7 +544,7 @@ fn handle_create_window_events(
fn handle_initial_window_events(world: &mut World, event_loop: &EventLoop<()>) {
let world = world.cell();
let mut winit_windows = world.get_resource_mut::<WinitWindows>().unwrap();
let mut winit_windows = world.get_non_send_mut::<WinitWindows>().unwrap();
let mut windows = world.get_resource_mut::<Windows>().unwrap();
let mut create_window_events = world.get_resource_mut::<Events<CreateWindow>>().unwrap();
let mut window_created_events = world.get_resource_mut::<Events<WindowCreated>>().unwrap();

View file

@ -9,6 +9,10 @@ pub struct WinitWindows {
pub windows: HashMap<winit::window::WindowId, winit::window::Window>,
pub window_id_to_winit: HashMap<WindowId, winit::window::WindowId>,
pub winit_to_window_id: HashMap<winit::window::WindowId, WindowId>,
// Some winit functions, such as `set_window_icon` can only be used from the main thread. If
// they are used in another thread, the app will hang. This marker ensures `WinitWindows` is
// only ever accessed with bevy's non-send functions and in NonSend systems.
_not_send_sync: core::marker::PhantomData<*const ()>,
}
impl WinitWindows {