From 3e6a8c53ca088891ee97f7f5c9db82f0d8d2b150 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 12:39:50 -0500 Subject: [PATCH 01/66] Basic functionality --- crates/bevy_ecs/src/system/mod.rs | 2 + crates/bevy_ecs/src/system/system_registry.rs | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 crates/bevy_ecs/src/system/system_registry.rs diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 52d8b0a12d6f9..d1cc45b7e940a 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -74,6 +74,7 @@ mod query; mod system; mod system_chaining; mod system_param; +mod system_registry; pub use commands::*; pub use exclusive_system::*; @@ -82,6 +83,7 @@ pub use query::*; pub use system::*; pub use system_chaining::*; pub use system_param::*; +pub use system_registry::*; pub fn assert_is_system>(sys: S) { if false { diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs new file mode 100644 index 0000000000000..c616b51a9aea7 --- /dev/null +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -0,0 +1,111 @@ +use bevy_utils::HashMap; +use std::any::{Any, TypeId}; + +use crate::system::{IntoSystem, System}; +use crate::world::{Mut, World}; + +/// A [`System`] that cannot be chained. +/// +/// [`BoxedSystem`](crate::system::BoxedSystem) is the equivalent type alias for arbitrary `In` and `Out` types. +pub type UnchainedSystem = Box>; + +/// Stores initialized [`Systems`](crate::system::System), so they can quickly be reused and run +/// +/// Stored systems cannot be chained: they can neither have an [`In`](crate::system::In) nor return any values. +#[derive(Default)] +pub struct SystemRegistry { + systems: HashMap, +} + +// User-facing methods +impl SystemRegistry { + /// Runs the supplied system on the [`World`] a single time + /// + /// Any [`Commands`](crate::system::Commands) generated will also be applied to the world immediately. + pub fn run + 'static>( + &mut self, + world: &mut World, + system: S, + ) { + // If the system is already registered and initialized, use it + if let Some(initialized_system) = self.systems.get_mut(&system.type_id()) { + initialized_system.run((), world); + initialized_system.apply_buffers(world); + // Otherwise, register and initialize it first. + } else { + let initialized_system = self.register(world, system); + initialized_system.run((), world); + initialized_system.apply_buffers(world); + } + } + + /// Runs the supplied system on the [`World`] a single time, without flushing [`Commands`](crate::system::Commands) + #[inline] + pub fn run_without_flushing + 'static>( + &mut self, + world: &mut World, + system: S, + ) { + // If the system is already registered and initialized, use it + if let Some(initialized_system) = self.systems.get_mut(&system.type_id()) { + initialized_system.run((), world); + // Otherwise, register and initialize it first. + } else { + let initialized_system = self.register(world, system); + initialized_system.run((), world); + } + } +} + +// Internals +impl SystemRegistry { + #[inline] + fn register + 'static>( + &mut self, + world: &mut World, + system: S, + ) -> &mut UnchainedSystem { + let label = system.type_id(); + + let mut unchained_system: UnchainedSystem = Box::new(IntoSystem::into_system(system)); + unchained_system.initialize(world); + + self.systems.insert(label, unchained_system); + self.systems.get_mut(&label).unwrap() + } +} + +impl World { + /// Runs the supplied system on the [`World`] a single time + /// + /// Any [`Commands`](crate::system::Commands) generated will also be applied to the world immediately. + /// + /// The system's state will be cached: any future calls using the same type will use this state, + /// improving performance and ensuring that change detection works properly. + /// + /// Unsurprisingly, this is evaluated in a sequential, single-threaded fashion. + /// Consider creating and running a [`Schedule`](crate::schedule::Schedule) if you need to execute large groups of systems + /// at once, and want parallel execution of these systems. + pub fn run + 'static>(&mut self, system: S) { + self.resource_scope(|world, mut registry: Mut| { + registry.run(world, system); + }); + } + + /// Runs the supplied system on the [`World`] a single time, wihthout flushing [`Commands`](crate::system::Commands) + /// + /// The system's state will be cached: any future calls using the same type will use this state, + /// improving performance and ensuring that change detection works properly. + /// + /// Unsurprisingly, this is evaluated in a sequential, single-threaded fashion. + /// Consider creating and running a [`Schedule`](crate::schedule::Schedule) if you need to execute large groups of systems + /// at once, and want parallel execution of these systems. + pub fn run_without_flushing + 'static>( + &mut self, + system: S, + ) { + self.resource_scope(|world, mut registry: Mut| { + registry.run_without_flushing(world, system); + }); + } +} From f9b16c23e1ecd15eac6208f3129ad5fb2c308a3c Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 13:11:08 -0500 Subject: [PATCH 02/66] commands.run_system --- crates/bevy_ecs/src/system/commands/mod.rs | 23 ++++++++ crates/bevy_ecs/src/system/system_registry.rs | 56 ++++++++++++++++--- 2 files changed, 72 insertions(+), 7 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 7d3f541dab930..4ecbf91444db8 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -4,6 +4,7 @@ use crate::{ bundle::Bundle, component::Component, entity::{Entities, Entity}, + system::{IntoSystem, RunSystemCommand}, world::{FromWorld, World}, }; use bevy_utils::tracing::{error, warn}; @@ -348,6 +349,28 @@ impl<'w, 's> Commands<'w, 's> { }); } + /// Calls [`World::run_system`] once [`Commands`] are flushed + pub fn run_system< + Params: Send + Sync + 'static, + S: IntoSystem<(), (), Params> + Send + Sync + 'static, + >( + &mut self, + system: S, + ) { + self.queue.push(RunSystemCommand::new(system, true)); + } + + /// Calls [`World::run_system_without_flushing`] once [`Commands`] are flushed + pub fn run_system_without_flushing< + Params: Send + Sync + 'static, + S: IntoSystem<(), (), Params> + Send + Sync + 'static, + >( + &mut self, + system: S, + ) { + self.queue.push(RunSystemCommand::new(system, false)); + } + /// Adds a command directly to the command list. /// /// # Example diff --git a/crates/bevy_ecs/src/system/system_registry.rs b/crates/bevy_ecs/src/system/system_registry.rs index c616b51a9aea7..9c7a23c2a8e37 100644 --- a/crates/bevy_ecs/src/system/system_registry.rs +++ b/crates/bevy_ecs/src/system/system_registry.rs @@ -1,7 +1,8 @@ use bevy_utils::HashMap; use std::any::{Any, TypeId}; +use std::marker::PhantomData; -use crate::system::{IntoSystem, System}; +use crate::system::{Command, IntoSystem, System}; use crate::world::{Mut, World}; /// A [`System`] that cannot be chained. @@ -22,7 +23,7 @@ impl SystemRegistry { /// Runs the supplied system on the [`World`] a single time /// /// Any [`Commands`](crate::system::Commands) generated will also be applied to the world immediately. - pub fn run + 'static>( + pub fn run_system + 'static>( &mut self, world: &mut World, system: S, @@ -41,7 +42,7 @@ impl SystemRegistry { /// Runs the supplied system on the [`World`] a single time, without flushing [`Commands`](crate::system::Commands) #[inline] - pub fn run_without_flushing + 'static>( + pub fn run_system_without_flushing + 'static>( &mut self, world: &mut World, system: S, @@ -86,9 +87,10 @@ impl World { /// Unsurprisingly, this is evaluated in a sequential, single-threaded fashion. /// Consider creating and running a [`Schedule`](crate::schedule::Schedule) if you need to execute large groups of systems /// at once, and want parallel execution of these systems. - pub fn run + 'static>(&mut self, system: S) { + #[inline] + pub fn run_system + 'static>(&mut self, system: S) { self.resource_scope(|world, mut registry: Mut| { - registry.run(world, system); + registry.run_system(world, system); }); } @@ -100,12 +102,52 @@ impl World { /// Unsurprisingly, this is evaluated in a sequential, single-threaded fashion. /// Consider creating and running a [`Schedule`](crate::schedule::Schedule) if you need to execute large groups of systems /// at once, and want parallel execution of these systems. - pub fn run_without_flushing + 'static>( + #[inline] + pub fn run_system_without_flushing + 'static>( &mut self, system: S, ) { self.resource_scope(|world, mut registry: Mut| { - registry.run_without_flushing(world, system); + registry.run_system_without_flushing(world, system); }); } } + +/// The [`Command`] type for [`SystemRegistry`] [`Commands`] +#[derive(Debug, Clone)] +pub struct RunSystemCommand< + Params: Send + Sync + 'static, + S: IntoSystem<(), (), Params> + Send + Sync + 'static, +> { + _phantom_params: PhantomData, + system: S, + flush: bool, +} + +impl + Send + Sync + 'static> + RunSystemCommand +{ + /// Creates a new [`Command`] struct, which can be addeded to [`Commands`] + #[inline] + #[must_use] + pub fn new(system: S, flush: bool) -> Self { + Self { + _phantom_params: PhantomData::default(), + system, + flush, + } + } +} + +impl + Send + Sync + 'static> Command + for RunSystemCommand +{ + #[inline] + fn write(self, world: &mut World) { + if self.flush { + world.run_system(self.system); + } else { + world.run_system_without_flushing(self.system); + } + } +} From 8e32c74433a69cb3e8d0d345266402704100ce85 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 13:13:04 -0500 Subject: [PATCH 03/66] Add SystemRegistry resource as part of CorePlugin No plugin exists for bevy_ecs, so this was the best place. --- crates/bevy_core/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 038abaeb16eb2..c777276f64e07 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -22,7 +22,7 @@ use bevy_app::prelude::*; use bevy_ecs::{ entity::Entity, schedule::{ExclusiveSystemDescriptorCoercion, SystemLabel}, - system::IntoExclusiveSystem, + system::{IntoExclusiveSystem, SystemRegistry}, }; use bevy_utils::HashSet; use std::ops::Range; @@ -50,6 +50,7 @@ impl Plugin for CorePlugin { app.init_resource::