From 0f9939bcb8158707dc70bb577f7e1e600551a223 Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Sat, 16 Sep 2023 15:03:54 +0200 Subject: [PATCH 1/5] Prevent black frames during startup --- crates/bevy_app/src/app.rs | 9 +++++++++ crates/bevy_app/src/main_schedule.rs | 22 +++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 9094773e4e678..5152514ba0d3e 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -288,6 +288,15 @@ impl App { panic!("App::run() was called from within Plugin::build(), which is not allowed."); } + // Finish the startup phase. + Main::startup(&mut app.world); + + // Force plugins to finish their setup and advance by one frame to make sure everything is + // setup up. This is important to prevent black frames during the launch animation on iOS, + // but it seems to also solve a brief flicker during startup on Linux/Wayland. + app.finish(); + app.update(); + let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); (runner)(app); } diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs index 004aba0a6fc0a..ef1773a4b429b 100644 --- a/crates/bevy_app/src/main_schedule.rs +++ b/crates/bevy_app/src/main_schedule.rs @@ -1,7 +1,7 @@ use crate::{App, Plugin}; use bevy_ecs::{ schedule::{ExecutorKind, Schedule, ScheduleLabel}, - system::{Local, Resource}, + system::Resource, world::{Mut, World}, }; @@ -138,15 +138,19 @@ impl MainScheduleOrder { } impl Main { - /// A system that runs the "main schedule" - pub fn run_main(world: &mut World, mut run_at_least_once: Local) { - if !*run_at_least_once { - let _ = world.try_run_schedule(PreStartup); - let _ = world.try_run_schedule(Startup); - let _ = world.try_run_schedule(PostStartup); - *run_at_least_once = true; - } + /// Runs the startup phase. + /// + /// Must be called once before the main schedule is started. + pub fn startup(world: &mut World) { + let _ = world.try_run_schedule(PreStartup); + let _ = world.try_run_schedule(Startup); + let _ = world.try_run_schedule(PostStartup); + + Self::run_main(world); + } + /// A system that runs the "main schedule" + pub fn run_main(world: &mut World) { world.resource_scope(|world, order: Mut| { for label in &order.labels { let _ = world.try_run_schedule(&**label); From 07bd2b4cc64ff0012e1ed3bede5174672e97473f Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Sat, 16 Sep 2023 17:06:03 +0200 Subject: [PATCH 2/5] Move call to running the main schedule directly into app.run() --- crates/bevy_app/src/app.rs | 3 ++- crates/bevy_app/src/main_schedule.rs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 5152514ba0d3e..5a43c2b8526b0 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -288,8 +288,9 @@ impl App { panic!("App::run() was called from within Plugin::build(), which is not allowed."); } - // Finish the startup phase. + // Finish the startup phase and run one clean pass of the main schedule. Main::startup(&mut app.world); + Main::run_main(&mut app.world); // Force plugins to finish their setup and advance by one frame to make sure everything is // setup up. This is important to prevent black frames during the launch animation on iOS, diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs index ef1773a4b429b..6773994e9325d 100644 --- a/crates/bevy_app/src/main_schedule.rs +++ b/crates/bevy_app/src/main_schedule.rs @@ -145,8 +145,6 @@ impl Main { let _ = world.try_run_schedule(PreStartup); let _ = world.try_run_schedule(Startup); let _ = world.try_run_schedule(PostStartup); - - Self::run_main(world); } /// A system that runs the "main schedule" From f886a711a16d64f8cdc770c5930442ae6eb294cd Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Mon, 18 Sep 2023 19:35:52 +0200 Subject: [PATCH 3/5] Only run app.finish() when ready --- crates/bevy_app/src/app.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 5a43c2b8526b0..6f0eb0627213b 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -292,11 +292,13 @@ impl App { Main::startup(&mut app.world); Main::run_main(&mut app.world); - // Force plugins to finish their setup and advance by one frame to make sure everything is - // setup up. This is important to prevent black frames during the launch animation on iOS, - // but it seems to also solve a brief flicker during startup on Linux/Wayland. - app.finish(); - app.update(); + if app.ready() { + // Force plugins to finish their setup and advance by one frame to make sure everything is + // setup up. This is important to prevent black frames during the launch animation on iOS, + // but it seems to also solve a brief flicker during startup on Linux/Wayland. + app.finish(); + app.update(); + } let runner = std::mem::replace(&mut app.runner, Box::new(run_once)); (runner)(app); From 50f83f7565efa51914ffd0c02dd55f7fa9367fef Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Mon, 18 Sep 2023 19:53:17 +0200 Subject: [PATCH 4/5] Revert change in Main schedule --- crates/bevy_app/src/app.rs | 4 ---- crates/bevy_app/src/main_schedule.rs | 20 +++++++++----------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 6f0eb0627213b..5a8e6c590a8ae 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -288,10 +288,6 @@ impl App { panic!("App::run() was called from within Plugin::build(), which is not allowed."); } - // Finish the startup phase and run one clean pass of the main schedule. - Main::startup(&mut app.world); - Main::run_main(&mut app.world); - if app.ready() { // Force plugins to finish their setup and advance by one frame to make sure everything is // setup up. This is important to prevent black frames during the launch animation on iOS, diff --git a/crates/bevy_app/src/main_schedule.rs b/crates/bevy_app/src/main_schedule.rs index 6773994e9325d..004aba0a6fc0a 100644 --- a/crates/bevy_app/src/main_schedule.rs +++ b/crates/bevy_app/src/main_schedule.rs @@ -1,7 +1,7 @@ use crate::{App, Plugin}; use bevy_ecs::{ schedule::{ExecutorKind, Schedule, ScheduleLabel}, - system::Resource, + system::{Local, Resource}, world::{Mut, World}, }; @@ -138,17 +138,15 @@ impl MainScheduleOrder { } impl Main { - /// Runs the startup phase. - /// - /// Must be called once before the main schedule is started. - pub fn startup(world: &mut World) { - let _ = world.try_run_schedule(PreStartup); - let _ = world.try_run_schedule(Startup); - let _ = world.try_run_schedule(PostStartup); - } - /// A system that runs the "main schedule" - pub fn run_main(world: &mut World) { + pub fn run_main(world: &mut World, mut run_at_least_once: Local) { + if !*run_at_least_once { + let _ = world.try_run_schedule(PreStartup); + let _ = world.try_run_schedule(Startup); + let _ = world.try_run_schedule(PostStartup); + *run_at_least_once = true; + } + world.resource_scope(|world, order: Mut| { for label in &order.labels { let _ = world.try_run_schedule(&**label); From 194c904dd3074aba6022613db489e79010bc15d0 Mon Sep 17 00:00:00 2001 From: "Arend van Beelen jr." Date: Mon, 18 Sep 2023 22:31:36 +0200 Subject: [PATCH 5/5] Call cleanup after finish and prevent double calling --- crates/bevy_app/src/app.rs | 6 +++--- crates/bevy_app/src/schedule_runner.rs | 12 +++++++----- crates/bevy_winit/src/lib.rs | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 5a8e6c590a8ae..7c2f7a207cefa 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -289,10 +289,10 @@ impl App { } if app.ready() { - // Force plugins to finish their setup and advance by one frame to make sure everything is - // setup up. This is important to prevent black frames during the launch animation on iOS, - // but it seems to also solve a brief flicker during startup on Linux/Wayland. + // If we're already ready, we finish up now and advance one frame. + // This prevents black frames during the launch transition on iOS. app.finish(); + app.cleanup(); app.update(); } diff --git a/crates/bevy_app/src/schedule_runner.rs b/crates/bevy_app/src/schedule_runner.rs index 0780a1b9a395a..e14687c92ff59 100644 --- a/crates/bevy_app/src/schedule_runner.rs +++ b/crates/bevy_app/src/schedule_runner.rs @@ -71,12 +71,14 @@ impl Plugin for ScheduleRunnerPlugin { fn build(&self, app: &mut App) { let run_mode = self.run_mode; app.set_runner(move |mut app: App| { - while !app.ready() { - #[cfg(not(target_arch = "wasm32"))] - bevy_tasks::tick_global_task_pools_on_main_thread(); + if !app.ready() { + while !app.ready() { + #[cfg(not(target_arch = "wasm32"))] + bevy_tasks::tick_global_task_pools_on_main_thread(); + } + app.finish(); + app.cleanup(); } - app.finish(); - app.cleanup(); let mut app_exit_event_reader = ManualEventReader::::default(); match run_mode { diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index a7cb24a7d1848..38f7d9b6e6cf1 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -378,7 +378,7 @@ pub fn winit_runner(mut app: App) { ResMut, )> = SystemState::from_world(&mut app.world); - let mut finished_and_setup_done = false; + let mut finished_and_setup_done = app.ready(); // setup up the event loop let event_handler = move |event: Event<()>,