From 8ace2ff9e361dd7ef1bc620b84674def0cb56454 Mon Sep 17 00:00:00 2001 From: James Liu Date: Sat, 23 Sep 2023 17:16:33 -0700 Subject: [PATCH] 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::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 --- crates/bevy_app/src/app.rs | 11 ++++++++--- crates/bevy_ecs/examples/events.rs | 2 +- crates/bevy_ecs/src/event.rs | 18 ++++++++++++------ crates/bevy_ecs/src/schedule/condition.rs | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 566de2686c9ca..195bc71d5dd4e 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -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::`], - /// 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::(); /// ``` + /// + /// [`event_update_system`]: bevy_ecs::event::event_update_system pub fn add_event(&mut self) -> &mut Self where T: Event, { if !self.world.contains_resource::>() { - self.init_resource::>() - .add_systems(First, Events::::update_system); + self.init_resource::>().add_systems( + First, + bevy_ecs::event::event_update_system:: + .run_if(bevy_ecs::event::event_update_condition::), + ); } self } diff --git a/crates/bevy_ecs/examples/events.rs b/crates/bevy_ecs/examples/events.rs index af342f1fca8f0..7be6795880775 100644 --- a/crates/bevy_ecs/examples/events.rs +++ b/crates/bevy_ecs/examples/events.rs @@ -16,7 +16,7 @@ fn main() { #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub struct FlushEvents; - schedule.add_systems(Events::::update_system.in_set(FlushEvents)); + schedule.add_systems(bevy_ecs::event::event_update_system::.in_set(FlushEvents)); // Add systems sending and receiving events after the events are flushed. schedule.add_systems(( diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index 0494555b04e65..781bbde448cd0 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -103,7 +103,7 @@ struct EventInstance { /// 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 Events { iter.map(|e| e.event) } - /// A system that calls [`Events::update`] once per frame. - pub fn update_system(mut events: ResMut) { - 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(mut events: ResMut>) { + events.update(); +} + +/// A run condition that checks if the event's [`event_update_system`] +/// needs to run or not. +pub fn event_update_condition(events: Res>) -> bool { + !events.events_a.is_empty() || !events.events_b.is_empty() +} + #[cfg(test)] mod tests { use crate::system::assert_is_read_only_system; diff --git a/crates/bevy_ecs/src/schedule/condition.rs b/crates/bevy_ecs/src/schedule/condition.rs index 021964311e294..1327d2ea52d17 100644 --- a/crates/bevy_ecs/src/schedule/condition.rs +++ b/crates/bevy_ecs/src/schedule/condition.rs @@ -867,7 +867,7 @@ pub mod common_conditions { /// # let mut world = World::new(); /// # world.init_resource::(); /// # world.init_resource::>(); - /// # app.add_systems(Events::::update_system.before(my_system)); + /// # app.add_systems(bevy_ecs::event::event_update_system::.before(my_system)); /// /// app.add_systems( /// my_system.run_if(on_event::()),