Wait until FixedUpdate
can see events before dropping them (#10077)
## Objective Currently, events are dropped after two frames. This cadence wasn't *chosen* for a specific reason, double buffering just lets events persist for at least two frames. Events only need to be dropped at a predictable point so that the event queues don't grow forever (i.e. events should never cause a memory leak). Events (and especially input events) need to be observable by systems in `FixedUpdate`, but as-is events are dropped before those systems even get a chance to see them. ## Solution Instead of unconditionally dropping events in `First`, require `FixedUpdate` to first queue the buffer swap (if the `TimePlugin` has been installed). This way, events are only dropped after a frame that runs `FixedUpdate`. ## Future Work In the same way we have independent copies of `Time` for tracking time in `Main` and `FixedUpdate`, we will need independent copies of `Input` for tracking press/release status correctly in `Main` and `FixedUpdate`. -- Every run of `FixedUpdate` covers a specific timespan. For example, if the fixed timestep `Δt` is 10ms, the first three `FixedUpdate` runs cover `[0ms, 10ms)`, `[10ms, 20ms)`, and `[20ms, 30ms)`. `FixedUpdate` can run many times in one frame. For truly framerate-independent behavior, each `FixedUpdate` should only see the events that occurred in its covered timespan, but what happens right now is the first step in the frame reads all pending events. Fixing that will require timestamped events. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
This commit is contained in:
parent
9cfc48145b
commit
0c199dd74d
@ -190,6 +190,7 @@ impl Default for App {
|
||||
app.init_resource::<AppTypeRegistry>();
|
||||
|
||||
app.add_plugins(MainSchedulePlugin);
|
||||
|
||||
app.add_event::<AppExit>();
|
||||
|
||||
#[cfg(feature = "bevy_ci_testing")]
|
||||
|
@ -749,8 +749,30 @@ impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> {
|
||||
}
|
||||
}
|
||||
|
||||
/// A system that calls [`Events::update`] once per frame.
|
||||
pub fn event_update_system<T: Event>(mut events: ResMut<Events<T>>) {
|
||||
#[doc(hidden)]
|
||||
#[derive(Resource, Default)]
|
||||
pub struct EventUpdateSignal(bool);
|
||||
|
||||
/// A system that queues a call to [`Events::update`].
|
||||
pub fn event_queue_update_system(signal: Option<ResMut<EventUpdateSignal>>) {
|
||||
if let Some(mut s) = signal {
|
||||
s.0 = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// A system that calls [`Events::update`].
|
||||
pub fn event_update_system<T: Event>(
|
||||
signal: Option<ResMut<EventUpdateSignal>>,
|
||||
mut events: ResMut<Events<T>>,
|
||||
) {
|
||||
if let Some(mut s) = signal {
|
||||
// If we haven't got a signal to update the events, but we *could* get such a signal
|
||||
// return early and update the events later.
|
||||
if !std::mem::replace(&mut s.0, false) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
events.update();
|
||||
}
|
||||
|
||||
|
@ -19,11 +19,6 @@ pub use time::*;
|
||||
pub use timer::*;
|
||||
pub use virt::*;
|
||||
|
||||
use bevy_ecs::system::{Res, ResMut};
|
||||
use bevy_utils::{tracing::warn, Duration, Instant};
|
||||
pub use crossbeam_channel::TrySendError;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
|
||||
pub mod prelude {
|
||||
//! The Bevy Time Prelude.
|
||||
#[doc(hidden)]
|
||||
@ -31,7 +26,11 @@ pub mod prelude {
|
||||
}
|
||||
|
||||
use bevy_app::{prelude::*, RunFixedUpdateLoop};
|
||||
use bevy_ecs::event::{event_queue_update_system, EventUpdateSignal};
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_utils::{tracing::warn, Duration, Instant};
|
||||
pub use crossbeam_channel::TrySendError;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
|
||||
/// Adds time functionality to Apps.
|
||||
#[derive(Default)]
|
||||
@ -61,6 +60,10 @@ impl Plugin for TimePlugin {
|
||||
)
|
||||
.add_systems(RunFixedUpdateLoop, run_fixed_update_schedule);
|
||||
|
||||
// ensure the events are not dropped until `FixedUpdate` systems can observe them
|
||||
app.init_resource::<EventUpdateSignal>()
|
||||
.add_systems(FixedUpdate, event_queue_update_system);
|
||||
|
||||
#[cfg(feature = "bevy_ci_testing")]
|
||||
if let Some(ci_testing_config) = app
|
||||
.world
|
||||
|
Loading…
Reference in New Issue
Block a user