Only run event systems if they have tangible work to do (#7728)

# Objective
Scheduling low cost systems has significant overhead due to task pool
contention and the extra machinery to schedule and run them. Event
update systems are the prime example of a low cost system, requiring a
guaranteed O(1) operation, and there are a *lot* of them.

## Solution
Add a run condition to every event system so they only run when there is
an event in either of it's two internal Vecs.

---

## Changelog
Changed: Event update systems will not run if there are no events to
process.

## Migration Guide
`Events<T>::update_system` has been split off from the the type and can
be found at `bevy_ecs::event::event_update_system`.

---------

Co-authored-by: IceSentry <IceSentry@users.noreply.github.com>
This commit is contained in:
James Liu 2023-09-23 17:16:33 -07:00 committed by GitHub
parent 22dfa9ee96
commit 8ace2ff9e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 22 additions and 11 deletions

View file

@ -424,7 +424,7 @@ impl App {
/// Setup the application to manage events of type `T`.
///
/// This is done by adding a [`Resource`] of type [`Events::<T>`],
/// and inserting an [`update_system`](Events::update_system) into [`First`].
/// and inserting an [`event_update_system`] into [`First`].
///
/// See [`Events`] for defining events.
///
@ -440,13 +440,18 @@ impl App {
/// #
/// app.add_event::<MyEvent>();
/// ```
///
/// [`event_update_system`]: bevy_ecs::event::event_update_system
pub fn add_event<T>(&mut self) -> &mut Self
where
T: Event,
{
if !self.world.contains_resource::<Events<T>>() {
self.init_resource::<Events<T>>()
.add_systems(First, Events::<T>::update_system);
self.init_resource::<Events<T>>().add_systems(
First,
bevy_ecs::event::event_update_system::<T>
.run_if(bevy_ecs::event::event_update_condition::<T>),
);
}
self
}

View file

@ -16,7 +16,7 @@ fn main() {
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
pub struct FlushEvents;
schedule.add_systems(Events::<MyEvent>::update_system.in_set(FlushEvents));
schedule.add_systems(bevy_ecs::event::event_update_system::<MyEvent>.in_set(FlushEvents));
// Add systems sending and receiving events after the events are flushed.
schedule.add_systems((

View file

@ -103,7 +103,7 @@ struct EventInstance<E: Event> {
/// This collection is meant to be paired with a system that calls
/// [`Events::update`] exactly once per update/frame.
///
/// [`Events::update_system`] is a system that does this, typically initialized automatically using
/// [`event_update_system`] is a system that does this, typically initialized automatically using
/// [`add_event`](https://docs.rs/bevy/*/bevy/app/struct.App.html#method.add_event).
/// [`EventReader`]s are expected to read events from this collection at least once per loop/frame.
/// Events will persist across a single frame boundary and so ordering of event producers and
@ -251,11 +251,6 @@ impl<E: Event> Events<E> {
iter.map(|e| e.event)
}
/// A system that calls [`Events::update`] once per frame.
pub fn update_system(mut events: ResMut<Self>) {
events.update();
}
#[inline]
fn reset_start_event_count(&mut self) {
self.events_a.start_event_count = self.event_count;
@ -754,6 +749,17 @@ 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>>) {
events.update();
}
/// A run condition that checks if the event's [`event_update_system`]
/// needs to run or not.
pub fn event_update_condition<T: Event>(events: Res<Events<T>>) -> bool {
!events.events_a.is_empty() || !events.events_b.is_empty()
}
#[cfg(test)]
mod tests {
use crate::system::assert_is_read_only_system;

View file

@ -867,7 +867,7 @@ pub mod common_conditions {
/// # let mut world = World::new();
/// # world.init_resource::<Counter>();
/// # world.init_resource::<Events<MyEvent>>();
/// # app.add_systems(Events::<MyEvent>::update_system.before(my_system));
/// # app.add_systems(bevy_ecs::event::event_update_system::<MyEvent>.before(my_system));
///
/// app.add_systems(
/// my_system.run_if(on_event::<MyEvent>()),