Skip to content

Commit

Permalink
Only run event systems if they have tangible work to do (bevyengine#7728
Browse files Browse the repository at this point in the history
)

# 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 <[email protected]>
  • Loading branch information
james7132 and IceSentry committed Sep 24, 2023
1 parent 22dfa9e commit 8ace2ff
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 11 deletions.
11 changes: 8 additions & 3 deletions crates/bevy_app/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/examples/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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((
Expand Down
18 changes: 12 additions & 6 deletions crates/bevy_ecs/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/schedule/condition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>()),
Expand Down

0 comments on commit 8ace2ff

Please sign in to comment.