From c03263c30afa341e3a0aef9ebaae61d6490d2452 Mon Sep 17 00:00:00 2001 From: Jet Spark Date: Wed, 29 Sep 2021 16:20:57 +0800 Subject: [PATCH 1/5] (WinitPlugin) Game tick dynamically --- crates/bevy_winit/src/lib.rs | 24 ++++++++++++++++++++--- crates/bevy_winit/src/winit_tick.rs | 30 +++++++++++++++++++++++++++++ examples/ui/ui.rs | 2 ++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 crates/bevy_winit/src/winit_tick.rs diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 294ba67cd9fc7..faea22a7fb38e 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -1,5 +1,6 @@ mod converters; mod winit_config; +mod winit_tick; mod winit_windows; use bevy_input::{ @@ -8,10 +9,15 @@ use bevy_input::{ touch::TouchInput, }; pub use winit_config::*; +pub use winit_tick::*; pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Events, ManualEventReader, Plugin}; -use bevy_ecs::{system::IntoExclusiveSystem, world::World}; +use bevy_ecs::{ + prelude::NonSend, + system::{IntoExclusiveSystem, Res}, + world::World, +}; use bevy_math::{ivec2, Vec2}; use bevy_utils::tracing::{error, trace, warn}; use bevy_window::{ @@ -19,6 +25,7 @@ use bevy_window::{ WindowBackendScaleFactorChanged, WindowCloseRequested, WindowCreated, WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, Windows, }; +use winit::event_loop::EventLoopProxy; use winit::{ dpi::PhysicalPosition, event::{self, DeviceEvent, Event, WindowEvent}, @@ -40,9 +47,20 @@ pub struct WinitPlugin; impl Plugin for WinitPlugin { fn build(&self, app: &mut App) { + if app.world.get_resource::().is_none() { + app.init_resource::(); + } + app.init_resource::() .set_runner(winit_runner) - .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()); + .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()) + .add_system_to_stage(CoreStage::Last, tick.exclusive_system()); + } +} + +fn tick(winit_tick: Res, event_loop_proxy: NonSend>) { + if winit_tick.finish() { + let _ = event_loop_proxy.send_event(()); } } @@ -238,7 +256,7 @@ pub fn winit_runner_with(mut app: App, mut event_loop: EventLoop<()>) { let event_handler = move |event: Event<()>, event_loop: &EventLoopWindowTarget<()>, control_flow: &mut ControlFlow| { - *control_flow = ControlFlow::Poll; + *control_flow = ControlFlow::Wait; if let Some(app_exit_events) = app.world.get_resource_mut::>() { if app_exit_event_reader diff --git a/crates/bevy_winit/src/winit_tick.rs b/crates/bevy_winit/src/winit_tick.rs new file mode 100644 index 0000000000000..23fe4af72694b --- /dev/null +++ b/crates/bevy_winit/src/winit_tick.rs @@ -0,0 +1,30 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +pub struct WinitTick { + default_poll_tick: bool, + should_poll_tick: AtomicBool, +} + +impl Default for WinitTick { + fn default() -> Self { + Self::new(true) + } +} + +impl WinitTick { + pub fn new(default_poll_tick: bool) -> Self { + Self { + default_poll_tick, + should_poll_tick: AtomicBool::new(default_poll_tick), + } + } + + pub fn poll_tick(&self) { + self.should_poll_tick.store(true, Ordering::Relaxed); + } + + pub fn finish(&self) -> bool { + self.should_poll_tick + .swap(self.default_poll_tick, Ordering::Relaxed) + } +} diff --git a/examples/ui/ui.rs b/examples/ui/ui.rs index 7278ce21cef93..083f23796d34c 100644 --- a/examples/ui/ui.rs +++ b/examples/ui/ui.rs @@ -1,9 +1,11 @@ use bevy::prelude::*; +use bevy::winit::WinitTick; /// This example illustrates the various features of Bevy UI. fn main() { App::new() .add_plugins(DefaultPlugins) + .insert_resource(WinitTick::new(false)) .add_startup_system(setup) .run(); } From 8e787f51005af4039f27b0164476f8c4872df81b Mon Sep 17 00:00:00 2001 From: Jet Spark Date: Wed, 29 Sep 2021 17:03:43 +0800 Subject: [PATCH 2/5] (WinitPlugin) Add documentation for WinitTick --- crates/bevy_winit/src/winit_tick.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/bevy_winit/src/winit_tick.rs b/crates/bevy_winit/src/winit_tick.rs index 23fe4af72694b..da4e880eac027 100644 --- a/crates/bevy_winit/src/winit_tick.rs +++ b/crates/bevy_winit/src/winit_tick.rs @@ -1,5 +1,6 @@ use std::sync::atomic::{AtomicBool, Ordering}; +/// A resource for dynamically control of game tick. pub struct WinitTick { default_poll_tick: bool, should_poll_tick: AtomicBool, @@ -12,6 +13,11 @@ impl Default for WinitTick { } impl WinitTick { + /// Create a `WinitTick` resource with the specified `default_poll_tick`. + /// `false` is recommended for GUI applications, animation systems can call + /// `poll_tick` with `WinitTick` resource to poll game tick for the next frame. + /// + /// Default value of `default_poll_tick` parameter is `true`. pub fn new(default_poll_tick: bool) -> Self { Self { default_poll_tick, From 148cf7258f571b13d9368c19f861ce99283f66a4 Mon Sep 17 00:00:00 2001 From: Jet Spark Date: Wed, 29 Sep 2021 20:09:55 +0800 Subject: [PATCH 3/5] Send a tick event to event loop when asset is loaded asynchronously --- crates/bevy_asset/src/asset_server.rs | 13 +++++++++++++ crates/bevy_winit/Cargo.toml | 2 ++ crates/bevy_winit/src/lib.rs | 19 +++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/crates/bevy_asset/src/asset_server.rs b/crates/bevy_asset/src/asset_server.rs index 4dcfee66afb7e..5dc78ebbe3f32 100644 --- a/crates/bevy_asset/src/asset_server.rs +++ b/crates/bevy_asset/src/asset_server.rs @@ -29,6 +29,10 @@ pub enum AssetServerError { AssetIoError(#[from] AssetIoError), } +pub trait AssetNotify: Send + Sync + 'static { + fn notify(&self); +} + fn format_missing_asset_ext(exts: &[String]) -> String { if !exts.is_empty() { format!( @@ -53,6 +57,7 @@ pub struct AssetServerInternal { pub(crate) asset_ref_counter: AssetRefCounter, pub(crate) asset_sources: Arc>>, pub(crate) asset_lifecycles: Arc>>>, + asset_notify: RwLock>>, loaders: RwLock>>, extension_to_loader_index: RwLock>, handle_to_path: Arc>>>, @@ -86,6 +91,7 @@ impl AssetServer { asset_ref_counter: Default::default(), handle_to_path: Default::default(), asset_lifecycles: Default::default(), + asset_notify: Default::default(), task_pool, asset_io, }), @@ -115,6 +121,10 @@ impl AssetServer { loaders.push(Arc::new(loader)); } + pub fn replace_notify(&self, asset_notify: Box) { + *self.server.asset_notify.write() = Some(asset_notify); + } + pub fn watch_for_changes(&self) -> Result<(), AssetServerError> { self.server.asset_io.watch_for_changes()?; Ok(()) @@ -371,6 +381,8 @@ impl AssetServer { .spawn(async move { if let Err(err) = server.load_async(owned_path, force).await { warn!("{}", err); + } else if let Some(asset_notify) = &*server.server.asset_notify.read() { + asset_notify.notify(); } }) .detach(); @@ -617,6 +629,7 @@ mod test { asset_ref_counter: Default::default(), handle_to_path: Default::default(), asset_lifecycles: Default::default(), + asset_notify: Default::default(), task_pool: Default::default(), asset_io: Box::new(FileAssetIo::new(asset_path)), }), diff --git a/crates/bevy_winit/Cargo.toml b/crates/bevy_winit/Cargo.toml index 3b8c56af12645..b444db03107cf 100644 --- a/crates/bevy_winit/Cargo.toml +++ b/crates/bevy_winit/Cargo.toml @@ -15,6 +15,7 @@ x11 = ["winit/x11"] [dependencies] # bevy bevy_app = { path = "../bevy_app", version = "0.5.0" } +bevy_asset = { path = "../bevy_asset", version = "0.5.0" } bevy_ecs = { path = "../bevy_ecs", version = "0.5.0" } bevy_input = { path = "../bevy_input", version = "0.5.0" } bevy_math = { path = "../bevy_math", version = "0.5.0" } @@ -24,6 +25,7 @@ bevy_utils = { path = "../bevy_utils", version = "0.5.0" } # other winit = { version = "0.25.0", default-features = false } approx = { version = "0.5.0", default-features = false } +parking_lot = "0.11.0" [target.'cfg(target_arch = "wasm32")'.dependencies] winit = { version = "0.25.0", features = ["web-sys"], default-features = false } diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index faea22a7fb38e..24ee5f9de5097 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -13,6 +13,7 @@ pub use winit_tick::*; pub use winit_windows::*; use bevy_app::{App, AppExit, CoreStage, Events, ManualEventReader, Plugin}; +use bevy_asset::{AssetNotify, AssetServer}; use bevy_ecs::{ prelude::NonSend, system::{IntoExclusiveSystem, Res}, @@ -25,6 +26,7 @@ use bevy_window::{ WindowBackendScaleFactorChanged, WindowCloseRequested, WindowCreated, WindowFocused, WindowMoved, WindowResized, WindowScaleFactorChanged, Windows, }; +use parking_lot::Mutex; use winit::event_loop::EventLoopProxy; use winit::{ dpi::PhysicalPosition, @@ -42,6 +44,16 @@ use winit::dpi::LogicalSize; ))] use winit::platform::unix::EventLoopExtUnix; +struct WinitNotify { + event_loop_proxy: Mutex>, +} + +impl AssetNotify for WinitNotify { + fn notify(&self) { + let _ = self.event_loop_proxy.lock().send_event(()); + } +} + #[derive(Default)] pub struct WinitPlugin; @@ -53,11 +65,18 @@ impl Plugin for WinitPlugin { app.init_resource::() .set_runner(winit_runner) + .add_startup_system(setup.exclusive_system()) .add_system_to_stage(CoreStage::PostUpdate, change_window.exclusive_system()) .add_system_to_stage(CoreStage::Last, tick.exclusive_system()); } } +fn setup(asset_server: Res, event_loop_proxy: NonSend>) { + asset_server.replace_notify(Box::new(WinitNotify { + event_loop_proxy: Mutex::new(event_loop_proxy.clone()), + })); +} + fn tick(winit_tick: Res, event_loop_proxy: NonSend>) { if winit_tick.finish() { let _ = event_loop_proxy.send_event(()); From d82ce674778f439891d4f94732192e52407b29e8 Mon Sep 17 00:00:00 2001 From: xiaopengli89 Date: Thu, 7 Oct 2021 12:24:13 +0800 Subject: [PATCH 4/5] Impl Send for WinitNotify on platform wasm32 --- crates/bevy_winit/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 24ee5f9de5097..dfe79572e0511 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -54,6 +54,10 @@ impl AssetNotify for WinitNotify { } } +// SAFETY: We clone the EventLoopProxy in exclusive system, and never destroy it. +#[cfg(target_arch = "wasm32")] +unsafe impl Send for WinitNotify {} + #[derive(Default)] pub struct WinitPlugin; From 725a43e44a9b66538b6164bf0e329c90947be5df Mon Sep 17 00:00:00 2001 From: xiaopengli89 Date: Thu, 7 Oct 2021 12:51:31 +0800 Subject: [PATCH 5/5] Impl Sync for WinitNotify on platform wasm32 --- crates/bevy_winit/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index dfe79572e0511..0e71aa7280fa9 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -58,6 +58,9 @@ impl AssetNotify for WinitNotify { #[cfg(target_arch = "wasm32")] unsafe impl Send for WinitNotify {} +#[cfg(target_arch = "wasm32")] +unsafe impl Sync for WinitNotify {} + #[derive(Default)] pub struct WinitPlugin;