Fix Reactive and ReactiveLowPower update modes (#11325)

# Objective

- Partial fix for #11235 
- Fixes #11274 
- Fixes #11320 
- Fixes #11273

## Solution

- check update mode to trigger redraw request, instead of once a redraw
request has been triggered
- don't ignore device event in case of `Reactive` update mode
- make sure that at least 5 updates are triggered on application start
to ensure everything is correctly initialized
- trigger manual updates instead of relying on redraw requests when
there are no window or they are not visible
This commit is contained in:
François 2024-01-15 16:46:11 +01:00 committed by GitHub
parent 01139b3472
commit 3d628a8191
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -254,6 +254,8 @@ struct WinitAppRunnerState {
active: ActiveState, active: ActiveState,
/// Is `true` if a new [`WindowEvent`] has been received since the last update. /// Is `true` if a new [`WindowEvent`] has been received since the last update.
window_event_received: bool, window_event_received: bool,
/// Is `true` if a new [`DeviceEvent`] has been received since the last update.
device_event_received: bool,
/// Is `true` if the app has requested a redraw since the last update. /// Is `true` if the app has requested a redraw since the last update.
redraw_requested: bool, redraw_requested: bool,
/// Is `true` if enough time has elapsed since `last_update` to run another update. /// Is `true` if enough time has elapsed since `last_update` to run another update.
@ -262,6 +264,17 @@ struct WinitAppRunnerState {
last_update: Instant, last_update: Instant,
/// The time the next update is scheduled to start. /// The time the next update is scheduled to start.
scheduled_update: Option<Instant>, scheduled_update: Option<Instant>,
/// Number of "forced" updates to trigger on application start
startup_forced_updates: u32,
}
impl WinitAppRunnerState {
fn reset_on_update(&mut self) {
self.redraw_requested = false;
self.window_event_received = false;
self.device_event_received = false;
self.wait_elapsed = false;
}
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@ -287,10 +300,13 @@ impl Default for WinitAppRunnerState {
Self { Self {
active: ActiveState::NotYetStarted, active: ActiveState::NotYetStarted,
window_event_received: false, window_event_received: false,
device_event_received: false,
redraw_requested: false, redraw_requested: false,
wait_elapsed: false, wait_elapsed: false,
last_update: Instant::now(), last_update: Instant::now(),
scheduled_update: None, scheduled_update: None,
// 3 seems to be enough, 5 is a safe margin
startup_forced_updates: 5,
} }
} }
} }
@ -364,14 +380,56 @@ pub fn winit_runner(mut app: App) {
match event { match event {
Event::AboutToWait => { Event::AboutToWait => {
if runner_state.redraw_requested { let (config, windows) = focused_windows_state.get(&app.world);
let focused = windows.iter().any(|window| window.focused);
let mut should_update = match config.update_mode(focused) {
UpdateMode::Continuous => {
runner_state.redraw_requested
|| runner_state.window_event_received
|| runner_state.device_event_received
}
UpdateMode::Reactive { .. } => {
runner_state.wait_elapsed
|| runner_state.redraw_requested
|| runner_state.window_event_received
|| runner_state.device_event_received
}
UpdateMode::ReactiveLowPower { .. } => {
runner_state.wait_elapsed
|| runner_state.redraw_requested
|| runner_state.window_event_received
}
};
// Ensure that an update is triggered on the first iterations for app initialization
if runner_state.startup_forced_updates > 0 {
runner_state.startup_forced_updates -= 1;
should_update = true;
}
if should_update {
let visible = windows.iter().any(|window| window.visible);
let (_, winit_windows, _, _) = let (_, winit_windows, _, _) =
event_writer_system_state.get_mut(&mut app.world); event_writer_system_state.get_mut(&mut app.world);
if visible {
for window in winit_windows.windows.values() { for window in winit_windows.windows.values() {
window.request_redraw(); window.request_redraw();
} }
} else {
// there are no windows, or they are not visible.
// Winit won't send events on some platforms, so trigger an update manually.
run_app_update_if_should(
&mut runner_state,
&mut app,
&mut focused_windows_state,
event_loop,
&mut create_window_system_state,
&mut app_exit_event_reader,
&mut redraw_event_reader,
);
event_loop.set_control_flow(ControlFlow::Poll);
}
} }
runner_state.redraw_requested = false;
} }
Event::NewEvents(_) => { Event::NewEvents(_) => {
if let Some(t) = runner_state.scheduled_update { if let Some(t) = runner_state.scheduled_update {
@ -638,7 +696,6 @@ pub fn winit_runner(mut app: App) {
}); });
} }
WindowEvent::RedrawRequested => { WindowEvent::RedrawRequested => {
runner_state.redraw_requested = false;
run_app_update_if_should( run_app_update_if_should(
&mut runner_state, &mut runner_state,
&mut app, &mut app,
@ -659,15 +716,15 @@ pub fn winit_runner(mut app: App) {
} }
} }
} }
Event::DeviceEvent { Event::DeviceEvent { event, .. } => {
event: DeviceEvent::MouseMotion { delta: (x, y) }, runner_state.device_event_received = true;
.. if let DeviceEvent::MouseMotion { delta: (x, y) } = event {
} => {
let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world); let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world);
event_writers.mouse_motion.send(MouseMotion { event_writers.mouse_motion.send(MouseMotion {
delta: Vec2::new(x as f32, y as f32), delta: Vec2::new(x as f32, y as f32),
}); });
} }
}
Event::Suspended => { Event::Suspended => {
let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world); let (mut event_writers, ..) = event_writer_system_state.get_mut(&mut app.world);
event_writers.lifetime.send(ApplicationLifetime::Suspended); event_writers.lifetime.send(ApplicationLifetime::Suspended);
@ -700,7 +757,7 @@ pub fn winit_runner(mut app: App) {
) = create_window_system_state.get_mut(&mut app.world); ) = create_window_system_state.get_mut(&mut app.world);
create_windows( create_windows(
&event_loop, event_loop,
commands, commands,
windows.iter_mut(), windows.iter_mut(),
event_writer, event_writer,
@ -793,6 +850,8 @@ fn run_app_update_if_should(
app_exit_event_reader: &mut ManualEventReader<AppExit>, app_exit_event_reader: &mut ManualEventReader<AppExit>,
redraw_event_reader: &mut ManualEventReader<RequestRedraw>, redraw_event_reader: &mut ManualEventReader<RequestRedraw>,
) { ) {
runner_state.reset_on_update();
if !runner_state.active.should_run() { if !runner_state.active.should_run() {
return; return;
} }
@ -808,32 +867,15 @@ fn run_app_update_if_should(
event_loop.set_control_flow(ControlFlow::Wait); event_loop.set_control_flow(ControlFlow::Wait);
} }
} }
let (config, windows) = focused_windows_state.get(&app.world);
let focused = windows.iter().any(|window| window.focused);
let should_update = match config.update_mode(focused) {
// `Reactive`: In order for `event_handler` to have been called, either
// we received a window or raw input event, the `wait` elapsed, or a
// redraw was requested (by the app or the OS). There are no other
// conditions, so we can just return `true` here.
UpdateMode::Continuous | UpdateMode::Reactive { .. } => true,
// TODO(bug): This is currently always true since we only run this function
// if we received a `RequestRedraw` event.
UpdateMode::ReactiveLowPower { .. } => {
runner_state.wait_elapsed
|| runner_state.redraw_requested
|| runner_state.window_event_received
}
};
if app.plugins_state() == PluginsState::Cleaned && should_update { if app.plugins_state() == PluginsState::Cleaned {
// reset these on each update
runner_state.wait_elapsed = false;
runner_state.last_update = Instant::now(); runner_state.last_update = Instant::now();
app.update(); app.update();
// decide when to run the next update // decide when to run the next update
let (config, _) = focused_windows_state.get(&app.world); let (config, windows) = focused_windows_state.get(&app.world);
let focused = windows.iter().any(|window| window.focused);
match config.update_mode(focused) { match config.update_mode(focused) {
UpdateMode::Continuous => { UpdateMode::Continuous => {
runner_state.redraw_requested = true; runner_state.redraw_requested = true;