diff --git a/crates/bevy_render/src/view/window/mod.rs b/crates/bevy_render/src/view/window/mod.rs index d0f38a3789f52..f402825b96509 100644 --- a/crates/bevy_render/src/view/window/mod.rs +++ b/crates/bevy_render/src/view/window/mod.rs @@ -109,6 +109,8 @@ fn extract_windows( screenshot_manager: Extract>, mut closed: Extract>, windows: Extract)>>, + mut removed: Extract>, + mut window_surfaces: ResMut, ) { for (entity, window, handle, primary) in windows.iter() { if primary.is_some() { @@ -166,6 +168,11 @@ fn extract_windows( for closed_window in closed.read() { extracted_windows.remove(&closed_window.window); + window_surfaces.remove(&closed_window.window); + } + for removed_window in removed.read() { + extracted_windows.remove(&removed_window); + window_surfaces.remove(&removed_window); } // This lock will never block because `callbacks` is `pub(crate)` and this is the singular callsite where it's locked. // Even if a user had multiple copies of this system, since the system has a mutable resource access the two systems would never run @@ -195,6 +202,13 @@ pub struct WindowSurfaces { configured_windows: HashSet, } +impl WindowSurfaces { + fn remove(&mut self, window: &Entity) { + self.surfaces.remove(window); + self.configured_windows.remove(window); + } +} + /// Creates and (re)configures window surfaces, and obtains a swapchain texture for rendering. /// /// NOTE: `get_current_texture` in `prepare_windows` can take a long time if the GPU workload is diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index a13c158709bfe..21b694cdc2982 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -43,6 +43,8 @@ use bevy_window::{ WindowCloseRequested, WindowCreated, WindowDestroyed, WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, WindowThemeChanged, }; +#[cfg(target_os = "android")] +use bevy_window::{PrimaryWindow, RawHandleWrapper}; #[cfg(target_os = "android")] pub use winit::platform::android::activity::AndroidApp; @@ -664,16 +666,55 @@ pub fn winit_runner(mut app: App) { runner_state.is_active = false; #[cfg(target_os = "android")] { - // Android sending this event invalidates all render surfaces. - // TODO - // Upon resume, check if the new render surfaces are compatible with the - // existing render device. If not (which should basically never happen), - // then try to rebuild the renderer. - *control_flow = ControlFlow::Exit; + // Remove the `RawHandleWrapper` from the primary window. + // This will trigger the surface destruction. + let mut query = app.world.query_filtered::>(); + let entity = query.single(&app.world); + app.world.entity_mut(entity).remove::(); + *control_flow = ControlFlow::Wait; } } event::Event::Resumed => { runner_state.is_active = true; + #[cfg(target_os = "android")] + { + // Get windows that are cached but without raw handles. Those window were already created, but got their + // handle wrapper removed when the app was suspended. + let mut query = app + .world + .query_filtered::<(Entity, &Window), (With, Without)>(); + if let Ok((entity, window)) = query.get_single(&app.world) { + use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; + let window = window.clone(); + + let ( + _, + _, + _, + mut winit_windows, + mut adapters, + mut handlers, + accessibility_requested, + ) = create_window_system_state.get_mut(&mut app.world); + + let winit_window = winit_windows.create_window( + event_loop, + entity, + &window, + &mut adapters, + &mut handlers, + &accessibility_requested, + ); + + let wrapper = RawHandleWrapper { + window_handle: winit_window.raw_window_handle(), + display_handle: winit_window.raw_display_handle(), + }; + + app.world.entity_mut(entity).insert(wrapper); + } + *control_flow = ControlFlow::Poll; + } } event::Event::MainEventsCleared => { if runner_state.is_active {