From 65449ea4e2a7b6ace737a13f75217deb08c675ad Mon Sep 17 00:00:00 2001 From: james7132 Date: Thu, 11 Apr 2024 20:52:27 -0700 Subject: [PATCH 01/19] Optimize Event Updates --- crates/bevy_app/src/app.rs | 9 +++-- crates/bevy_app/src/sub_app.rs | 10 ++---- crates/bevy_ecs/src/event.rs | 66 +++++++++++++++++++++++----------- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index a676214d5dc47..642a1882f4fba 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -1,8 +1,10 @@ use crate::{ - Main, MainSchedulePlugin, PlaceholderPlugin, Plugin, Plugins, PluginsState, SubApp, SubApps, + First, Main, MainSchedulePlugin, PlaceholderPlugin, Plugin, Plugins, PluginsState, SubApp, + SubApps, }; pub use bevy_derive::AppLabel; use bevy_ecs::{ + event::event_update_system, intern::Interned, prelude::*, schedule::{ScheduleBuildSettings, ScheduleLabel}, @@ -89,7 +91,10 @@ impl Default for App { #[cfg(feature = "bevy_reflect")] app.init_resource::(); app.add_plugins(MainSchedulePlugin); - + app.add_systems( + First, + event_update_system.in_set(bevy_ecs::event::EventUpdates), + ); app.add_event::(); app diff --git a/crates/bevy_app/src/sub_app.rs b/crates/bevy_app/src/sub_app.rs index b4baa6968925d..c24291a2cc1ae 100644 --- a/crates/bevy_app/src/sub_app.rs +++ b/crates/bevy_app/src/sub_app.rs @@ -1,5 +1,6 @@ -use crate::{App, First, InternedAppLabel, Plugin, Plugins, PluginsState, StateTransition}; +use crate::{App, InternedAppLabel, Plugin, Plugins, PluginsState, StateTransition}; use bevy_ecs::{ + event::EventRegistry, prelude::*, schedule::{ common_conditions::run_once as run_once_condition, run_enter_schedule, @@ -362,12 +363,7 @@ impl SubApp { T: Event, { if !self.world.contains_resource::>() { - self.init_resource::>().add_systems( - First, - bevy_ecs::event::event_update_system:: - .in_set(bevy_ecs::event::EventUpdates) - .run_if(bevy_ecs::event::event_update_condition::), - ); + EventRegistry::register_event::(self.world_mut()); } self diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index af7b6df6b6a27..dcfd141229125 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -1,7 +1,11 @@ //! Event handling types. use crate as bevy_ecs; -use crate::system::{Local, Res, ResMut, Resource, SystemParam}; +use crate::{ + change_detection::Mut, + system::{Local, Res, ResMut, Resource, SystemParam}, + world::World, +}; pub use bevy_ecs_macros::Event; use bevy_ecs_macros::SystemSet; use bevy_utils::detailed_trace; @@ -253,7 +257,8 @@ impl Events { /// /// If you need access to the events that were removed, consider using [`Events::update_drain`]. pub fn update(&mut self) { - let _ = self.update_drain(); + std::mem::swap(&mut self.events_a, &mut self.events_b); + self.events_b.clear(); } /// Swaps the event buffers and drains the oldest event buffer, returning an iterator @@ -798,6 +803,40 @@ impl<'a, E: Event> ExactSizeIterator for EventIteratorWithId<'a, E> { } } +/// A registry of all of the [`Events`] in the [`World`], used by [`event_update_system`] +/// to update all of the events. +#[derive(Resource, Default)] +pub struct EventRegistry { + event_updates: Vec, +} + +impl EventRegistry { + /// Registers an event type to be updated. + pub fn register_event(world: &mut World) { + world.init_resource::>(); + let mut registry = world.get_resource_or_insert_with(|| Self::default()); + registry.event_updates.push(|world| { + if let Some(mut events) = world.get_resource_mut::>() { + events.update(); + } + }); + } + + /// Updates all of the registered events in the World. + pub fn run_updates(&mut self, world: &mut World) { + if let Some(signal) = world.get_resource::() { + // 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 !signal.0 { + return; + } + } + for update in &mut self.event_updates { + update(world) + } + } +} + #[doc(hidden)] #[derive(Resource, Default)] pub struct EventUpdateSignal(bool); @@ -821,25 +860,12 @@ pub fn reset_event_update_signal_system(signal: Option } /// A system that calls [`Events::update`]. -pub fn event_update_system( - update_signal: Option>, - mut events: ResMut>, -) { - if let Some(signal) = update_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 !signal.0 { - return; - } +pub fn event_update_system(world: &mut World) { + if world.contains_resource::() { + world.resource_scope(|world, mut registry: Mut| { + registry.run_updates(world); + }); } - - 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() } /// [`Iterator`] over sent [`EventIds`](`EventId`) from a batch. From dd932841febb345e00db8142f6e651c461c1838a Mon Sep 17 00:00:00 2001 From: james7132 Date: Fri, 12 Apr 2024 01:04:33 -0700 Subject: [PATCH 02/19] Avoid context switches from time systems --- crates/bevy_app/src/app.rs | 6 +++--- crates/bevy_ecs/src/event.rs | 23 +++++++++-------------- crates/bevy_time/src/lib.rs | 23 ++++++++++------------- crates/bevy_time/src/virt.rs | 7 +------ 4 files changed, 23 insertions(+), 36 deletions(-) diff --git a/crates/bevy_app/src/app.rs b/crates/bevy_app/src/app.rs index 642a1882f4fba..65c8f0196cd42 100644 --- a/crates/bevy_app/src/app.rs +++ b/crates/bevy_app/src/app.rs @@ -93,7 +93,9 @@ impl Default for App { app.add_plugins(MainSchedulePlugin); app.add_systems( First, - event_update_system.in_set(bevy_ecs::event::EventUpdates), + event_update_system + .in_set(bevy_ecs::event::EventUpdates) + .run_if(bevy_ecs::event::event_update_condition), ); app.add_event::(); @@ -374,8 +376,6 @@ 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, diff --git a/crates/bevy_ecs/src/event.rs b/crates/bevy_ecs/src/event.rs index dcfd141229125..b6d529a52dc05 100644 --- a/crates/bevy_ecs/src/event.rs +++ b/crates/bevy_ecs/src/event.rs @@ -824,13 +824,6 @@ impl EventRegistry { /// Updates all of the registered events in the World. pub fn run_updates(&mut self, world: &mut World) { - if let Some(signal) = world.get_resource::() { - // 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 !signal.0 { - return; - } - } for update in &mut self.event_updates { update(world) } @@ -852,13 +845,6 @@ pub fn signal_event_update_system(signal: Option>) { } } -/// Resets the `EventUpdateSignal` -pub fn reset_event_update_signal_system(signal: Option>) { - if let Some(mut s) = signal { - s.0 = false; - } -} - /// A system that calls [`Events::update`]. pub fn event_update_system(world: &mut World) { if world.contains_resource::() { @@ -866,6 +852,15 @@ pub fn event_update_system(world: &mut World) { registry.run_updates(world); }); } + if let Some(mut s) = world.get_resource_mut::() { + s.0 = false; + } +} + +pub fn event_update_condition(signal: Option>) -> bool { + // 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. + signal.map(|signal| signal.0).unwrap_or(false) } /// [`Iterator`] over sent [`EventIds`](`EventId`) from a batch. diff --git a/crates/bevy_time/src/lib.rs b/crates/bevy_time/src/lib.rs index 3119f023523e9..2365a9d4f4be7 100644 --- a/crates/bevy_time/src/lib.rs +++ b/crates/bevy_time/src/lib.rs @@ -30,7 +30,7 @@ pub mod prelude { } use bevy_app::{prelude::*, RunFixedMainLoop}; -use bevy_ecs::event::{signal_event_update_system, EventUpdateSignal, EventUpdates}; +use bevy_ecs::event::{signal_event_update_system, EventUpdateSignal}; use bevy_ecs::prelude::*; use bevy_utils::{tracing::warn, Duration, Instant}; pub use crossbeam_channel::TrySendError; @@ -57,18 +57,11 @@ impl Plugin for TimePlugin { .register_type::>() .register_type::>() .register_type::() - .add_systems( - First, - (time_system, virtual_time_system.after(time_system)).in_set(TimeSystem), - ) + .add_systems(First, time_system.in_set(TimeSystem)) .add_systems(RunFixedMainLoop, run_fixed_main_schedule); // ensure the events are not dropped until `FixedMain` systems can observe them app.init_resource::() - .add_systems( - First, - bevy_ecs::event::reset_event_update_signal_system.after(EventUpdates), - ) .add_systems(FixedPostUpdate, signal_event_update_system); } } @@ -111,7 +104,9 @@ pub fn create_time_channels() -> (TimeSender, TimeReceiver) { /// The system used to update the [`Time`] used by app logic. If there is a render world the time is /// sent from there to this system through channels. Otherwise the time is updated in this system. fn time_system( - mut time: ResMut>, + mut real_time: ResMut>, + mut virtual_time: ResMut>, + mut time: ResMut