mirror of
https://github.com/bevyengine/bevy
synced 2024-11-29 08:00:20 +00:00
Prevent black frames during startup (#9826)
# Objective This PR addresses the issue where Bevy displays one or several black frames before the scene is first rendered. This is particularly noticeable on iOS, where the black frames disrupt the transition from the launch screen to the game UI. I have written about my search to solve this issue on the Bevy discord: https://discord.com/channels/691052431525675048/1151047604520632352 While I can attest this PR works on both iOS and Linux/Wayland (and even seems to resolve a slight flicker during startup with the latter as well), I'm not familiar enough with Bevy to judge the full implications of these changes. I hope a reviewer or tester can help me confirm whether this is the right approach, or what might be a cleaner solution to resolve this issue. ## Solution I have moved the "startup phase" as well as the plugin finalization into the `app.run()` function so those things finish synchronously before the "main schedule" starts. I even move one frame forward as well, using `app.update()`, to make sure the rendering has caught up with the state of the finalized plugins as well. I admit that part of this was achieved through trial-and-error, since not doing the "startup phase" *before* `app.finish()` resulted in panics, while not calling an extra `app.update()` didn't fully resolve the issue. What I *can* say, is that the iOS launch screen animation works in such a way that the OS initiates the transition once the framework's [`didFinishLaunching()`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) returns, meaning app developers **must** finish setting up their UI before that function returns. This is what basically led me on the path to try to "finish stuff earlier" :) ## Changelog ### Changed - The startup phase and the first frame are rendered synchronously when calling `app.run()`, before the "main schedule" is started. This fixes black frames during the iOS launch transition and possible flickering on other platforms, but may affect initialization order in your application. ## Migration Guide Because of this change, the timing of the first few frames might have changed, and I think it could be that some things one may expect to be initialized in a system may no longer be. To be honest, I feel out of my depth to judge the exact impact here.
This commit is contained in:
parent
4b65a533f1
commit
5d110eb96e
3 changed files with 16 additions and 6 deletions
|
@ -288,6 +288,14 @@ impl App {
|
||||||
panic!("App::run() was called from within Plugin::build(), which is not allowed.");
|
panic!("App::run() was called from within Plugin::build(), which is not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if app.ready() {
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
let runner = std::mem::replace(&mut app.runner, Box::new(run_once));
|
let runner = std::mem::replace(&mut app.runner, Box::new(run_once));
|
||||||
(runner)(app);
|
(runner)(app);
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,12 +71,14 @@ impl Plugin for ScheduleRunnerPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
let run_mode = self.run_mode;
|
let run_mode = self.run_mode;
|
||||||
app.set_runner(move |mut app: App| {
|
app.set_runner(move |mut app: App| {
|
||||||
while !app.ready() {
|
if !app.ready() {
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
while !app.ready() {
|
||||||
bevy_tasks::tick_global_task_pools_on_main_thread();
|
#[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::<AppExit>::default();
|
let mut app_exit_event_reader = ManualEventReader::<AppExit>::default();
|
||||||
match run_mode {
|
match run_mode {
|
||||||
|
|
|
@ -378,7 +378,7 @@ pub fn winit_runner(mut app: App) {
|
||||||
ResMut<CanvasParentResizeEventChannel>,
|
ResMut<CanvasParentResizeEventChannel>,
|
||||||
)> = SystemState::from_world(&mut app.world);
|
)> = 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
|
// setup up the event loop
|
||||||
let event_handler = move |event: Event<()>,
|
let event_handler = move |event: Event<()>,
|
||||||
|
|
Loading…
Reference in a new issue