mirror of
https://github.com/bevyengine/bevy
synced 2024-11-10 07:04:33 +00:00
Android: handle suspend / resume (#9937)
# Objective - Handle suspend / resume events on Android without exiting ## Solution - On suspend: despawn the window, and set the control flow to wait for events from the OS - On resume: spawn a new window, and set the control flow to poll In this video, you can see the Android example being suspended, stopping receiving events, and working again after being resumed https://github.com/bevyengine/bevy/assets/8672791/aaaf4b09-ee6a-4a0d-87ad-41f05def7945
This commit is contained in:
parent
1bf271d56e
commit
eb1effa643
2 changed files with 61 additions and 6 deletions
|
@ -109,6 +109,8 @@ fn extract_windows(
|
|||
screenshot_manager: Extract<Res<ScreenshotManager>>,
|
||||
mut closed: Extract<EventReader<WindowClosed>>,
|
||||
windows: Extract<Query<(Entity, &Window, &RawHandleWrapper, Option<&PrimaryWindow>)>>,
|
||||
mut removed: Extract<RemovedComponents<RawHandleWrapper>>,
|
||||
mut window_surfaces: ResMut<WindowSurfaces>,
|
||||
) {
|
||||
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<Entity>,
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -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::<Entity, With<PrimaryWindow>>();
|
||||
let entity = query.single(&app.world);
|
||||
app.world.entity_mut(entity).remove::<RawHandleWrapper>();
|
||||
*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<CachedWindow>, Without<bevy_window::RawHandleWrapper>)>();
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue