Skip to content

Commit

Permalink
Fix winit control flow when re-focusing game (#12239)
Browse files Browse the repository at this point in the history
# 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.
  • Loading branch information
thebluefish authored and mockersf committed Mar 5, 2024
1 parent db9d84f commit 54f0c55
Showing 1 changed file with 11 additions and 12 deletions.
23 changes: 11 additions & 12 deletions crates/bevy_winit/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,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::*;
Expand Down Expand Up @@ -44,6 +44,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},
Expand Down Expand Up @@ -181,8 +182,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<Instant>,
/// Number of "forced" updates to trigger on application start
startup_forced_updates: u32,
}
Expand Down Expand Up @@ -223,7 +222,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,
}
Expand Down Expand Up @@ -404,12 +402,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, ..
Expand Down Expand Up @@ -744,6 +744,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.
Expand All @@ -752,10 +753,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);
}
}
Expand Down

0 comments on commit 54f0c55

Please sign in to comment.