Skip to content

Commit

Permalink
fix: upgrade to winit v0.30 (#13366)
Browse files Browse the repository at this point in the history
# Objective

- Upgrade winit to v0.30
- Fixes #13331

## Solution

This is a rewrite/adaptation of the new trait system described and
implemented in `winit` v0.30.

## Migration Guide

The custom UserEvent is now renamed as WakeUp, used to wake up the loop
if anything happens outside the app (a new
[custom_user_event](https://github.com/bevyengine/bevy/pull/13366/files#diff-2de8c0a8d3028d0059a3d80ae31b2bbc1cde2595ce2d317ea378fe3e0cf6ef2d)
shows this behavior.

The internal `UpdateState` has been removed and replaced internally by
the AppLifecycle. When changed, the AppLifecycle is sent as an event.

The `UpdateMode` now accepts only two values: `Continuous` and
`Reactive`, but the latter exposes 3 new properties to enable reactive
to device, user or window events. The previous `UpdateMode::Reactive` is
now equivalent to `UpdateMode::reactive()`, while
`UpdateMode::ReactiveLowPower` to `UpdateMode::reactive_low_power()`.

The `ApplicationLifecycle` has been renamed as `AppLifecycle`, and now
contains the possible values of the application state inside the event
loop:
* `Idle`: the loop has not started yet
* `Running` (previously called `Started`): the loop is running
* `WillSuspend`: the loop is going to be suspended
* `Suspended`: the loop is suspended
* `WillResume`: the loop is going to be resumed

Note: the `Resumed` state has been removed since the resumed app is just
running.

Finally, now that `winit` enables this, it extends the `WinitPlugin` to
support custom events.

## Test platforms

- [x] Windows
- [x] MacOs
- [x] Linux (x11)
- [x] Linux (Wayland)
- [x] Android
- [x] iOS
- [x] WASM/WebGPU
- [x] WASM/WebGL2

## Outstanding issues / regressions

- [ ] iOS: build failed in CI
   - blocking, but may just be flakiness
- [x] Cross-platform: when the window is maximised, changes in the scale
factor don't apply, to make them apply one has to make the window
smaller again. (Re-maximising keeps the updated scale factor)
    - non-blocking, but good to fix
- [ ] Android: it's pretty easy to quickly open and close the app and
then the music keeps playing when suspended.
    - non-blocking but worrying
- [ ]  Web: the application will hang when switching tabs
- Not new, duplicate of #13486
- [ ] Cross-platform?: Screenshot failure, `ERROR present_frames:
wgpu_core::present: No work has been submitted for this frame before`
taking the first screenshot, but after pressing space
    - non-blocking, but good to fix

---------

Co-authored-by: François <[email protected]>
  • Loading branch information
pietrosophya and mockersf authored Jun 3, 2024
1 parent 44c8cc6 commit 061bee7
Show file tree
Hide file tree
Showing 25 changed files with 1,349 additions and 1,021 deletions.
15 changes: 15 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,10 @@ argh = "0.1.12"
thiserror = "1.0"
event-listener = "5.3.0"

[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen = { version = "0.2" }
web-sys = { version = "0.3", features = ["Window"] }

[[example]]
name = "hello_world"
path = "examples/hello_world.rs"
Expand Down Expand Up @@ -2836,6 +2840,17 @@ description = "Creates a solid color window"
category = "Window"
wasm = true

[[example]]
name = "custom_user_event"
path = "examples/window/custom_user_event.rs"
doc-scrape-examples = true

[package.metadata.example.custom_user_event]
name = "Custom User Event"
description = "Handles custom user events within the event loop"
category = "Window"
wasm = true

[[example]]
name = "low_power"
path = "examples/window/low_power.rs"
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_a11y/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ bevy_app = { path = "../bevy_app", version = "0.14.0-dev" }
bevy_derive = { path = "../bevy_derive", version = "0.14.0-dev" }
bevy_ecs = { path = "../bevy_ecs", version = "0.14.0-dev" }

accesskit = "0.12"
accesskit = "0.14"

[lints]
workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_internal/src/default_plugins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ impl PluginGroup for DefaultPlugins {

#[cfg(feature = "bevy_winit")]
{
group = group.add(bevy_winit::WinitPlugin::default());
group = group.add::<bevy_winit::WinitPlugin>(bevy_winit::WinitPlugin::default());
}

#[cfg(feature = "bevy_render")]
Expand Down
24 changes: 16 additions & 8 deletions crates/bevy_render/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ use bevy_utils::prelude::default;
pub use extract_param::Extract;

use bevy_hierarchy::ValidParentCheckPlugin;
use bevy_window::{PrimaryWindow, RawHandleWrapper};
use bevy_window::{PrimaryWindow, RawHandleWrapperHolder};
use extract_resource::ExtractResourcePlugin;
use globals::GlobalsPlugin;
use render_asset::RenderAssetBytesPerFrame;
Expand Down Expand Up @@ -268,10 +268,9 @@ impl Plugin for RenderPlugin {
));

let mut system_state: SystemState<
Query<&RawHandleWrapper, With<PrimaryWindow>>,
Query<&RawHandleWrapperHolder, With<PrimaryWindow>>,
> = SystemState::new(app.world_mut());
let primary_window = system_state.get(app.world()).get_single().ok().cloned();

let settings = render_creation.clone();
let async_renderer = async move {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
Expand All @@ -282,11 +281,20 @@ impl Plugin for RenderPlugin {
});

// SAFETY: Plugins should be set up on the main thread.
let surface = primary_window.map(|wrapper| unsafe {
let handle = wrapper.get_handle();
instance
.create_surface(handle)
.expect("Failed to create wgpu surface")
let surface = primary_window.and_then(|wrapper| unsafe {
let maybe_handle = wrapper.0.lock().expect(
"Couldn't get the window handle in time for renderer initialization",
);
if let Some(wrapper) = maybe_handle.as_ref() {
let handle = wrapper.get_handle();
Some(
instance
.create_surface(handle)
.expect("Failed to create wgpu surface"),
)
} else {
None
}
});

let request_adapter_options = wgpu::RequestAdapterOptions {
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_tasks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ concurrent-queue = { version = "2.0.0", optional = true }
wasm-bindgen-futures = "0.4"

[dev-dependencies]
web-time = { version = "0.2" }
web-time = { version = "1.1" }

[lints]
workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ detailed_trace = []
[dependencies]
ahash = "0.8.7"
tracing = { version = "0.1", default-features = false, features = ["std"] }
web-time = { version = "0.2" }
web-time = { version = "1.1" }
hashbrown = { version = "0.14", features = ["serde"] }
bevy_utils_proc_macros = { version = "0.14.0-dev", path = "macros" }
thread_local = "1.0"
Expand Down
29 changes: 22 additions & 7 deletions crates/bevy_window/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,13 +388,28 @@ pub struct WindowThemeChanged {
derive(serde::Serialize, serde::Deserialize),
reflect(Serialize, Deserialize)
)]
pub enum ApplicationLifetime {
/// The application just started.
Started,
pub enum AppLifecycle {
/// The application is not started yet.
Idle,
/// The application is running.
Running,
/// The application is going to be suspended.
/// Applications have one frame to react to this event before being paused in the background.
WillSuspend,
/// The application was suspended.
///
/// On Android, applications have one frame to react to this event before being paused in the background.
Suspended,
/// The application was resumed.
Resumed,
/// The application is going to be resumed.
/// Applications have one extra frame to react to this event before being fully resumed.
WillResume,
}

impl AppLifecycle {
/// Return `true` if the app can be updated.
#[inline]
pub fn is_active(&self) -> bool {
match self {
Self::Idle | Self::Suspended => false,
Self::Running | Self::WillSuspend | Self::WillResume => true,
}
}
}
11 changes: 8 additions & 3 deletions crates/bevy_window/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
//! The [`WindowPlugin`] sets up some global window-related parameters and
//! is part of the [`DefaultPlugins`](https://docs.rs/bevy/latest/bevy/struct.DefaultPlugins.html).
use std::sync::{Arc, Mutex};

use bevy_a11y::Focus;

mod cursor;
Expand Down Expand Up @@ -106,13 +108,16 @@ impl Plugin for WindowPlugin {
.add_event::<FileDragAndDrop>()
.add_event::<WindowMoved>()
.add_event::<WindowThemeChanged>()
.add_event::<ApplicationLifetime>();
.add_event::<AppLifecycle>();

if let Some(primary_window) = &self.primary_window {
let initial_focus = app
.world_mut()
.spawn(primary_window.clone())
.insert(PrimaryWindow)
.insert((
PrimaryWindow,
RawHandleWrapperHolder(Arc::new(Mutex::new(None))),
))
.id();
if let Some(mut focus) = app.world_mut().get_resource_mut::<Focus>() {
**focus = Some(initial_focus);
Expand Down Expand Up @@ -153,7 +158,7 @@ impl Plugin for WindowPlugin {
.register_type::<FileDragAndDrop>()
.register_type::<WindowMoved>()
.register_type::<WindowThemeChanged>()
.register_type::<ApplicationLifetime>();
.register_type::<AppLifecycle>();

// Register window descriptor and related types
app.register_type::<Window>()
Expand Down
11 changes: 10 additions & 1 deletion crates/bevy_window/src/raw_handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ use raw_window_handle::{
DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle,
RawWindowHandle, WindowHandle,
};
use std::{any::Any, marker::PhantomData, ops::Deref, sync::Arc};
use std::{
any::Any,
marker::PhantomData,
ops::Deref,
sync::{Arc, Mutex},
};

/// A wrapper over a window.
///
Expand Down Expand Up @@ -116,3 +121,7 @@ impl HasDisplayHandle for ThreadLockedRawWindowHandleWrapper {
Ok(unsafe { DisplayHandle::borrow_raw(self.0.display_handle) })
}
}

/// Holder of the [`RawHandleWrapper`] with wrappers, to allow use in asynchronous context
#[derive(Debug, Clone, Component)]
pub struct RawHandleWrapperHolder(pub Arc<Mutex<Option<RawHandleWrapper>>>);
10 changes: 3 additions & 7 deletions crates/bevy_window/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -687,10 +687,10 @@ impl Default for WindowResolution {

impl WindowResolution {
/// Creates a new [`WindowResolution`].
pub fn new(logical_width: f32, logical_height: f32) -> Self {
pub fn new(physical_width: f32, physical_height: f32) -> Self {
Self {
physical_width: logical_width as u32,
physical_height: logical_height as u32,
physical_width: physical_width as u32,
physical_height: physical_height as u32,
..Default::default()
}
}
Expand Down Expand Up @@ -783,9 +783,7 @@ impl WindowResolution {
/// Set the window's scale factor, this may get overridden by the backend.
#[inline]
pub fn set_scale_factor(&mut self, scale_factor: f32) {
let (width, height) = (self.width(), self.height());
self.scale_factor = scale_factor;
self.set(width, height);
}

/// Set the window's scale factor, this will be used over what the backend decides.
Expand All @@ -794,9 +792,7 @@ impl WindowResolution {
/// size is not within the limits.
#[inline]
pub fn set_scale_factor_override(&mut self, scale_factor_override: Option<f32>) {
let (width, height) = (self.width(), self.height());
self.scale_factor_override = scale_factor_override;
self.set(width, height);
}
}

Expand Down
6 changes: 3 additions & 3 deletions crates/bevy_winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ bevy_tasks = { path = "../bevy_tasks", version = "0.14.0-dev" }

# other
# feature rwh_06 refers to [email protected]
winit = { version = "0.29", default-features = false, features = ["rwh_06"] }
accesskit_winit = { version = "0.17", default-features = false, features = [
winit = { version = "0.30", default-features = false, features = ["rwh_06"] }
accesskit_winit = { version = "0.20", default-features = false, features = [
"rwh_06",
] }
approx = { version = "0.5", default-features = false }
Expand All @@ -42,7 +42,7 @@ raw-window-handle = "0.6"
serde = { version = "1.0", features = ["derive"], optional = true }

[target.'cfg(target_os = "android")'.dependencies]
winit = { version = "0.29", default-features = false, features = [
winit = { version = "0.30", default-features = false, features = [
"android-native-activity",
"rwh_06",
] }
Expand Down
Loading

0 comments on commit 061bee7

Please sign in to comment.