From d6a7319106e55222e9ad215f27a20bc5ad8b27b1 Mon Sep 17 00:00:00 2001 From: thebluefish Date: Mon, 4 Mar 2024 11:15:05 -0800 Subject: [PATCH] Fix winit control flow when re-focusing game (#12239) # Objective Fixes #12126 Notably this does not appear to fix the console error spam that appears to be coming from winit, only the performance bug that occurs when tabbing out and back into the game. ## Solution The winit event loop starts out as `ControlFlow::Wait` by default. When switching to `UpdateMode::Reactive` or `UpdateMode::ReactiveLowPower`, we repeatedly update this to `ControlFlow::WaitUntil(next)`. When switching back to `UpdateMode::Continuous`, the event loop is erroneously stuck at the latest `ControlFlow::WaitUntil(next)` that was issued. I also changed how we handle `Event::NewEvents` since the `StartCause` already tells us enough information to make that decision. This came about my debugging and I left it in as an improvement. --- crates/bevy_winit/src/lib.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crates/bevy_winit/src/lib.rs b/crates/bevy_winit/src/lib.rs index 784e569b0c..b0ea7533fb 100644 --- a/crates/bevy_winit/src/lib.rs +++ b/crates/bevy_winit/src/lib.rs @@ -14,7 +14,7 @@ mod winit_windows; use approx::relative_eq; use bevy_a11y::AccessibilityRequested; -use bevy_utils::{Duration, Instant}; +use bevy_utils::Instant; use system::{changed_windows, create_windows, despawn_windows, CachedWindow}; use winit::dpi::{LogicalSize, PhysicalSize}; pub use winit_config::*; @@ -46,6 +46,7 @@ use bevy_window::{PrimaryWindow, RawHandleWrapper}; #[cfg(target_os = "android")] pub use winit::platform::android::activity as android_activity; +use winit::event::StartCause; use winit::{ event::{self, DeviceEvent, Event, WindowEvent}, event_loop::{ControlFlow, EventLoop, EventLoopBuilder, EventLoopWindowTarget}, @@ -184,8 +185,6 @@ struct WinitAppRunnerState { wait_elapsed: bool, /// The time the last update started. last_update: Instant, - /// The time the next update is scheduled to start. - scheduled_update: Option, /// Number of "forced" updates to trigger on application start startup_forced_updates: u32, } @@ -226,7 +225,6 @@ impl Default for WinitAppRunnerState { redraw_requested: false, wait_elapsed: false, last_update: Instant::now(), - scheduled_update: None, // 3 seems to be enough, 5 is a safe margin startup_forced_updates: 5, } @@ -411,12 +409,14 @@ fn handle_winit_event( } } } - Event::NewEvents(_) => { - if let Some(t) = runner_state.scheduled_update { - let now = Instant::now(); - let remaining = t.checked_duration_since(now).unwrap_or(Duration::ZERO); - runner_state.wait_elapsed = remaining.is_zero(); - } + Event::NewEvents(cause) => { + runner_state.wait_elapsed = match cause { + StartCause::WaitCancelled { + requested_resume: Some(resume), + .. + } => resume >= Instant::now(), + _ => true, + }; } Event::WindowEvent { event, window_id, .. @@ -762,6 +762,7 @@ fn run_app_update_if_should( match config.update_mode(focused) { UpdateMode::Continuous => { runner_state.redraw_requested = true; + event_loop.set_control_flow(ControlFlow::Wait); } UpdateMode::Reactive { wait } | UpdateMode::ReactiveLowPower { wait } => { // TODO(bug): this is unexpected behavior. @@ -770,10 +771,8 @@ fn run_app_update_if_should( // Need to verify the plateform specifics (whether this can occur in // rare-but-possible cases) and replace this with a panic or a log warn! if let Some(next) = runner_state.last_update.checked_add(*wait) { - runner_state.scheduled_update = Some(next); event_loop.set_control_flow(ControlFlow::WaitUntil(next)); } else { - runner_state.scheduled_update = None; event_loop.set_control_flow(ControlFlow::Wait); } }