From 844524b429ad0abba77c631e22400aebf5397617 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 12:39:50 -0500 Subject: [PATCH 001/113] 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 051795fd566eb..4b077855b46d1 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -112,6 +112,7 @@ mod query; #[allow(clippy::module_inception)] mod system; mod system_param; +mod system_registry; use std::borrow::Cow; @@ -124,6 +125,7 @@ pub use function_system::*; pub use query::*; pub use system::*; pub use system_param::*; +pub use system_registry::*; use crate::world::World; 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 c8ddefda93271e5dcc36cd2a9a28bff722466270 Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 13:11:08 -0500 Subject: [PATCH 002/113] commands.run_system --- crates/bevy_ecs/src/system/commands/mod.rs | 26 ++++++++- crates/bevy_ecs/src/system/system_registry.rs | 56 ++++++++++++++++--- 2 files changed, 73 insertions(+), 9 deletions(-) diff --git a/crates/bevy_ecs/src/system/commands/mod.rs b/crates/bevy_ecs/src/system/commands/mod.rs index 3c404a551c97e..d1a1388a05a60 100644 --- a/crates/bevy_ecs/src/system/commands/mod.rs +++ b/crates/bevy_ecs/src/system/commands/mod.rs @@ -5,7 +5,8 @@ use crate::{ self as bevy_ecs, bundle::Bundle, entity::{Entities, Entity}, - world::{EntityMut, FromWorld, World}, + system::{IntoSystem, RunSystemCommand}, + world::{FromWorld, World}, }; use bevy_ecs_macros::SystemParam; use bevy_utils::tracing::{error, info}; @@ -519,11 +520,32 @@ impl<'w, 's> Commands<'w, 's> { self.queue.push(RemoveResource::::new()); } + /// 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)); + } + /// Pushes a generic [`Command`] to the command queue. /// /// `command` can be a built-in command, custom struct that implements [`Command`] or a closure /// that takes [`&mut World`](World) as an argument. - /// /// # 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 945c81f81d4467956e7f265550d3224ea1ea3d2a Mon Sep 17 00:00:00 2001 From: Alice Cecile Date: Thu, 3 Mar 2022 13:13:04 -0500 Subject: [PATCH 003/113] 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 | 35 +++++++++++++++---- crates/bevy_ecs/src/system/system_registry.rs | 2 +- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/crates/bevy_core/src/lib.rs b/crates/bevy_core/src/lib.rs index 977f01489f286..ae6c8d600badc 100644 --- a/crates/bevy_core/src/lib.rs +++ b/crates/bevy_core/src/lib.rs @@ -21,12 +21,12 @@ pub mod prelude { } use bevy_app::prelude::*; -use bevy_ecs::prelude::*; -use bevy_reflect::{ReflectDeserialize, ReflectSerialize}; -use bevy_utils::{Duration, HashSet, Instant}; -use std::borrow::Cow; -use std::ffi::OsString; -use std::marker::PhantomData; +use bevy_ecs::{ + entity::Entity, + schedule::{ExclusiveSystemDescriptorCoercion, SystemLabel}, + system::{IntoExclusiveSystem, SystemRegistry}, +}; +use bevy_utils::HashSet; use std::ops::Range; use std::path::{Path, PathBuf}; @@ -40,7 +40,28 @@ pub struct TypeRegistrationPlugin; impl Plugin for TypeRegistrationPlugin { fn build(&self, app: &mut App) { - app.register_type::().register_type::(); + // Setup the default bevy task pools + app.world + .get_resource::() + .cloned() + .unwrap_or_default() + .create_default_pools(&mut app.world); + + app.init_resource::