diff --git a/crates/bevy_app/src/app_builder.rs b/crates/bevy_app/src/app_builder.rs index 6f6e9dd789931..42cdb0f6dcbcb 100644 --- a/crates/bevy_app/src/app_builder.rs +++ b/crates/bevy_app/src/app_builder.rs @@ -5,8 +5,8 @@ use crate::{ stage, startup_stage, PluginGroup, PluginGroupBuilder, }; use bevy_ecs::{ - clear_trackers_system, FromResources, IntoSystem, Resource, Resources, RunOnce, Schedule, - Stage, StateStage, System, SystemStage, World, + clear_trackers_system, AsSystem, FromResources, IntoSystem, Resource, Resources, RunOnce, + Schedule, Stage, StateStage, System, SystemStage, World, }; use bevy_utils::tracing::debug; @@ -125,48 +125,63 @@ impl AppBuilder { self } - pub fn add_system>(&mut self, system: S) -> &mut Self { + pub fn add_system(&mut self, system: S) -> &mut Self + where + S::System: System, + { self.add_system_to_stage(stage::UPDATE, system) } - pub fn on_state_enter>( + pub fn on_state_enter( &mut self, stage: &str, state: T, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { self.stage(stage, |stage: &mut StateStage| { stage.on_state_enter(state, system) }) } - pub fn on_state_update>( + pub fn on_state_update( &mut self, stage: &str, state: T, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { self.stage(stage, |stage: &mut StateStage| { stage.on_state_update(state, system) }) } - pub fn on_state_exit>( + pub fn on_state_exit( &mut self, stage: &str, state: T, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { self.stage(stage, |stage: &mut StateStage| { stage.on_state_exit(state, system) }) } - pub fn add_startup_system_to_stage>( + pub fn add_startup_system_to_stage( &mut self, stage_name: &'static str, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { self.app .schedule .stage(stage::STARTUP, |schedule: &mut Schedule| { @@ -175,7 +190,10 @@ impl AppBuilder { self } - pub fn add_startup_system>(&mut self, system: S) -> &mut Self { + pub fn add_startup_system(&mut self, system: S) -> &mut Self + where + S::System: System, + { self.add_startup_system_to_stage(startup_stage::STARTUP, system) } @@ -197,11 +215,14 @@ impl AppBuilder { .add_stage(stage::LAST, SystemStage::parallel()) } - pub fn add_system_to_stage>( + pub fn add_system_to_stage( &mut self, stage_name: &'static str, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { self.app.schedule.add_system_to_stage(stage_name, system); self } diff --git a/crates/bevy_app/src/event.rs b/crates/bevy_app/src/event.rs index ddc8fd2271eb5..282c47e633c38 100644 --- a/crates/bevy_app/src/event.rs +++ b/crates/bevy_app/src/event.rs @@ -124,7 +124,8 @@ fn map_instance_event(event_instance: &EventInstance) -> &T { /// Reads events of type `T` in order and tracks which events have already been read. #[derive(SystemParam)] pub struct EventReader<'a, T: bevy_ecs::Resource> { - last_event_count: Local<'a, (usize, PhantomData)>, + // Each local is unique, so this won't collide + last_event_count: Local<'a, usize>, events: Res<'a, Events>, } @@ -216,7 +217,7 @@ impl<'a, T: bevy_ecs::Resource> EventReader<'a, T> { /// Like [`iter`](Self::iter), except also returning the [`EventId`] of the events. pub fn iter_with_id(&mut self) -> impl DoubleEndedIterator)> { - internal_event_reader(&mut self.last_event_count.0, &self.events).map(|(event, id)| { + internal_event_reader(&mut self.last_event_count, &self.events).map(|(event, id)| { trace!("EventReader::iter() -> {}", id); (event, id) }) diff --git a/crates/bevy_ecs/macros/src/lib.rs b/crates/bevy_ecs/macros/src/lib.rs index b6984d2a101ba..b3dcea26c4bf8 100644 --- a/crates/bevy_ecs/macros/src/lib.rs +++ b/crates/bevy_ecs/macros/src/lib.rs @@ -314,6 +314,7 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { let query_fn_mut = &query_fn_muts[0..query_count]; tokens.extend(TokenStream::from(quote! { impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: QueryFilter,)*> QueryTuple for (#(Query<#lifetime, #query, #filter>,)*) { + type Fetch = (#( (#query::Fetch, #filter), )*); unsafe fn new(world: &World, component_access: &TypeAccess) -> Self { ( #( @@ -332,13 +333,17 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream { } } + impl<'a, #(#query: Fetch<'a>,)* #(#filter: QueryFilter,)*> QueryTupleFetch<'a> for (#((#query, #filter),)*) { + type Item = (#(Query<'a, <#query as Fetch<'a>>::Query, #filter>,)*); + } + impl<#(#lifetime,)* #(#query: WorldQuery,)* #(#filter: QueryFilter,)*> QuerySet<(#(Query<#lifetime, #query, #filter>,)*)> { #(#query_fn)* #(#query_fn_mut)* } })); } - + // println!("{}", tokens); tokens } @@ -416,19 +421,6 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { .filter(|g| matches!(g, GenericParam::Type(_))) .collect(); - let phantoms = lifetimeless_generics - .iter() - .map(|g| { - let g = match g { - GenericParam::Type(g) => &g.ident, - _ => panic!(), - }; - quote! { ::std::marker::PhantomData::<#g>, } - }) - .fold(quote!(), |old, new| { - quote! { #old #new } - }); - let mut punctuated_generics = Punctuated::<_, Token![,]>::new(); punctuated_generics.extend(lifetimeless_generics.iter()); @@ -439,30 +431,58 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream { })); let struct_name = &ast.ident; - let fetch_struct_name = Ident::new(&format!("Fetch{}", struct_name), Span::call_site()); + let config_struct_name = Ident::new(&format!("{}Config", struct_name), Span::call_site()); + let state_struct_name = Ident::new(&format!("{}State", struct_name), Span::call_site()); + + let output = quote! { + // This construction, as strange as it may seem, + // is to force the config struct to be 'static. + #[allow(non_camel_case_types)] + pub struct #config_struct_name<#(#fields,)*>{ + #(pub #fields: #fields,)* + } + #[allow(non_camel_case_types)] + pub struct #state_struct_name<#(#fields,)* #punctuated_generic_idents>{ + #(#fields: #fields,)* + _phantom: std::marker::PhantomData(#punctuated_generic_idents)> + } - TokenStream::from(quote! { - pub struct #fetch_struct_name<#punctuated_generics>(#phantoms); impl #impl_generics #path::SystemParam for #struct_name#ty_generics #where_clause { - type Fetch = #fetch_struct_name <#punctuated_generic_idents>; - } + type Config = #config_struct_name<#(<#field_types as #path::SystemParam>::Config,)*>; + type State = #state_struct_name<#(<#field_types as #path::SystemParam>::State,)* #punctuated_generic_idents>; + fn default_config() -> Self::Config { + #config_struct_name { + #(#fields: <#field_types as #path::SystemParam>::default_config(),)* + } + } - impl #impl_generics #path::FetchSystemParam<'a> for #fetch_struct_name<#punctuated_generic_idents> { + fn create_state(config: Self::Config) -> Self::State { + #state_struct_name { + #(#fields: <#field_types as #path::SystemParam>::create_state(config.#fields),)* + _phantom: std::marker::PhantomData + } + } + } + #[allow(non_camel_case_types)] + impl <'a, #(#fields: #path::ParamState<'a, Item=#field_types>,)* #punctuated_generics> #path::ParamState<'a> for #state_struct_name<#(#fields,)* #punctuated_generic_idents> { type Item = #struct_name#ty_generics; - fn init(system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) { - #(<<#field_types as SystemParam>::Fetch as #path::FetchSystemParam>::init(system_state, world, resources);)* + fn init(&mut self, system_state: &mut #path::SystemState, world: &#path::World, resources: &mut #path::Resources) { + #(self.#fields.init(system_state, world, resources);)* } unsafe fn get_param( + &'a mut self, system_state: &'a #path::SystemState, world: &'a #path::World, resources: &'a #path::Resources, ) -> Option { Some(#struct_name { - #(#fields: <<#field_types as SystemParam>::Fetch as #path::FetchSystemParam>::get_param(system_state, world, resources)?,)* - #(#ignored_fields: <#ignored_field_types>::default(),)* + #(#fields: self.#fields.get_param(system_state, world, resources)?,)* + #(#ignored_fields: Default::default(),)* }) } } - }) + }; + // println!("{}", output); + TokenStream::from(output) } diff --git a/crates/bevy_ecs/src/core/filter.rs b/crates/bevy_ecs/src/core/filter.rs index 65e01f4d58762..dc8e6ecebc0ec 100644 --- a/crates/bevy_ecs/src/core/filter.rs +++ b/crates/bevy_ecs/src/core/filter.rs @@ -1,7 +1,7 @@ use crate::{core::ComponentFlags, Archetype, Bundle, Component, QueryAccess}; use std::{any::TypeId, marker::PhantomData, ptr::NonNull}; -pub trait QueryFilter: Sized { +pub trait QueryFilter: Sized + 'static { type EntityFilter: EntityFilter; fn access() -> QueryAccess; fn get_entity_filter(archetype: &Archetype) -> Option; @@ -164,7 +164,7 @@ impl QueryFilter for With { pub struct WithType(PhantomData); -impl QueryFilter for WithType { +impl QueryFilter for WithType { type EntityFilter = AnyEntityFilter; fn access() -> QueryAccess { diff --git a/crates/bevy_ecs/src/core/query.rs b/crates/bevy_ecs/src/core/query.rs index d4bac85f9a82a..018e59420a4c5 100644 --- a/crates/bevy_ecs/src/core/query.rs +++ b/crates/bevy_ecs/src/core/query.rs @@ -33,9 +33,10 @@ pub trait WorldQuery { pub unsafe trait ReadOnlyFetch {} /// Streaming iterators over contiguous homogeneous ranges of components -pub trait Fetch<'a>: Sized { +pub trait Fetch<'a>: Sized + 'static { /// Type of value to be fetched type Item; + type Query: WorldQuery; /// A value on which `get` may never be called #[allow(clippy::declare_interior_mutable_const)] // no const fn in traits @@ -70,6 +71,7 @@ impl WorldQuery for Entity { impl<'a> Fetch<'a> for EntityFetch { type Item = Entity; + type Query = Entity; const DANGLING: Self = Self(NonNull::dangling()); @@ -102,6 +104,7 @@ unsafe impl ReadOnlyFetch for FetchRead {} impl<'a, T: Component> Fetch<'a> for FetchRead { type Item = &'a T; + type Query = &'a T; const DANGLING: Self = Self(NonNull::dangling()); @@ -230,6 +233,7 @@ pub struct FetchMut(NonNull, NonNull); impl<'a, T: Component> Fetch<'a> for FetchMut { type Item = Mut<'a, T>; + type Query = &'a mut T; const DANGLING: Self = Self(NonNull::dangling(), NonNull::dangling()); @@ -264,6 +268,7 @@ unsafe impl ReadOnlyFetch for TryFetch where T: ReadOnlyFetch {} impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch { type Item = Option; + type Query = Option; const DANGLING: Self = Self(None); @@ -287,6 +292,7 @@ unsafe impl ReadOnlyFetch for FlagsFetch {} impl<'a, T: Component> Fetch<'a> for FlagsFetch { type Item = Flags; + type Query = Flags; const DANGLING: Self = Self(None, PhantomData::); @@ -521,6 +527,7 @@ macro_rules! tuple_impl { ($($name: ident),*) => { impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) { type Item = ($($name::Item,)*); + type Query = ($($name::Query,)*); const DANGLING: Self = ($($name::DANGLING,)*); #[allow(unused_variables, unused_mut)] diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 70be39c666110..2deeb55866df6 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -13,10 +13,10 @@ pub use system::{Query, *}; pub mod prelude { pub use crate::{ core::WorldBuilderSource, - resource::{ChangedRes, FromResources, Local, Res, ResMut, Resource, Resources}, + resource::{ChangedRes, FromResources, Res, ResMut, Resource, Resources}, schedule::{Schedule, State, StateStage, SystemStage}, - system::{Commands, IntoSystem, Query, System}, - Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mut, Mutated, Or, - QuerySet, Ref, RefMut, With, Without, World, + system::{AsSystem, Commands, IntoSystem, Local, Query, System}, + Added, Bundle, Changed, Component, Entity, Flags, In, IntoChainSystem, Mutated, Or, + QuerySet, With, Without, World, }; } diff --git a/crates/bevy_ecs/src/resource/resource_query.rs b/crates/bevy_ecs/src/resource/resource_query.rs index ef4d560dfd0bb..4996bbd79041b 100644 --- a/crates/bevy_ecs/src/resource/resource_query.rs +++ b/crates/bevy_ecs/src/resource/resource_query.rs @@ -1,5 +1,4 @@ -use super::FromResources; -use crate::{Resource, ResourceIndex, Resources, SystemId}; +use crate::Resource; use std::{ marker::PhantomData, ops::{Deref, DerefMut}, @@ -100,39 +99,6 @@ impl<'a, T: Resource> DerefMut for ResMut<'a, T> { } } -/// Local resources are unique per-system. Two instances of the same system will each have their own resource. -/// Local resources are automatically initialized using the FromResources trait. -#[derive(Debug)] -pub struct Local<'a, T: Resource + FromResources> { - value: *mut T, - _marker: PhantomData<&'a T>, -} - -impl<'a, T: Resource + FromResources> Local<'a, T> { - pub(crate) unsafe fn new(resources: &Resources, id: SystemId) -> Self { - Local { - value: resources - .get_unsafe_ref::(ResourceIndex::System(id)) - .as_ptr(), - _marker: Default::default(), - } - } -} - -impl<'a, T: Resource + FromResources> Deref for Local<'a, T> { - type Target = T; - - fn deref(&self) -> &T { - unsafe { &*self.value } - } -} - -impl<'a, T: Resource + FromResources> DerefMut for Local<'a, T> { - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.value } - } -} - // #[cfg(test)] // mod tests { // use super::*; diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 9c9ada93affa7..f17223abe256c 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -6,7 +6,7 @@ pub use stage::*; pub use stage_executor::*; pub use state::*; -use crate::{BoxedSystem, IntoSystem, Resources, System, World}; +use crate::{AsSystem, BoxedSystem, Resources, System, World}; use bevy_utils::HashMap; #[derive(Default)] @@ -47,11 +47,12 @@ impl Schedule { self } - pub fn set_run_criteria>( - &mut self, - system: S, - ) -> &mut Self { - self.run_criteria = Some(Box::new(system.system())); + pub fn set_run_criteria(&mut self, system: S) -> &mut Self + where + S::System: System, + { + // TODO(before-merge): Get resources here somehow + self.run_criteria = Some(Box::new(system.as_system())); self.run_criteria_initialized = false; self } @@ -99,11 +100,14 @@ impl Schedule { self } - pub fn add_system_to_stage>( + pub fn add_system_to_stage( &mut self, stage_name: &'static str, system: S, - ) -> &mut Self { + ) -> &mut Self + where + S::System: System, + { let stage = self .get_stage_mut::(stage_name) .unwrap_or_else(|| { @@ -112,7 +116,7 @@ impl Schedule { stage_name ) }); - stage.add_system(system.system()); + stage.add_system(system.as_system()); self } diff --git a/crates/bevy_ecs/src/schedule/stage.rs b/crates/bevy_ecs/src/schedule/stage.rs index 62a6761d94a4f..45d5e72028ad4 100644 --- a/crates/bevy_ecs/src/schedule/stage.rs +++ b/crates/bevy_ecs/src/schedule/stage.rs @@ -1,8 +1,8 @@ use std::{any::TypeId, borrow::Cow}; use crate::{ - ArchetypeComponent, BoxedSystem, Resources, System, SystemId, ThreadLocalExecution, TypeAccess, - World, + ArchetypeComponent, AsSystem, BoxedSystem, Resources, System, SystemId, ThreadLocalExecution, + TypeAccess, World, }; use bevy_utils::HashSet; use downcast_rs::{impl_downcast, Downcast}; @@ -59,8 +59,11 @@ impl SystemStage { Self::new(Box::new(ParallelSystemStageExecutor::default())) } - pub fn with_system>(mut self, system: S) -> Self { - self.add_system_boxed(Box::new(system)); + pub fn with_system(mut self, system: S) -> Self + where + S::System: System, + { + self.add_system_boxed(Box::new(system.as_system())); self } @@ -78,8 +81,12 @@ impl SystemStage { self } - pub fn add_system>(&mut self, system: S) -> &mut Self { - self.add_system_boxed(Box::new(system)); + pub fn add_system(&mut self, system: S) -> &mut Self + where + S::System: System, + { + // TODO(before-merge): get the resources to here + self.add_system_boxed(Box::new(system.as_system())); self } diff --git a/crates/bevy_ecs/src/schedule/state.rs b/crates/bevy_ecs/src/schedule/state.rs index a798ebbd4d199..bff371aab80b0 100644 --- a/crates/bevy_ecs/src/schedule/state.rs +++ b/crates/bevy_ecs/src/schedule/state.rs @@ -1,4 +1,4 @@ -use crate::{Resource, Resources, Stage, System, SystemStage, World}; +use crate::{AsSystem, Resource, Resources, Stage, System, SystemStage, World}; use bevy_utils::HashMap; use std::{mem::Discriminant, ops::Deref}; use thiserror::Error; @@ -66,31 +66,28 @@ impl StateStage { self } - pub fn on_state_enter>( - &mut self, - state: T, - system: S, - ) -> &mut Self { + pub fn on_state_enter(&mut self, state: T, system: S) -> &mut Self + where + S::System: System, + { self.enter_stage(state, |system_stage: &mut SystemStage| { system_stage.add_system(system) }) } - pub fn on_state_exit>( - &mut self, - state: T, - system: S, - ) -> &mut Self { + pub fn on_state_exit(&mut self, state: T, system: S) -> &mut Self + where + S::System: System, + { self.exit_stage(state, |system_stage: &mut SystemStage| { system_stage.add_system(system) }) } - pub fn on_state_update>( - &mut self, - state: T, - system: S, - ) -> &mut Self { + pub fn on_state_update(&mut self, state: T, system: S) -> &mut Self + where + S::System: System, + { self.update_stage(state, |system_stage: &mut SystemStage| { system_stage.add_system(system) }) diff --git a/crates/bevy_ecs/src/system/into_system.rs b/crates/bevy_ecs/src/system/into_system.rs index 5027ca05b6f47..c2a6120493f50 100644 --- a/crates/bevy_ecs/src/system/into_system.rs +++ b/crates/bevy_ecs/src/system/into_system.rs @@ -1,22 +1,19 @@ -use super::system_param::FetchSystemParam; -use crate::{ - ArchetypeComponent, Commands, QueryAccess, Resources, System, SystemId, SystemParam, - ThreadLocalExecution, TypeAccess, World, -}; -use parking_lot::Mutex; -use std::{any::TypeId, borrow::Cow, cell::UnsafeCell, sync::Arc}; +//! Create a system from a function + +use std::{any::TypeId, borrow::Cow, cell::UnsafeCell, marker::PhantomData}; + +use crate::{ArchetypeComponent, QueryAccess, Resources, System, SystemId, TypeAccess, World}; + +use super::system_param::{ParamState, SystemParam}; pub struct SystemState { pub(crate) id: SystemId, pub(crate) name: Cow<'static, str>, pub(crate) archetype_component_access: TypeAccess, pub(crate) resource_access: TypeAccess, - pub(crate) local_resource_access: TypeAccess, pub(crate) query_archetype_component_accesses: Vec>, pub(crate) query_accesses: Vec>, pub(crate) query_type_names: Vec<&'static str>, - pub(crate) commands: UnsafeCell, - pub(crate) arc_commands: Option>>, pub(crate) current_query_index: UnsafeCell, } @@ -79,255 +76,312 @@ impl SystemState { } } -pub struct FuncSystem { - func: - Box Option + Send + Sync + 'static>, - thread_local_func: - Box, - init_func: Box, - state: SystemState, +/// A type which can be converted into a system at some point in the future +/// This should only be implemented for functions +/// ```rust +/// use bevy_ecs::{IntoSystem, Local}; +/// fn legal_system(it: Local){} +/// # fn main(){ +/// legal_system.system(); +/// # } +/// ``` +/// This will not compile, since it is trying to get a 'static reference to the value +/// ```compile_fail +/// use bevy_ecs::{IntoSystem, Local}; +/// fn illegal_system(it: Local<'static, u32>){} +/// # fn main(){ +/// illegal_system.system(); +/// # } +/// ``` +// TODO: Seal? +pub trait IntoSystem { + type SystemConfig; + fn system(self) -> Self::SystemConfig; } -impl System for FuncSystem { - type In = (); - type Out = Out; - - fn name(&self) -> std::borrow::Cow<'static, str> { - self.state.name.clone() - } +/// A function which will be turned into a system and the configuration for its state +/// You should call [`FuncSystemPrepare::configure`] to modify the configuration using a builder pattern +pub struct FuncSystemPrepare { + function: Func, + pub config: Params::Config, + input: PhantomData, +} - fn id(&self) -> SystemId { - self.state.id +impl FuncSystemPrepare { + /// Modify the configuration of self using a builder pattern. This allows setting the value for local values. For example: + // TODO: Actually insert that example + /// ``` + /// /* use bevy_ecs::Local; + /// fn my_system(){} */ + /// ``` + pub fn configure(mut self, f: impl FnOnce(&mut

::Config)) -> Self { + f(&mut self.config); + self } +} - fn update(&mut self, world: &World) { - self.state.update(world); - } +/// Convert a type into an actual system. This is not to be confused with [`IntoSystem`], +/// which creates a value which itself implements [`AsSystem`] +/// Bounds on the input type of any method which wishes to accept any system should be expressed in terms of this trait +pub trait AsSystem { + type System: System; + fn as_system(self) -> Self::System; +} - fn archetype_component_access(&self) -> &TypeAccess { - &self.state.archetype_component_access - } +/// Every system can trivially be converted into a System +impl AsSystem for T { + type System = T; - fn resource_access(&self) -> &TypeAccess { - &self.state.resource_access + fn as_system(self) -> Self::System { + self } +} - fn thread_local_execution(&self) -> ThreadLocalExecution { - ThreadLocalExecution::NextFlush - } +/// A system which runs based on the given function +pub struct FuncSystem { + param_state: State, + function: F, + sys_state: SystemState, + input: PhantomData, +} - unsafe fn run_unsafe( - &mut self, - _input: Self::In, - world: &World, - resources: &Resources, - ) -> Option { - (self.func)(&mut self.state, world, resources) +impl AsSystem for FuncSystemPrepare +where + FuncSystem: System, +{ + type System = FuncSystem; + + fn as_system(self) -> Self::System { + FuncSystem { + param_state: Params::create_state(self.config), + function: self.function, + sys_state: SystemState { + name: std::any::type_name::().into(), + archetype_component_access: TypeAccess::default(), + resource_access: TypeAccess::default(), + id: SystemId::new(), + current_query_index: Default::default(), + query_archetype_component_accesses: Vec::new(), + query_accesses: Vec::new(), + query_type_names: Vec::new(), + }, + input: PhantomData, + } } +} - fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { - (self.thread_local_func)(&mut self.state, world, resources) - } +pub struct In(pub In); - fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - (self.init_func)(&mut self.state, world, resources); +impl AsSystem + for FuncSystemPrepare, Params> +where + FuncSystem, Params::State>: System, +{ + type System = FuncSystem, Params::State>; + + fn as_system(self) -> Self::System { + FuncSystem { + param_state: Params::create_state(self.config), + function: self.function, + sys_state: SystemState { + name: std::any::type_name::().into(), + archetype_component_access: TypeAccess::default(), + resource_access: TypeAccess::default(), + id: SystemId::new(), + current_query_index: Default::default(), + query_archetype_component_accesses: Vec::new(), + query_accesses: Vec::new(), + query_type_names: Vec::new(), + }, + input: PhantomData, + } } } -pub struct InputFuncSystem { - func: Box< - dyn FnMut(In, &mut SystemState, &World, &Resources) -> Option + Send + Sync + 'static, - >, - thread_local_func: - Box, - init_func: Box, - state: SystemState, -} +macro_rules! impl_into_system { + ($($param: ident),*) => { + impl IntoSystem, ($($param,)*)> for F + where + F: FnMut(In, $($param,)*) -> Out + + FnMut(In, $(<<$param as SystemParam>::State as ParamState>::Item,)*) -> Out, + { + type SystemConfig = FuncSystemPrepare, ($($param,)*)>; -impl System for InputFuncSystem { - type In = In; - type Out = Out; + fn system(self) -> Self::SystemConfig { + FuncSystemPrepare { + function: self, + config: ($($param::default_config(),)*), + input: PhantomData, + } + } + } - fn name(&self) -> std::borrow::Cow<'static, str> { - self.state.name.clone() - } + impl IntoSystem<(), ($($param,)*)> for F + where + F: FnMut( $($param,)*) -> Out + + FnMut($(<<$param as SystemParam>::State as ParamState>::Item,)*) -> Out, + { + type SystemConfig = FuncSystemPrepare; - fn id(&self) -> SystemId { - self.state.id - } + fn system(self) -> Self::SystemConfig { + FuncSystemPrepare { + function: self, + config: ($($param::default_config(),)*), + input: PhantomData, + } + } + } - fn update(&mut self, world: &World) { - self.state.update(world); - } - fn archetype_component_access(&self) -> &TypeAccess { - &self.state.archetype_component_access - } + #[allow(non_snake_case)] + #[allow(unused_variables)] // For the zero item case + impl ParamState<'a>,)* Out: 'static> System for FuncSystem + where + F: Send + + Sync + + 'static + + FnMut($(<$param as ParamState>::Item,)*) -> Out, + { + type In = (); + type Out = Out; - fn resource_access(&self) -> &TypeAccess { - &self.state.resource_access - } + fn name(&self) -> std::borrow::Cow<'static, str> { + self.sys_state.name.clone() + } - fn thread_local_execution(&self) -> ThreadLocalExecution { - ThreadLocalExecution::NextFlush - } + fn id(&self) -> SystemId { + self.sys_state.id + } - unsafe fn run_unsafe( - &mut self, - input: In, - world: &World, - resources: &Resources, - ) -> Option { - (self.func)(input, &mut self.state, world, resources) - } + fn update(&mut self, world: &crate::World) { + self.sys_state.update(world) + } - fn run_thread_local(&mut self, world: &mut World, resources: &mut Resources) { - (self.thread_local_func)(&mut self.state, world, resources) - } + fn archetype_component_access(&self) -> &crate::TypeAccess { + &self.sys_state.archetype_component_access + } - fn initialize(&mut self, world: &mut World, resources: &mut Resources) { - (self.init_func)(&mut self.state, world, resources); - } -} + fn resource_access(&self) -> &crate::TypeAccess { + &self.sys_state.resource_access + } -pub trait IntoSystem { - fn system(self) -> SystemType; -} + fn thread_local_execution(&self) -> crate::ThreadLocalExecution { + crate::ThreadLocalExecution::NextFlush + } -// Systems implicitly implement IntoSystem -impl IntoSystem<(), Sys> for Sys { - fn system(self) -> Sys { - self - } -} -pub struct In(pub In); + unsafe fn run_unsafe( + &mut self, + _: Self::In, + world: &crate::World, + resources: &Resources, + ) -> Option { + self.sys_state.reset_indices(); + let ($($param,)*) = &mut self.param_state; + Some((self.function)($($param.get_param( + &self.sys_state, + world, + resources, + )?,)*)) + } + + fn run_thread_local(&mut self, world: &mut crate::World, resources: &mut Resources) { + let ($($param,)*) = &mut self.param_state; + $($param.run_sync(world, resources);)* -macro_rules! impl_into_system { - ($($param: ident),*) => { - impl IntoSystem<($($param,)*), FuncSystem> for Func - where - Func: - FnMut($($param),*) -> Out + - FnMut($(<<$param as SystemParam>::Fetch as FetchSystemParam>::Item),*) -> Out + - Send + Sync + 'static, Out: 'static - { - #[allow(unused_variables)] - #[allow(unused_unsafe)] - #[allow(non_snake_case)] - fn system(mut self) -> FuncSystem { - FuncSystem { - state: SystemState { - name: std::any::type_name::().into(), - archetype_component_access: TypeAccess::default(), - resource_access: TypeAccess::default(), - local_resource_access: TypeAccess::default(), - id: SystemId::new(), - commands: Default::default(), - arc_commands: Default::default(), - current_query_index: Default::default(), - query_archetype_component_accesses: Vec::new(), - query_accesses: Vec::new(), - query_type_names: Vec::new(), - }, - func: Box::new(move |state, world, resources| { - state.reset_indices(); - // let mut input = Some(input); - unsafe { - if let Some(($($param,)*)) = <<($($param,)*) as SystemParam>::Fetch as FetchSystemParam>::get_param(state, world, resources) { - Some(self($($param),*)) - } else { - None - } - } - }), - thread_local_func: Box::new(|state, world, resources| { - // SAFE: this is called with unique access to SystemState - unsafe { - (&mut *state.commands.get()).apply(world, resources); - } - if let Some(ref commands) = state.arc_commands { - let mut commands = commands.lock(); - commands.apply(world, resources); - } - }), - init_func: Box::new(|state, world, resources| { - <<($($param,)*) as SystemParam>::Fetch as FetchSystemParam>::init(state, world, resources) - }), - } + } + + fn initialize(&mut self, world: &mut crate::World, resources: &mut Resources) { + // This code can be easily macro generated + let ($($param,)*) = &mut self.param_state; + $($param.init(&mut self.sys_state, world, resources);)* } } - impl IntoSystem<(Input, $($param,)*), InputFuncSystem> for Func + + #[allow(non_snake_case)] + #[allow(unused_variables)] // For the zero item case + impl ParamState<'a>,)* Out: 'static> System + for FuncSystem, ($($param,)*)> where - Func: - FnMut(In, $($param),*) -> Out + - FnMut(In, $(<<$param as SystemParam>::Fetch as FetchSystemParam>::Item),*) -> Out + - Send + Sync + 'static, Input: 'static, Out: 'static + F: Send + + Sync + + 'static + + FnMut(In, $(<$param as ParamState>::Item,)*) -> Out, { - #[allow(unused_variables)] - #[allow(unused_unsafe)] - #[allow(non_snake_case)] - fn system(mut self) -> InputFuncSystem { - InputFuncSystem { - state: SystemState { - name: std::any::type_name::().into(), - archetype_component_access: TypeAccess::default(), - resource_access: TypeAccess::default(), - local_resource_access: TypeAccess::default(), - id: SystemId::new(), - commands: Default::default(), - arc_commands: Default::default(), - current_query_index: Default::default(), - query_archetype_component_accesses: Vec::new(), - query_accesses: Vec::new(), - query_type_names: Vec::new(), - }, - func: Box::new(move |input, state, world, resources| { - state.reset_indices(); - // let mut input = Some(input); - unsafe { - if let Some(($($param,)*)) = <<($($param,)*) as SystemParam>::Fetch as FetchSystemParam>::get_param(state, world, resources) { - Some(self(In(input), $($param),*)) - } else { - None - } - } - }), - thread_local_func: Box::new(|state, world, resources| { - // SAFE: this is called with unique access to SystemState - unsafe { - (&mut *state.commands.get()).apply(world, resources); - } - if let Some(ref commands) = state.arc_commands { - let mut commands = commands.lock(); - commands.apply(world, resources); - } - }), - init_func: Box::new(|state, world, resources| { - <<($($param,)*) as SystemParam>::Fetch as FetchSystemParam>::init(state, world, resources) - }), - } + type In = Input; + type Out = Out; + + fn name(&self) -> std::borrow::Cow<'static, str> { + self.sys_state.name.clone() + } + + fn id(&self) -> SystemId { + self.sys_state.id + } + + fn update(&mut self, world: &crate::World) { + self.sys_state.update(world) + } + + fn archetype_component_access(&self) -> &crate::TypeAccess { + &self.sys_state.archetype_component_access + } + + fn resource_access(&self) -> &crate::TypeAccess { + &self.sys_state.resource_access + } + + fn thread_local_execution(&self) -> crate::ThreadLocalExecution { + crate::ThreadLocalExecution::NextFlush + } + + unsafe fn run_unsafe( + &mut self, + input: Self::In, + world: &crate::World, + resources: &Resources, + ) -> Option { + self.sys_state.reset_indices(); + let ($($param,)*) = &mut self.param_state; + Some((self.function)(In(input), + $($param.get_param( + &self.sys_state, + world, + resources, + )?,)*)) + } + + fn run_thread_local(&mut self, world: &mut crate::World, resources: &mut Resources) { + let ($($param,)*) = &mut self.param_state; + $($param.run_sync(world, resources);)* + + } + + fn initialize(&mut self, world: &mut crate::World, resources: &mut Resources) { + // This code can be easily macro generated + let ($($param,)*) = &mut self.param_state; + $($param.init(&mut self.sys_state, world, resources);)* } } }; } impl_into_system!(); -impl_into_system!(A); -impl_into_system!(A, B); -impl_into_system!(A, B, C); -impl_into_system!(A, B, C, D); -impl_into_system!(A, B, C, D, E); -impl_into_system!(A, B, C, D, E, F); -impl_into_system!(A, B, C, D, E, F, G); -impl_into_system!(A, B, C, D, E, F, G, H); -impl_into_system!(A, B, C, D, E, F, G, H, I); -impl_into_system!(A, B, C, D, E, F, G, H, I, J); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); -impl_into_system!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); +impl_into_system!(T1); +impl_into_system!(T1, T2); +impl_into_system!(T1, T2, T3); +impl_into_system!(T1, T2, T3, T4); +impl_into_system!(T1, T2, T3, T4, T5); +impl_into_system!(T1, T2, T3, T4, T5, T6); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); +impl_into_system!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); #[cfg(test)] mod tests { @@ -336,7 +390,7 @@ mod tests { clear_trackers_system, resource::{Res, ResMut, Resources}, schedule::Schedule, - ChangedRes, Entity, Local, Or, Query, QuerySet, System, SystemStage, With, World, + AsSystem, ChangedRes, Entity, Local, Or, Query, QuerySet, System, SystemStage, With, World, }; #[derive(Debug, Eq, PartialEq, Default)] @@ -572,11 +626,10 @@ mod tests { run_system(&mut world, &mut resources, sys.system()); } - fn run_system>( - world: &mut World, - resources: &mut Resources, - system: S, - ) { + fn run_system(world: &mut World, resources: &mut Resources, system: S) + where + S::System: System, + { let mut schedule = Schedule::default(); let mut update = SystemStage::parallel(); update.add_system(system); @@ -589,13 +642,16 @@ mod tests { _buffer: Vec, } - fn test_for_conflicting_resources>(sys: S) { + fn test_for_conflicting_resources(sys: S) + where + S::System: System, + { let mut world = World::default(); let mut resources = Resources::default(); resources.insert(BufferRes::default()); resources.insert(A); resources.insert(B); - run_system(&mut world, &mut resources, sys.system()); + run_system(&mut world, &mut resources, sys.as_system()); } #[test] @@ -627,13 +683,6 @@ mod tests { test_for_conflicting_resources(sys.system()) } - #[test] - #[should_panic] - fn conflicting_system_local_resources() { - fn sys(_: Local, _: Local) {} - test_for_conflicting_resources(sys.system()) - } - #[test] fn nonconflicting_system_resources() { fn sys(_: Local, _: ResMut, _: Local, _: ResMut) {} diff --git a/crates/bevy_ecs/src/system/into_thread_local.rs b/crates/bevy_ecs/src/system/into_thread_local.rs index d08f33a200d7a..7a3c55bca1647 100644 --- a/crates/bevy_ecs/src/system/into_thread_local.rs +++ b/crates/bevy_ecs/src/system/into_thread_local.rs @@ -56,7 +56,7 @@ impl System for ThreadLocalSystemFn { } } -impl IntoSystem<(&mut World, &mut Resources), ThreadLocalSystemFn> for F +impl IntoSystem<(), F> for F where F: FnMut(&mut World, &mut Resources) + Send + Sync + 'static, { @@ -69,4 +69,6 @@ where archetype_component_access: TypeAccess::default(), } } + + type SystemConfig = ThreadLocalSystemFn; } diff --git a/crates/bevy_ecs/src/system/query/query_set.rs b/crates/bevy_ecs/src/system/query/query_set.rs index 5ee1e1f11fa80..3814368dbdd29 100644 --- a/crates/bevy_ecs/src/system/query/query_set.rs +++ b/crates/bevy_ecs/src/system/query/query_set.rs @@ -10,12 +10,17 @@ pub struct QuerySet { impl_query_set!(); pub trait QueryTuple { + type Fetch: for<'a> QueryTupleFetch<'a>; /// # Safety /// this might cast world and component access to the relevant Self lifetimes. verify that this is safe in each impl unsafe fn new(world: &World, component_access: &TypeAccess) -> Self; fn get_accesses() -> Vec; } +pub trait QueryTupleFetch<'a>: 'static { + type Item: QueryTuple; +} + impl QuerySet { /// # Safety /// This will create a set of Query types that could violate memory safety rules. Make sure that this is only called in diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 0b29e91371e0c..145e7ca6484d5 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -1,372 +1,235 @@ -use crate::{ - ArchetypeComponent, ChangedRes, Commands, Fetch, FromResources, Local, Or, Query, QueryAccess, - QueryFilter, QuerySet, QueryTuple, Res, ResMut, Resource, ResourceIndex, Resources, - SystemState, TypeAccess, World, WorldQuery, -}; -use parking_lot::Mutex; -use std::{any::TypeId, marker::PhantomData, sync::Arc}; -pub trait SystemParam: Sized { - type Fetch: for<'a> FetchSystemParam<'a>; -} - -pub trait FetchSystemParam<'a> { - type Item; - fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources); - /// # Safety - /// This call might access any of the input parameters in an unsafe way. Make sure the data access is safe in - /// the context of the system scheduler - unsafe fn get_param( - system_state: &'a SystemState, - world: &'a World, - resources: &'a Resources, - ) -> Option; -} +use std::ops::{Deref, DerefMut}; -pub struct FetchQuery(PhantomData<(Q, F)>); +use crate::{Or, Resources, SystemState, World}; -impl<'a, Q: WorldQuery, F: QueryFilter> SystemParam for Query<'a, Q, F> { - type Fetch = FetchQuery; -} +mod impls; -impl<'a, Q: WorldQuery, F: QueryFilter> FetchSystemParam<'a> for FetchQuery { - type Item = Query<'a, Q, F>; +/// System parameters which can be in exclusive systems (i.e. those which have `&mut World`, `&mut Resources` arguments) +/// This allows these systems to use local state +pub trait PureSystemParam: Sized { + type PureConfig: 'static; + fn create_state_pure(config: Self::PureConfig) -> Self::PureState; - #[inline] - unsafe fn get_param( - system_state: &'a SystemState, - world: &'a World, - _resources: &'a Resources, - ) -> Option { - let query_index = *system_state.current_query_index.get(); - let archetype_component_access: &'a TypeAccess = - &system_state.query_archetype_component_accesses[query_index]; - *system_state.current_query_index.get() += 1; - Some(Query::new(world, archetype_component_access)) - } - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - system_state - .query_archetype_component_accesses - .push(TypeAccess::default()); - let access = QueryAccess::union(vec![Q::Fetch::access(), F::access()]); - system_state.query_accesses.push(vec![access]); - system_state - .query_type_names - .push(std::any::type_name::()); - } + type PureState: for<'a> PureParamState<'a>; + // For documentation purposes, at some future point + // type Item = >::Item; + fn default_config_pure() -> Self::PureConfig; } -pub struct FetchQuerySet(PhantomData); - -impl SystemParam for QuerySet { - type Fetch = FetchQuerySet; -} - -impl<'a, T: QueryTuple> FetchSystemParam<'a> for FetchQuerySet { - type Item = QuerySet; - - #[inline] - unsafe fn get_param( - system_state: &'a SystemState, - world: &'a World, - _resources: &'a Resources, - ) -> Option { - let query_index = *system_state.current_query_index.get(); - *system_state.current_query_index.get() += 1; - Some(QuerySet::new( - world, - &system_state.query_archetype_component_accesses[query_index], - )) - } - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - system_state - .query_archetype_component_accesses - .push(TypeAccess::default()); - system_state.query_accesses.push(T::get_accesses()); - system_state - .query_type_names - .push(std::any::type_name::()); - } +pub trait PureParamState<'a>: Sized + Send + Sync + 'static { + type Item; + fn view_param(&'a mut self) -> Self::Item; } -pub struct FetchCommands; - -impl<'a> SystemParam for &'a mut Commands { - type Fetch = FetchCommands; +pub trait SystemParam: Sized { + type Config: 'static; + type State: for<'a> ParamState<'a> + 'static; + fn create_state(config: Self::Config) -> Self::State; + fn default_config() -> Self::Config; } -impl<'a> FetchSystemParam<'a> for FetchCommands { - type Item = &'a mut Commands; - fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { - // SAFE: this is called with unique access to SystemState - unsafe { - (&mut *system_state.commands.get()).set_entity_reserver(world.get_entity_reserver()) - } - } +pub trait ParamState<'a>: Sized + Send + Sync + 'static { + type Item; - #[inline] + /// # Safety + /// + /// Init must have been called and the aliasing requirements set up in + /// SystemState must be met unsafe fn get_param( + &'a mut self, system_state: &'a SystemState, - _world: &'a World, - _resources: &'a Resources, - ) -> Option { - Some(&mut *system_state.commands.get()) - } -} - -pub struct FetchArcCommands; -impl SystemParam for Arc> { - type Fetch = FetchArcCommands; -} - -impl<'a> FetchSystemParam<'a> for FetchArcCommands { - type Item = Arc>; - - fn init(system_state: &mut SystemState, world: &World, _resources: &mut Resources) { - system_state.arc_commands.get_or_insert_with(|| { - let mut commands = Commands::default(); - commands.set_entity_reserver(world.get_entity_reserver()); - Arc::new(Mutex::new(commands)) - }); - } - - #[inline] - unsafe fn get_param( - system_state: &SystemState, - _world: &World, - _resources: &Resources, - ) -> Option { - Some(system_state.arc_commands.as_ref().unwrap().clone()) - } + world: &'a World, + resources: &'a Resources, + ) -> Option; + // TODO: Make this significantly cleaner by having methods for resource access and archetype access + // That is, don't store that information in SystemState + fn init(&mut self, system_state: &mut SystemState, world: &World, resources: &mut Resources); + // TODO: investigate `fn requires_sync()->bool{false}` to determine if there are pessimisations resulting from the lack thereof + fn run_sync(&mut self, _world: &mut World, _resources: &mut Resources) {} } -pub struct FetchRes(PhantomData); +// TODO: This impl is too clever - in particular it breaks being able to use tuples of PureSystemParam +// Any `PureSystemParam` can be an 'impure' `SystemParam` +impl SystemParam for T { + type Config = T::PureConfig; -impl<'a, T: Resource> SystemParam for Res<'a, T> { - type Fetch = FetchRes; -} + type State = T::PureState; -impl<'a, T: Resource> FetchSystemParam<'a> for FetchRes { - type Item = Res<'a, T>; - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - if system_state.resource_access.is_write(&TypeId::of::()) { - panic!( - "System `{}` has a `Res<{res}>` parameter that conflicts with \ - another parameter with mutable access to the same `{res}` resource.", - system_state.name, - res = std::any::type_name::() - ); - } - system_state.resource_access.add_read(TypeId::of::()); + fn create_state(config: Self::Config) -> Self::State { + T::create_state_pure(config) } - #[inline] - unsafe fn get_param( - _system_state: &'a SystemState, - _world: &'a World, - resources: &'a Resources, - ) -> Option { - Some(Res::new( - resources.get_unsafe_ref::(ResourceIndex::Global), - )) + fn default_config() -> Self::Config { + T::default_config_pure() } } -pub struct FetchResMut(PhantomData); - -impl<'a, T: Resource> SystemParam for ResMut<'a, T> { - type Fetch = FetchResMut; -} -impl<'a, T: Resource> FetchSystemParam<'a> for FetchResMut { - type Item = ResMut<'a, T>; - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - // If a system already has access to the resource in another parameter, then we fail early. - // e.g. `fn(Res, ResMut)` or `fn(ResMut, ResMut)` must not be allowed. - if system_state - .resource_access - .is_read_or_write(&TypeId::of::()) - { - panic!( - "System `{}` has a `ResMut<{res}>` parameter that conflicts with \ - another parameter to the same `{res}` resource. `ResMut` must have unique access.", - system_state.name, - res = std::any::type_name::() - ); - } - system_state.resource_access.add_write(TypeId::of::()); - } +// Is this impl also too clever? Probably is so +impl<'a, T: PureParamState<'a>> ParamState<'a> for T { + type Item = >::Item; - #[inline] unsafe fn get_param( - _system_state: &'a SystemState, - _world: &'a World, - resources: &'a Resources, + &'a mut self, + _: &'a crate::SystemState, + _: &'a World, + _: &'a Resources, ) -> Option { - let (value, _added, mutated) = - resources.get_unsafe_ref_with_added_and_mutated::(ResourceIndex::Global); - Some(ResMut::new(value, mutated)) + Some(self.view_param()) } -} - -pub struct FetchChangedRes(PhantomData); -impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { - type Fetch = FetchChangedRes; + fn init(&mut self, _: &mut SystemState, _: &World, _: &mut Resources) {} } -impl<'a, T: Resource> FetchSystemParam<'a> for FetchChangedRes { - type Item = ChangedRes<'a, T>; - - fn init(system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { - if system_state.resource_access.is_write(&TypeId::of::()) { - panic!( - "System `{}` has a `ChangedRes<{res}>` parameter that conflicts with \ - another parameter with mutable access to the same `{res}` resource.", - system_state.name, - res = std::any::type_name::() - ); - } - system_state.resource_access.add_read(TypeId::of::()); - } +#[derive(Debug)] +pub struct Local<'a, T>(pub(crate) &'a mut T); - #[inline] - unsafe fn get_param( - _system_state: &'a SystemState, - _world: &'a World, - resources: &'a Resources, - ) -> Option { - let (value, added, mutated) = - resources.get_unsafe_ref_with_added_and_mutated::(ResourceIndex::Global); - if *added.as_ptr() || *mutated.as_ptr() { - Some(ChangedRes::new(value)) - } else { - None - } +impl<'a, T> DerefMut for Local<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } +impl<'a, T> Deref for Local<'a, T> { + type Target = T; -pub struct FetchLocal(PhantomData); - -impl<'a, T: Resource + FromResources> SystemParam for Local<'a, T> { - type Fetch = FetchLocal; -} -impl<'a, T: Resource + FromResources> FetchSystemParam<'a> for FetchLocal { - type Item = Local<'a, T>; - - fn init(system_state: &mut SystemState, _world: &World, resources: &mut Resources) { - if system_state - .local_resource_access - .is_read_or_write(&TypeId::of::()) - { - panic!( - "System `{}` has multiple parameters requesting access to a local resource of type `{}`. \ - There may be at most one `Local` parameter per resource type.", - system_state.name, - std::any::type_name::() - ); - } - - // A resource could have been already initialized by another system with - // `Commands::insert_local_resource` or `Resources::insert_local` - if resources.get_local::(system_state.id).is_none() { - let value = T::from_resources(resources); - resources.insert_local(system_state.id, value); - } - - system_state - .local_resource_access - .add_write(TypeId::of::()); - } - - #[inline] - unsafe fn get_param( - system_state: &'a SystemState, - _world: &'a World, - resources: &'a Resources, - ) -> Option { - Some(Local::new(resources, system_state.id)) + fn deref(&self) -> &Self::Target { + &*self.0 } } -pub struct FetchParamTuple(PhantomData); -pub struct FetchOr(PhantomData); - macro_rules! impl_system_param_tuple { ($($param: ident),*) => { - impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { - type Fetch = FetchParamTuple<($($param::Fetch,)*)>; + #[allow(non_snake_case)] + #[allow(unused_variables)] // Unused in the (,) case + impl<$($param: SystemParam,)*> SystemParam for ($($param,)*) { + type Config = ($($param::Config,)*); + type State = ($($param::State,)*); + + fn create_state(config: Self::Config) -> Self::State { + let ($($param,)*) = config; + ($($param::create_state($param),)*) + } + + fn default_config() -> Self::Config{ + ($($param::default_config(),)*) + } } #[allow(unused_variables)] - impl<'a, $($param: FetchSystemParam<'a>),*> FetchSystemParam<'a> for FetchParamTuple<($($param,)*)> { + #[allow(non_snake_case)] + impl<'a, $($param: ParamState<'a>,)*> ParamState<'a> for ($($param,)*) { type Item = ($($param::Item,)*); - fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { - $($param::init(system_state, world, resources);)* - } - #[inline] unsafe fn get_param( + &'a mut self, system_state: &'a SystemState, world: &'a World, resources: &'a Resources, ) -> Option { - Some(($($param::get_param(system_state, world, resources)?,)*)) + let ($($param,)*) = self; + Some(( + $($param.get_param( + system_state, + world, + resources + )?,)* + )) } - } - impl<$($param: SystemParam),*> SystemParam for Or<($(Option<$param>,)*)> { - type Fetch = FetchOr<($($param::Fetch,)*)>; + fn init( + &mut self, + system_state: &mut SystemState, + world: &World, + resources: &mut Resources, + ) { + let ($($param,)*) = self; + $($param.init(system_state, world, resources);)* + } + + fn run_sync(&mut self, world: &mut World, resources: &mut Resources) { + let ($($param,)*) = self; + $($param.run_sync(world, resources);)* + } } - #[allow(unused_variables)] - #[allow(unused_mut)] #[allow(non_snake_case)] - impl<'a, $($param: FetchSystemParam<'a>),*> FetchSystemParam<'a> for FetchOr<($($param,)*)> { - type Item = Or<($(Option<$param::Item>,)*)>; - fn init(system_state: &mut SystemState, world: &World, resources: &mut Resources) { - $($param::init(system_state, world, resources);)* + #[allow(unused_variables)] + impl<$($param: SystemParam,)*> SystemParam for Or<($(Option<$param>,)*)> { + type Config = ($($param::Config,)*); + type State = Or<($($param::State,)*)>; + + fn create_state(config: Self::Config) -> Self::State { + let ($($param,)*) = config; + Or(($($param::create_state($param),)*)) + } + fn default_config() -> Self::Config{ + ($($param::default_config(),)*) } + } + + #[allow(non_snake_case)] + #[allow(unused_variables)] + impl<'a, $($param: ParamState<'a>,)*> ParamState<'a> for Or<($($param,)*)> { + type Item = Or<($(Option<<$param as ParamState<'a>>::Item>,)*)>; - #[inline] unsafe fn get_param( + &'a mut self, system_state: &'a SystemState, world: &'a World, resources: &'a Resources, ) -> Option { + // Required for the (,) case + #[allow(unused_mut)] let mut has_some = false; + let ($($param,)*) = &mut self.0; + $( - let $param = $param::get_param(system_state, world, resources); + let $param = $param.get_param(system_state, world, resources); if $param.is_some() { has_some = true; } )* + let v = ($($param,)*); if has_some { - Some(Or(($($param,)*))) + Some(Or(v)) } else { None } } + + fn init( + &mut self, + system_state: &mut SystemState, + world: &World, + resources: &mut Resources, + ) { + let ($($param,)*) = &mut self.0; + $($param.init(system_state, world, resources);)* + } + + fn run_sync(&mut self, world: &mut World, resources: &mut Resources) { + let ($($param,)*) = &mut self.0; + $($param.run_sync(world, resources);)* + } } }; } impl_system_param_tuple!(); -impl_system_param_tuple!(A); -impl_system_param_tuple!(A, B); -impl_system_param_tuple!(A, B, C); -impl_system_param_tuple!(A, B, C, D); -impl_system_param_tuple!(A, B, C, D, E); -impl_system_param_tuple!(A, B, C, D, E, F); -impl_system_param_tuple!(A, B, C, D, E, F, G); -impl_system_param_tuple!(A, B, C, D, E, F, G, H); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); -impl_system_param_tuple!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); +impl_system_param_tuple!(T1); +impl_system_param_tuple!(T1, T2); +impl_system_param_tuple!(T1, T2, T3); +impl_system_param_tuple!(T1, T2, T3, T4); +impl_system_param_tuple!(T1, T2, T3, T4, T5); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12); + +// We can't use default because these use more types than tuples implement Default for +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15); +impl_system_param_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16); diff --git a/crates/bevy_ecs/src/system/system_param/impls.rs b/crates/bevy_ecs/src/system/system_param/impls.rs new file mode 100644 index 0000000000000..f5faf30e92a6d --- /dev/null +++ b/crates/bevy_ecs/src/system/system_param/impls.rs @@ -0,0 +1,318 @@ +use std::{any::TypeId, marker::PhantomData, sync::Arc}; + +use parking_lot::Mutex; + +use crate::{ + ArchetypeComponent, ChangedRes, Commands, Fetch, Local, Query, QueryAccess, QueryFilter, + QuerySet, QueryTuple, QueryTupleFetch, Res, ResMut, Resource, ResourceIndex, Resources, + SystemState, TypeAccess, World, WorldQuery, +}; + +use super::{ParamState, PureParamState, PureSystemParam, SystemParam}; + +// TODO NOW: Make Local equivalent to &mut T +pub struct LocalState(T); + +impl<'a, T: Default + 'static + Send + Sync> PureSystemParam for Local<'a, T> { + type PureConfig = Option; + type PureState = LocalState; + + fn create_state_pure(config: Self::PureConfig) -> Self::PureState { + LocalState(config.unwrap_or_else(T::default)) + } + + fn default_config_pure() -> Self::PureConfig { + None + } +} + +impl<'a, T: Send + Sync + 'static> PureParamState<'a> for LocalState { + type Item = Local<'a, T>; + + fn view_param(&'a mut self) -> Self::Item { + Local(&mut self.0) + } +} + +// TODO: Store state here instead of in super::SystemState +pub struct QueryState(PhantomData (Q, F)>); + +impl<'a, Q: WorldQuery, F: QueryFilter> SystemParam for Query<'a, Q, F> { + type Config = (); + + type State = QueryState; + + fn create_state(_: Self::Config) -> Self::State { + QueryState(PhantomData) + } + + fn default_config() {} +} + +// This Fetch lifetime can be anything, but since 'a is available, it'll do +impl<'a, Q: Fetch<'static>, F: QueryFilter> ParamState<'a> for QueryState { + type Item = Query<'a, Q::Query, F>; + + #[inline] + unsafe fn get_param( + &mut self, + system_state: &'a SystemState, + world: &'a World, + _resources: &'a Resources, + ) -> Option { + let query_index = *system_state.current_query_index.get(); + let archetype_component_access: &'a TypeAccess = + &system_state.query_archetype_component_accesses[query_index]; + *system_state.current_query_index.get() += 1; + Some(Query::new(world, archetype_component_access)) + } + + fn init(&mut self, system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + let access = QueryAccess::union(vec![>::access(), F::access()]); + system_state.query_accesses.push(vec![access]); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +pub struct QuerySetState(PhantomData T>); + +impl SystemParam for QuerySet { + type Config = (); + type State = QuerySetState; + + fn create_state(_: Self::Config) -> Self::State { + QuerySetState(PhantomData) + } + + fn default_config() {} +} + +impl<'a, T: QueryTupleFetch<'a>> ParamState<'a> for QuerySetState { + type Item = QuerySet; + + #[inline] + unsafe fn get_param( + &mut self, + system_state: &'a SystemState, + world: &'a World, + _resources: &'a Resources, + ) -> Option { + let query_index = *system_state.current_query_index.get(); + *system_state.current_query_index.get() += 1; + Some(QuerySet::new( + world, + &system_state.query_archetype_component_accesses[query_index], + )) + } + + fn init(&mut self, system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + system_state + .query_archetype_component_accesses + .push(TypeAccess::default()); + system_state.query_accesses.push(T::Item::get_accesses()); + system_state + .query_type_names + .push(std::any::type_name::()); + } +} + +impl<'a> SystemParam for &'a mut Commands { + type Config = (); + type State = Commands; + + fn create_state(_: Self::Config) -> Self::State { + Commands::default() + } + + fn default_config() {} +} +impl<'a> ParamState<'a> for Commands { + type Item = &'a mut Commands; + + fn init(&mut self, _: &mut SystemState, world: &World, _resources: &mut Resources) { + self.set_entity_reserver(world.get_entity_reserver()) + } + + #[inline] + unsafe fn get_param( + &'a mut self, + _: &'a SystemState, + _world: &'a World, + _resources: &'a Resources, + ) -> Option { + Some(self) + } + + fn run_sync(&mut self, world: &mut World, resources: &mut Resources) { + self.apply(world, resources); + } +} + +impl SystemParam for Arc> { + type Config = (); + type State = Arc>; + + fn create_state(_: Self::Config) -> Self::State { + Arc::new(Mutex::new(Commands::default())) + } + + fn default_config() {} +} + +impl<'a> ParamState<'a> for Arc> { + type Item = Arc>; + + fn init(&mut self, _: &mut SystemState, world: &World, _resources: &mut Resources) { + // TODO(DJMcNab): init should be combined into create_state + self.lock().set_entity_reserver(world.get_entity_reserver()); + } + + #[inline] + unsafe fn get_param( + &mut self, + _: &SystemState, + _world: &World, + _resources: &Resources, + ) -> Option { + Some(self.clone()) + } + + fn run_sync(&mut self, world: &mut World, resources: &mut Resources) { + // TODO: try_lock here? + // Don't want to block the entire world on a single missing lock release + self.lock().apply(world, resources); + } +} + +pub struct ResState(PhantomData); + +impl<'a, T: Resource> SystemParam for Res<'a, T> { + type Config = (); + type State = ResState; + fn create_state(_: Self::Config) -> Self::State { + ResState(PhantomData) + } + + fn default_config() {} +} + +impl<'a, T: Resource> ParamState<'a> for ResState { + type Item = Res<'a, T>; + + fn init(&mut self, system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + if system_state.resource_access.is_write(&TypeId::of::()) { + panic!( + "System `{}` has a `Res<{res}>` parameter that conflicts with \ + another parameter with mutable access to the same `{res}` resource.", + system_state.name, + res = std::any::type_name::() + ); + } + system_state.resource_access.add_read(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + &mut self, + _system_state: &'a SystemState, + _world: &'a World, + resources: &'a Resources, + ) -> Option { + Some(Res::new( + resources.get_unsafe_ref::(ResourceIndex::Global), + )) + } +} + +pub struct ResMutState(PhantomData); + +impl<'a, T: Resource> SystemParam for ResMut<'a, T> { + type Config = (); + type State = ResMutState; + fn create_state(_: Self::Config) -> Self::State { + ResMutState(PhantomData) + } + + fn default_config() {} +} + +impl<'a, T: Resource> ParamState<'a> for ResMutState { + type Item = ResMut<'a, T>; + + fn init(&mut self, system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + if system_state + .resource_access + .is_read_or_write(&TypeId::of::()) + { + panic!( + "System `{}` has a `ResMut<{res}>` parameter that conflicts with \ + another parameter to the same `{res}` resource. `ResMut` must have unique access.", + system_state.name, + res = std::any::type_name::() + ); + } + system_state.resource_access.add_write(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + &mut self, + _system_state: &'a SystemState, + _world: &'a World, + resources: &'a Resources, + ) -> Option { + let (value, _added, mutated) = + resources.get_unsafe_ref_with_added_and_mutated::(ResourceIndex::Global); + Some(ResMut::new(value, mutated)) + } +} + +pub struct ChangedResState(PhantomData); + +impl<'a, T: Resource> SystemParam for ChangedRes<'a, T> { + type Config = (); + type State = ChangedResState; + + fn create_state(_: Self::Config) -> Self::State { + ChangedResState(PhantomData) + } + + fn default_config() {} +} + +impl<'a, T: Resource> ParamState<'a> for ChangedResState { + type Item = ChangedRes<'a, T>; + + fn init(&mut self, system_state: &mut SystemState, _world: &World, _resources: &mut Resources) { + if system_state.resource_access.is_write(&TypeId::of::()) { + panic!( + "System `{}` has a `ChangedRes<{res}>` parameter that conflicts with \ + another parameter with mutable access to the same `{res}` resource.", + system_state.name, + res = std::any::type_name::() + ); + } + system_state.resource_access.add_read(TypeId::of::()); + } + + #[inline] + unsafe fn get_param( + &mut self, + _system_state: &'a SystemState, + _world: &'a World, + resources: &'a Resources, + ) -> Option { + let (value, added, mutated) = + resources.get_unsafe_ref_with_added_and_mutated::(ResourceIndex::Global); + if *added.as_ptr() || *mutated.as_ptr() { + Some(ChangedRes::new(value)) + } else { + None + } + } +} diff --git a/crates/bevy_pbr/src/render_graph/lights_node.rs b/crates/bevy_pbr/src/render_graph/lights_node.rs index 94ad213f4fbfc..36de74c549469 100644 --- a/crates/bevy_pbr/src/render_graph/lights_node.rs +++ b/crates/bevy_pbr/src/render_graph/lights_node.rs @@ -4,7 +4,7 @@ use crate::{ }; use bevy_core::{AsBytes, Byteable}; use bevy_ecs::{ - BoxedSystem, Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World, + AsSystem, BoxedSystem, Commands, IntoSystem, Local, Query, Res, ResMut, Resources, World, }; use bevy_render::{ render_graph::{CommandQueue, Node, ResourceSlots, SystemNode}, @@ -53,18 +53,16 @@ struct LightCount { unsafe impl Byteable for LightCount {} impl SystemNode for LightsNode { - fn get_system(&self, commands: &mut Commands) -> BoxedSystem { - let system = lights_node_system.system(); - commands.insert_local_resource( - system.id(), - LightsNodeSystemState { + fn get_system(&self, _: &mut Commands) -> BoxedSystem { + let system = lights_node_system.system().configure(|c| { + c.0 = Some(LightsNodeSystemState { command_queue: self.command_queue.clone(), max_lights: self.max_lights, light_buffer: None, staging_buffer: None, - }, - ); - Box::new(system) + }) + }); + Box::new(system.as_system()) } } diff --git a/crates/bevy_render/src/render_graph/nodes/camera_node.rs b/crates/bevy_render/src/render_graph/nodes/camera_node.rs index 54d119c13a4b4..1fdce68348efc 100644 --- a/crates/bevy_render/src/render_graph/nodes/camera_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/camera_node.rs @@ -9,7 +9,7 @@ use crate::{ use bevy_core::AsBytes; use bevy_ecs::{ - BoxedSystem, Commands, IntoSystem, Local, Query, Res, ResMut, Resources, System, World, + AsSystem, BoxedSystem, Commands, IntoSystem, Local, Query, Res, ResMut, Resources, World, }; use bevy_transform::prelude::*; use std::borrow::Cow; @@ -46,18 +46,16 @@ impl Node for CameraNode { } impl SystemNode for CameraNode { - fn get_system(&self, commands: &mut Commands) -> BoxedSystem { - let system = camera_node_system.system(); - commands.insert_local_resource( - system.id(), - CameraNodeState { + fn get_system(&self, _: &mut Commands) -> BoxedSystem { + let system = camera_node_system.system().configure(|config| { + config.0 = Some(CameraNodeState { camera_name: self.camera_name.clone(), command_queue: self.command_queue.clone(), camera_buffer: None, staging_buffer: None, - }, - ); - Box::new(system) + }) + }); + Box::new(system.as_system()) } } diff --git a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs index 141d8bfc9d757..a09e56ce3e547 100644 --- a/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs +++ b/crates/bevy_render/src/render_graph/nodes/render_resources_node.rs @@ -12,8 +12,8 @@ use crate::{ use bevy_app::EventReader; use bevy_asset::{Asset, AssetEvent, Assets, Handle, HandleId}; use bevy_ecs::{ - BoxedSystem, Changed, Commands, Entity, IntoSystem, Local, Or, Query, QuerySet, Res, ResMut, - Resources, System, With, World, + AsSystem, BoxedSystem, Changed, Commands, Entity, IntoSystem, Local, Or, Query, QuerySet, Res, + ResMut, Resources, With, World, }; use bevy_utils::HashMap; use renderer::{AssetRenderResourceBindings, BufferId, RenderResourceType, RenderResources}; @@ -401,18 +401,17 @@ impl SystemNode for RenderResourcesNode where T: renderer::RenderResources, { - fn get_system(&self, commands: &mut Commands) -> BoxedSystem { - let system = render_resources_node_system::.system(); - commands.insert_local_resource( - system.id(), - RenderResourcesNodeState { - command_queue: self.command_queue.clone(), - uniform_buffer_arrays: UniformBufferArrays::::default(), - dynamic_uniforms: self.dynamic_uniforms, - }, - ); - - Box::new(system) + fn get_system(&self, _: &mut Commands) -> BoxedSystem { + let system = render_resources_node_system:: + .system() + .configure(|config| { + config.0 = Some(RenderResourcesNodeState { + command_queue: self.command_queue.clone(), + uniform_buffer_arrays: UniformBufferArrays::::default(), + dynamic_uniforms: self.dynamic_uniforms, + }) + }); + Box::new(system.as_system()) } } @@ -584,18 +583,17 @@ impl SystemNode for AssetRenderResourcesNode where T: renderer::RenderResources + Asset, { - fn get_system(&self, commands: &mut Commands) -> BoxedSystem { - let system = asset_render_resources_node_system::.system(); - commands.insert_local_resource( - system.id(), - RenderResourcesNodeState { - command_queue: self.command_queue.clone(), - uniform_buffer_arrays: UniformBufferArrays::::default(), - dynamic_uniforms: self.dynamic_uniforms, - }, - ); - - Box::new(system) + fn get_system(&self, _: &mut Commands) -> BoxedSystem { + let system = asset_render_resources_node_system:: + .system() + .configure(|config| { + config.0 = Some(RenderResourcesNodeState { + command_queue: self.command_queue.clone(), + uniform_buffer_arrays: UniformBufferArrays::::default(), + dynamic_uniforms: self.dynamic_uniforms, + }) + }); + Box::new(system.as_system()) } } diff --git a/examples/2d/contributors.rs b/examples/2d/contributors.rs index 821196b9c1c8b..d446b35cec686 100644 --- a/examples/2d/contributors.rs +++ b/examples/2d/contributors.rs @@ -143,8 +143,8 @@ fn setup( fn select_system( mut materials: ResMut>, mut sel: ResMut, - mut dq: Query, With>, - mut tq: Query, With>, + mut dq: Query<&mut Text, With>, + mut tq: Query<&mut Timer, With>, mut q: Query<(&Contributor, &Handle, &mut Transform)>, time: Res