Add new SystemState and rename old SystemState to SystemMeta (#2283)

This enables `SystemParams` to be used outside of function systems. Anything can create and store `SystemState`, which enables efficient "param state cached" access to `SystemParams`.

It adds a `ReadOnlySystemParamFetch` trait, which enables safe `SystemState::get` calls without unique world access.

I renamed the old `SystemState` to `SystemMeta` to enable us to mirror the `QueryState` naming convention (but I'm happy to discuss alternative names if people have other ideas). I initially pitched this as `ParamState`, but given that it needs to include full system metadata, that doesn't feel like a particularly accurate name.

```rust
#[derive(Eq, PartialEq, Debug)]
struct A(usize);

#[derive(Eq, PartialEq, Debug)]
struct B(usize);

let mut world = World::default();
world.insert_resource(A(42));
world.spawn().insert(B(7));

// we get nice lifetime elision when declaring the type on the left hand side
let mut system_state: SystemState<(Res<A>, Query<&B>)> = SystemState::new(&mut world);
let (a, query) = system_state.get(&world);
assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!(
    *query.single().unwrap(),
    B(7),
    "returned component matches initial value"
);

// mutable system params require unique world access
let mut system_state: SystemState<(ResMut<A>, Query<&mut B>)> = SystemState::new(&mut world);
let (a, query) = system_state.get_mut(&mut world);

// static lifetimes are required when declaring inside of structs
struct SomeContainer {
  state: SystemState<(Res<'static, A>, Res<'static, B>)>
}

// this can be shortened using type aliases, which will be useful for complex param tuples
type MyParams<'a> = (Res<'a, A>, Res<'a, B>);
struct SomeContainer {
  state: SystemState<MyParams<'static>>
}

// It is the user's responsibility to call SystemState::apply(world) for parameters that queue up work   
let mut system_state: SystemState<(Commands, Query<&B>)> = SystemState::new(&mut world);
{
  let (mut commands, query) = system_state.get(&world);
  commands.insert_resource(3.14);
}
system_state.apply(&mut world);
```

## Future Work

* Actually use SystemState inside FunctionSystem. This would be trivial, but it requires FunctionSystem to wrap SystemState in Option in its current form (which complicates system metadata lookup). I'd prefer to hold off until we adopt something like the later designs linked in #1364, which enable us to contruct Systems using a World reference (and also remove the need for `.system`).
* Consider a "scoped" approach to automatically call SystemState::apply when systems params are no longer being used (either a container type with a Drop impl, or a function that takes a closure for user logic operating on params).
This commit is contained in:
Carter Anderson 2021-06-02 19:57:38 +00:00
parent f45dbe5bac
commit a20dc36c8c
4 changed files with 418 additions and 136 deletions

View file

@ -221,40 +221,45 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>; type Fetch = QuerySetState<(#(QueryState<#query, #filter>,)*)>;
} }
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemState. If any QueryState conflicts // SAFE: All Queries are constrained to ReadOnlyFetch, so World is only read
unsafe impl<#(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> ReadOnlySystemParamFetch for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#query::Fetch: ReadOnlyFetch,)* #(#filter::Fetch: FilterFetch,)*
{ }
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If any QueryState conflicts
// with any prior access, a panic will occur. // with any prior access, a panic will occur.
unsafe impl<#(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamState for QuerySetState<(#(QueryState<#query, #filter>,)*)> unsafe impl<#(#query: WorldQuery + 'static,)* #(#filter: WorldQuery + 'static,)*> SystemParamState for QuerySetState<(#(QueryState<#query, #filter>,)*)>
where #(#filter::Fetch: FilterFetch,)* where #(#filter::Fetch: FilterFetch,)*
{ {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, config: Self::Config) -> Self {
#( #(
let mut #query = QueryState::<#query, #filter>::new(world); let mut #query = QueryState::<#query, #filter>::new(world);
assert_component_access_compatibility( assert_component_access_compatibility(
&system_state.name, &system_meta.name,
std::any::type_name::<#query>(), std::any::type_name::<#query>(),
std::any::type_name::<#filter>(), std::any::type_name::<#filter>(),
&system_state.component_access_set, &system_meta.component_access_set,
&#query.component_access, &#query.component_access,
world, world,
); );
)* )*
#( #(
system_state system_meta
.component_access_set .component_access_set
.add(#query.component_access.clone()); .add(#query.component_access.clone());
system_state system_meta
.archetype_component_access .archetype_component_access
.extend(&#query.archetype_component_access); .extend(&#query.archetype_component_access);
)* )*
QuerySetState((#(#query,)*)) QuerySetState((#(#query,)*))
} }
fn new_archetype(&mut self, archetype: &Archetype, system_state: &mut SystemState) { fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
let (#(#query,)*) = &mut self.0; let (#(#query,)*) = &mut self.0;
#( #(
#query.new_archetype(archetype); #query.new_archetype(archetype);
system_state system_meta
.archetype_component_access .archetype_component_access
.extend(&#query.archetype_component_access); .extend(&#query.archetype_component_access);
)* )*
@ -271,12 +276,12 @@ pub fn impl_query_set(_input: TokenStream) -> TokenStream {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
let (#(#query,)*) = &state.0; let (#(#query,)*) = &state.0;
QuerySet((#(Query::new(world, #query, system_state.last_change_tick, change_tick),)*)) QuerySet((#(Query::new(world, #query, system_meta.last_change_tick, change_tick),)*))
} }
} }
@ -388,15 +393,15 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
unsafe impl<TSystemParamState: #path::system::SystemParamState, #punctuated_generics> #path::system::SystemParamState for #fetch_struct_name<TSystemParamState, #punctuated_generic_idents> { unsafe impl<TSystemParamState: #path::system::SystemParamState, #punctuated_generics> #path::system::SystemParamState for #fetch_struct_name<TSystemParamState, #punctuated_generic_idents> {
type Config = TSystemParamState::Config; type Config = TSystemParamState::Config;
fn init(world: &mut #path::world::World, system_state: &mut #path::system::SystemState, config: Self::Config) -> Self { fn init(world: &mut #path::world::World, system_meta: &mut #path::system::SystemMeta, config: Self::Config) -> Self {
Self { Self {
state: TSystemParamState::init(world, system_state, config), state: TSystemParamState::init(world, system_meta, config),
marker: std::marker::PhantomData, marker: std::marker::PhantomData,
} }
} }
fn new_archetype(&mut self, archetype: &#path::archetype::Archetype, system_state: &mut #path::system::SystemState) { fn new_archetype(&mut self, archetype: &#path::archetype::Archetype, system_meta: &mut #path::system::SystemMeta) {
self.state.new_archetype(archetype, system_state) self.state.new_archetype(archetype, system_meta)
} }
fn default_config() -> TSystemParamState::Config { fn default_config() -> TSystemParamState::Config {
@ -412,12 +417,12 @@ pub fn derive_system_param(input: TokenStream) -> TokenStream {
type Item = #struct_name#ty_generics; type Item = #struct_name#ty_generics;
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a #path::system::SystemState, system_meta: &#path::system::SystemMeta,
world: &'a #path::world::World, world: &'a #path::world::World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
#struct_name { #struct_name {
#(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_state, world, change_tick),)* #(#fields: <<#field_types as SystemParam>::Fetch as #path::system::SystemParamFetch>::get_param(&mut state.state.#field_indices, system_meta, world, change_tick),)*
#(#ignored_fields: <#ignored_field_types>::default(),)* #(#ignored_fields: <#ignored_field_types>::default(),)*
} }
} }

View file

@ -1,28 +1,29 @@
use crate::{ use crate::{
archetype::{Archetype, ArchetypeComponentId}, archetype::{Archetype, ArchetypeComponentId, ArchetypeGeneration, ArchetypeId},
component::ComponentId, component::ComponentId,
query::{Access, FilteredAccessSet}, query::{Access, FilteredAccessSet},
system::{ system::{
check_system_change_tick, System, SystemId, SystemParam, SystemParamFetch, SystemParamState, check_system_change_tick, ReadOnlySystemParamFetch, System, SystemId, SystemParam,
SystemParamFetch, SystemParamState,
}, },
world::World, world::{World, WorldId},
}; };
use bevy_ecs_macros::all_tuples; use bevy_ecs_macros::all_tuples;
use std::{borrow::Cow, marker::PhantomData}; use std::{borrow::Cow, marker::PhantomData};
/// The state of a [`System`]. /// The metadata of a [`System`].
pub struct SystemState { pub struct SystemMeta {
pub(crate) id: SystemId, pub(crate) id: SystemId,
pub(crate) name: Cow<'static, str>, pub(crate) name: Cow<'static, str>,
pub(crate) component_access_set: FilteredAccessSet<ComponentId>, pub(crate) component_access_set: FilteredAccessSet<ComponentId>,
pub(crate) archetype_component_access: Access<ArchetypeComponentId>, pub(crate) archetype_component_access: Access<ArchetypeComponentId>,
// NOTE: this must be kept private. making a SystemState non-send is irreversible to prevent // NOTE: this must be kept private. making a SystemMeta non-send is irreversible to prevent
// SystemParams from overriding each other // SystemParams from overriding each other
is_send: bool, is_send: bool,
pub(crate) last_change_tick: u32, pub(crate) last_change_tick: u32,
} }
impl SystemState { impl SystemMeta {
fn new<T>() -> Self { fn new<T>() -> Self {
Self { Self {
name: std::any::type_name::<T>().into(), name: std::any::type_name::<T>().into(),
@ -49,6 +50,114 @@ impl SystemState {
} }
} }
// TODO: Actually use this in FunctionSystem. We should probably only do this once Systems are constructed using a World reference
// (to avoid the need for unwrapping to retrieve SystemMeta)
/// Holds on to persistent state required to drive [`SystemParam`] for a [`System`].
pub struct SystemState<Param: SystemParam> {
meta: SystemMeta,
param_state: <Param as SystemParam>::Fetch,
world_id: WorldId,
archetype_generation: ArchetypeGeneration,
}
impl<Param: SystemParam> SystemState<Param> {
pub fn new(world: &mut World) -> Self {
let config = <Param::Fetch as SystemParamState>::default_config();
Self::with_config(world, config)
}
pub fn with_config(
world: &mut World,
config: <Param::Fetch as SystemParamState>::Config,
) -> Self {
let mut meta = SystemMeta::new::<Param>();
let param_state = <Param::Fetch as SystemParamState>::init(world, &mut meta, config);
Self {
meta,
param_state,
world_id: world.id(),
archetype_generation: ArchetypeGeneration::initial(),
}
}
#[inline]
pub fn meta(&self) -> &SystemMeta {
&self.meta
}
/// Retrieve the [`SystemParam`] values. This can only be called when all parameters are read-only.
#[inline]
pub fn get<'a>(&'a mut self, world: &'a World) -> <Param::Fetch as SystemParamFetch<'a>>::Item
where
Param::Fetch: ReadOnlySystemParamFetch,
{
self.validate_world_and_update_archetypes(world);
// SAFE: Param is read-only and doesn't allow mutable access to World. It also matches the World this SystemState was created with.
unsafe { self.get_unchecked_manual(world) }
}
/// Retrieve the mutable [`SystemParam`] values.
#[inline]
pub fn get_mut<'a>(
&'a mut self,
world: &'a mut World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
self.validate_world_and_update_archetypes(world);
// SAFE: World is uniquely borrowed and matches the World this SystemState was created with.
unsafe { self.get_unchecked_manual(world) }
}
/// Applies all state queued up for [`SystemParam`] values. For example, this will apply commands queued up
/// by a [`Commands`](`super::Commands`) parameter to the given [`World`].
/// This function should be called manually after the values returned by [`SystemState::get`] and [`SystemState::get_mut`]
/// are finished being used.
pub fn apply(&mut self, world: &mut World) {
self.param_state.apply(world);
}
#[inline]
pub fn matches_world(&self, world: &World) -> bool {
self.world_id == world.id()
}
fn validate_world_and_update_archetypes(&mut self, world: &World) {
assert!(self.matches_world(world), "Encountered a mismatched World. A SystemState cannot be used with Worlds other than the one it was created with.");
let archetypes = world.archetypes();
let new_generation = archetypes.generation();
let old_generation = std::mem::replace(&mut self.archetype_generation, new_generation);
let archetype_index_range = old_generation.value()..new_generation.value();
for archetype_index in archetype_index_range {
self.param_state.new_archetype(
&archetypes[ArchetypeId::new(archetype_index)],
&mut self.meta,
);
}
}
/// Retrieve the [`SystemParam`] values. This will not update archetypes automatically.
///
/// # Safety
/// This call might access any of the input parameters in a way that violates Rust's mutability rules. Make sure the data
/// access is safe in the context of global [`World`] access. The passed-in [`World`] _must_ be the [`World`] the [`SystemState`] was
/// created with.
#[inline]
pub unsafe fn get_unchecked_manual<'a>(
&'a mut self,
world: &'a World,
) -> <Param::Fetch as SystemParamFetch<'a>>::Item {
let change_tick = world.increment_change_tick();
let param = <Param::Fetch as SystemParamFetch>::get_param(
&mut self.param_state,
&self.meta,
world,
change_tick,
);
self.meta.last_change_tick = change_tick;
param
}
}
/// Conversion trait to turn something into a [`System`]. /// Conversion trait to turn something into a [`System`].
/// ///
/// Use this to get a system from a function. Also note that every system implements this trait as /// Use this to get a system from a function. Also note that every system implements this trait as
@ -116,7 +225,7 @@ where
{ {
func: F, func: F,
param_state: Option<Param::Fetch>, param_state: Option<Param::Fetch>,
system_state: SystemState, system_meta: SystemMeta,
config: Option<<Param::Fetch as SystemParamState>::Config>, config: Option<<Param::Fetch as SystemParamState>::Config>,
// NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls // NOTE: PhantomData<fn()-> T> gives this safe Send/Sync impls
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
@ -161,7 +270,7 @@ where
func: self, func: self,
param_state: None, param_state: None,
config: Some(<Param::Fetch as SystemParamState>::default_config()), config: Some(<Param::Fetch as SystemParamState>::default_config()),
system_state: SystemState::new::<F>(), system_meta: SystemMeta::new::<F>(),
marker: PhantomData, marker: PhantomData,
} }
} }
@ -180,33 +289,33 @@ where
#[inline] #[inline]
fn name(&self) -> Cow<'static, str> { fn name(&self) -> Cow<'static, str> {
self.system_state.name.clone() self.system_meta.name.clone()
} }
#[inline] #[inline]
fn id(&self) -> SystemId { fn id(&self) -> SystemId {
self.system_state.id self.system_meta.id
} }
#[inline] #[inline]
fn new_archetype(&mut self, archetype: &Archetype) { fn new_archetype(&mut self, archetype: &Archetype) {
let param_state = self.param_state.as_mut().unwrap(); let param_state = self.param_state.as_mut().unwrap();
param_state.new_archetype(archetype, &mut self.system_state); param_state.new_archetype(archetype, &mut self.system_meta);
} }
#[inline] #[inline]
fn component_access(&self) -> &Access<ComponentId> { fn component_access(&self) -> &Access<ComponentId> {
&self.system_state.component_access_set.combined_access() &self.system_meta.component_access_set.combined_access()
} }
#[inline] #[inline]
fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> { fn archetype_component_access(&self) -> &Access<ArchetypeComponentId> {
&self.system_state.archetype_component_access &self.system_meta.archetype_component_access
} }
#[inline] #[inline]
fn is_send(&self) -> bool { fn is_send(&self) -> bool {
self.system_state.is_send self.system_meta.is_send
} }
#[inline] #[inline]
@ -215,11 +324,11 @@ where
let out = self.func.run( let out = self.func.run(
input, input,
self.param_state.as_mut().unwrap(), self.param_state.as_mut().unwrap(),
&self.system_state, &self.system_meta,
world, world,
change_tick, change_tick,
); );
self.system_state.last_change_tick = change_tick; self.system_meta.last_change_tick = change_tick;
out out
} }
@ -233,7 +342,7 @@ where
fn initialize(&mut self, world: &mut World) { fn initialize(&mut self, world: &mut World) {
self.param_state = Some(<Param::Fetch as SystemParamState>::init( self.param_state = Some(<Param::Fetch as SystemParamState>::init(
world, world,
&mut self.system_state, &mut self.system_meta,
self.config.take().unwrap(), self.config.take().unwrap(),
)); ));
} }
@ -241,20 +350,24 @@ where
#[inline] #[inline]
fn check_change_tick(&mut self, change_tick: u32) { fn check_change_tick(&mut self, change_tick: u32) {
check_system_change_tick( check_system_change_tick(
&mut self.system_state.last_change_tick, &mut self.system_meta.last_change_tick,
change_tick, change_tick,
self.system_state.name.as_ref(), self.system_meta.name.as_ref(),
); );
} }
} }
/// A trait implemented for all functions that can be used as [`System`]s. /// A trait implemented for all functions that can be used as [`System`]s.
pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static { pub trait SystemParamFunction<In, Out, Param: SystemParam, Marker>: Send + Sync + 'static {
fn run( /// # 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 run(
&mut self, &mut self,
input: In, input: In,
state: &mut Param::Fetch, state: &mut Param::Fetch,
system_state: &SystemState, system_meta: &SystemMeta,
world: &World, world: &World,
change_tick: u32, change_tick: u32,
) -> Out; ) -> Out;
@ -270,13 +383,11 @@ macro_rules! impl_system_function {
FnMut($(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static FnMut($(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static
{ {
#[inline] #[inline]
fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out { unsafe fn run(&mut self, _input: (), state: &mut <($($param,)*) as SystemParam>::Fetch, system_meta: &SystemMeta, world: &World, change_tick: u32) -> Out {
unsafe { let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_meta, world, change_tick);
let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick);
self($($param),*) self($($param),*)
} }
} }
}
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl<Input, Out, Func, $($param: SystemParam),*> SystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func impl<Input, Out, Func, $($param: SystemParam),*> SystemParamFunction<Input, Out, ($($param,)*), InputMarker> for Func
@ -286,13 +397,11 @@ macro_rules! impl_system_function {
FnMut(In<Input>, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static FnMut(In<Input>, $(<<$param as SystemParam>::Fetch as SystemParamFetch>::Item),*) -> Out + Send + Sync + 'static, Out: 'static
{ {
#[inline] #[inline]
fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_state: &SystemState, world: &World, change_tick: u32) -> Out { unsafe fn run(&mut self, input: Input, state: &mut <($($param,)*) as SystemParam>::Fetch, system_meta: &SystemMeta, world: &World, change_tick: u32) -> Out {
unsafe { let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_meta, world, change_tick);
let ($($param,)*) = <<($($param,)*) as SystemParam>::Fetch as SystemParamFetch>::get_param(state, system_state, world, change_tick);
self(In(input), $($param),*) self(In(input), $($param),*)
} }
} }
}
}; };
} }

View file

@ -1,6 +1,6 @@
mod commands; mod commands;
mod exclusive_system; mod exclusive_system;
mod into_system; mod function_system;
mod query; mod query;
#[allow(clippy::module_inception)] #[allow(clippy::module_inception)]
mod system; mod system;
@ -9,7 +9,7 @@ mod system_param;
pub use commands::*; pub use commands::*;
pub use exclusive_system::*; pub use exclusive_system::*;
pub use into_system::*; pub use function_system::*;
pub use query::*; pub use query::*;
pub use system::*; pub use system::*;
pub use system_chaining::*; pub use system_chaining::*;
@ -28,7 +28,7 @@ mod tests {
schedule::{Schedule, Stage, SystemStage}, schedule::{Schedule, Stage, SystemStage},
system::{ system::{
IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res, IntoExclusiveSystem, IntoSystem, Local, Query, QuerySet, RemovedComponents, Res,
ResMut, System, ResMut, System, SystemState,
}, },
world::{FromWorld, World}, world::{FromWorld, World},
}; };
@ -485,4 +485,121 @@ mod tests {
x.initialize(&mut world); x.initialize(&mut world);
y.initialize(&mut world); y.initialize(&mut world);
} }
#[test]
fn read_system_state() {
#[derive(Eq, PartialEq, Debug)]
struct A(usize);
#[derive(Eq, PartialEq, Debug)]
struct B(usize);
let mut world = World::default();
world.insert_resource(A(42));
world.spawn().insert(B(7));
let mut system_state: SystemState<(Res<A>, Query<&B>, QuerySet<(Query<&C>, Query<&D>)>)> =
SystemState::new(&mut world);
let (a, query, _) = system_state.get(&world);
assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!(
*query.single().unwrap(),
B(7),
"returned component matches initial value"
);
}
#[test]
fn write_system_state() {
#[derive(Eq, PartialEq, Debug)]
struct A(usize);
#[derive(Eq, PartialEq, Debug)]
struct B(usize);
let mut world = World::default();
world.insert_resource(A(42));
world.spawn().insert(B(7));
let mut system_state: SystemState<(ResMut<A>, Query<&mut B>)> =
SystemState::new(&mut world);
// The following line shouldn't compile because the parameters used are not ReadOnlySystemParam
// let (a, query) = system_state.get(&world);
let (a, mut query) = system_state.get_mut(&mut world);
assert_eq!(*a, A(42), "returned resource matches initial value");
assert_eq!(
*query.single_mut().unwrap(),
B(7),
"returned component matches initial value"
);
}
#[test]
fn system_state_change_detection() {
#[derive(Eq, PartialEq, Debug)]
struct A(usize);
let mut world = World::default();
let entity = world.spawn().insert(A(1)).id();
let mut system_state: SystemState<Query<&A, Changed<A>>> = SystemState::new(&mut world);
{
let query = system_state.get(&world);
assert_eq!(*query.single().unwrap(), A(1));
}
{
let query = system_state.get(&world);
assert!(query.single().is_err());
}
world.entity_mut(entity).get_mut::<A>().unwrap().0 = 2;
{
let query = system_state.get(&world);
assert_eq!(*query.single().unwrap(), A(2));
}
}
#[test]
#[should_panic]
fn system_state_invalid_world() {
let mut world = World::default();
let mut system_state = SystemState::<Query<&A>>::new(&mut world);
let mismatched_world = World::default();
system_state.get(&mismatched_world);
}
#[test]
fn system_state_archetype_update() {
#[derive(Eq, PartialEq, Debug)]
struct A(usize);
#[derive(Eq, PartialEq, Debug)]
struct B(usize);
let mut world = World::default();
world.spawn().insert(A(1));
let mut system_state = SystemState::<Query<&A>>::new(&mut world);
{
let query = system_state.get(&world);
assert_eq!(
query.iter().collect::<Vec<_>>(),
vec![&A(1)],
"exactly one component returned"
);
}
world.spawn().insert_bundle((A(2), B(2)));
{
let query = system_state.get(&world);
assert_eq!(
query.iter().collect::<Vec<_>>(),
vec![&A(1), &A(2)],
"components from both archetypes returned"
);
}
}
} }

View file

@ -5,8 +5,10 @@ use crate::{
change_detection::Ticks, change_detection::Ticks,
component::{Component, ComponentId, ComponentTicks, Components}, component::{Component, ComponentId, ComponentTicks, Components},
entity::{Entities, Entity}, entity::{Entities, Entity},
query::{FilterFetch, FilteredAccess, FilteredAccessSet, QueryState, WorldQuery}, query::{
system::{CommandQueue, Commands, Query, SystemState}, FilterFetch, FilteredAccess, FilteredAccessSet, QueryState, ReadOnlyFetch, WorldQuery,
},
system::{CommandQueue, Commands, Query, SystemMeta},
world::{FromWorld, World}, world::{FromWorld, World},
}; };
pub use bevy_ecs_macros::SystemParam; pub use bevy_ecs_macros::SystemParam;
@ -46,7 +48,7 @@ pub trait SystemParam: Sized {
/// ///
/// # Safety /// # Safety
/// ///
/// It is the implementor's responsibility to ensure `system_state` is populated with the _exact_ /// It is the implementor's responsibility to ensure `system_meta` is populated with the _exact_
/// [`World`] access used by the `SystemParamState` (and associated [`SystemParamFetch`]). /// [`World`] access used by the `SystemParamState` (and associated [`SystemParamFetch`]).
/// Additionally, it is the implementor's responsibility to ensure there is no /// Additionally, it is the implementor's responsibility to ensure there is no
/// conflicting access across all SystemParams. /// conflicting access across all SystemParams.
@ -64,14 +66,20 @@ pub unsafe trait SystemParamState: Send + Sync + 'static {
/// See [`FunctionSystem::config`](super::FunctionSystem::config) /// See [`FunctionSystem::config`](super::FunctionSystem::config)
/// for more information and examples. /// for more information and examples.
type Config: Send + Sync; type Config: Send + Sync;
fn init(world: &mut World, system_state: &mut SystemState, config: Self::Config) -> Self; fn init(world: &mut World, system_meta: &mut SystemMeta, config: Self::Config) -> Self;
#[inline] #[inline]
fn new_archetype(&mut self, _archetype: &Archetype, _system_state: &mut SystemState) {} fn new_archetype(&mut self, _archetype: &Archetype, _system_meta: &mut SystemMeta) {}
#[inline] #[inline]
fn apply(&mut self, _world: &mut World) {} fn apply(&mut self, _world: &mut World) {}
fn default_config() -> Self::Config; fn default_config() -> Self::Config;
} }
/// A [`SystemParamFetch`] that only reads a given [`World`].
///
/// # Safety
/// This must only be implemented for [`SystemParamFetch`] impls that exclusively read the World passed in to [`SystemParamFetch::get_param`]
pub unsafe trait ReadOnlySystemParamFetch {}
pub trait SystemParamFetch<'a>: SystemParamState { pub trait SystemParamFetch<'a>: SystemParamState {
type Item; type Item;
/// # Safety /// # Safety
@ -80,14 +88,12 @@ pub trait SystemParamFetch<'a>: SystemParamState {
/// access is safe in the context of the system scheduler. /// access is safe in the context of the system scheduler.
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item; ) -> Self::Item;
} }
pub struct QueryFetch<Q, F>(PhantomData<(Q, F)>);
impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'a, Q, F> impl<'a, Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParam for Query<'a, Q, F>
where where
F::Fetch: FilterFetch, F::Fetch: FilterFetch,
@ -95,7 +101,15 @@ where
type Fetch = QueryState<Q, F>; type Fetch = QueryState<Q, F>;
} }
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemState. If // SAFE: QueryState is constrained to read-only fetches, so it only reads World.
unsafe impl<Q: WorldQuery, F: WorldQuery> ReadOnlySystemParamFetch for QueryState<Q, F>
where
Q::Fetch: ReadOnlyFetch,
F::Fetch: FilterFetch,
{
}
// SAFE: Relevant query ComponentId and ArchetypeComponentId access is applied to SystemMeta. If
// this QueryState conflicts with any prior access, a panic will occur. // this QueryState conflicts with any prior access, a panic will occur.
unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState for QueryState<Q, F> unsafe impl<Q: WorldQuery + 'static, F: WorldQuery + 'static> SystemParamState for QueryState<Q, F>
where where
@ -103,28 +117,28 @@ where
{ {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
let state = QueryState::new(world); let state = QueryState::new(world);
assert_component_access_compatibility( assert_component_access_compatibility(
&system_state.name, &system_meta.name,
std::any::type_name::<Q>(), std::any::type_name::<Q>(),
std::any::type_name::<F>(), std::any::type_name::<F>(),
&system_state.component_access_set, &system_meta.component_access_set,
&state.component_access, &state.component_access,
world, world,
); );
system_state system_meta
.component_access_set .component_access_set
.add(state.component_access.clone()); .add(state.component_access.clone());
system_state system_meta
.archetype_component_access .archetype_component_access
.extend(&state.archetype_component_access); .extend(&state.archetype_component_access);
state state
} }
fn new_archetype(&mut self, archetype: &Archetype, system_state: &mut SystemState) { fn new_archetype(&mut self, archetype: &Archetype, system_meta: &mut SystemMeta) {
self.new_archetype(archetype); self.new_archetype(archetype);
system_state system_meta
.archetype_component_access .archetype_component_access
.extend(&self.archetype_component_access); .extend(&self.archetype_component_access);
} }
@ -141,11 +155,11 @@ where
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
Query::new(world, state, system_state.last_change_tick, change_tick) Query::new(world, state, system_meta.last_change_tick, change_tick)
} }
} }
@ -189,6 +203,9 @@ pub struct Res<'w, T: Component> {
change_tick: u32, change_tick: u32,
} }
// SAFE: Res only reads a single World resource
unsafe impl<T: Component> ReadOnlySystemParamFetch for ResState<T> {}
impl<'w, T: Component> Debug for Res<'w, T> impl<'w, T: Component> Debug for Res<'w, T>
where where
T: Debug, T: Debug,
@ -238,18 +255,18 @@ impl<'a, T: Component> SystemParam for Res<'a, T> {
type Fetch = ResState<T>; type Fetch = ResState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// conflicts with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResState<T> { unsafe impl<T: Component> SystemParamState for ResState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
let component_id = world.initialize_resource::<T>(); let component_id = world.initialize_resource::<T>();
let combined_access = system_state.component_access_set.combined_access_mut(); let combined_access = system_meta.component_access_set.combined_access_mut();
if combined_access.has_write(component_id) { if combined_access.has_write(component_id) {
panic!( panic!(
"Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} }
combined_access.add_read(component_id); combined_access.add_read(component_id);
@ -257,7 +274,7 @@ unsafe impl<T: Component> SystemParamState for ResState<T> {
let archetype_component_id = resource_archetype let archetype_component_id = resource_archetype
.get_archetype_component_id(component_id) .get_archetype_component_id(component_id)
.unwrap(); .unwrap();
system_state system_meta
.archetype_component_access .archetype_component_access
.add_read(archetype_component_id); .add_read(archetype_component_id);
Self { Self {
@ -275,7 +292,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -284,14 +301,14 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResState<T> {
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"Resource requested by {} does not exist: {}", "Resource requested by {} does not exist: {}",
system_state.name, system_meta.name,
std::any::type_name::<T>() std::any::type_name::<T>()
) )
}); });
Res { Res {
value: &*column.get_data_ptr().cast::<T>().as_ptr(), value: &*column.get_data_ptr().cast::<T>().as_ptr(),
ticks: column.get_ticks_unchecked(0), ticks: column.get_ticks_unchecked(0),
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
} }
} }
@ -304,11 +321,14 @@ impl<'a, T: Component> SystemParam for Option<Res<'a, T>> {
type Fetch = OptionResState<T>; type Fetch = OptionResState<T>;
} }
// SAFE: Only reads a single World resource
unsafe impl<T: Component> ReadOnlySystemParamFetch for OptionResState<T> {}
unsafe impl<T: Component> SystemParamState for OptionResState<T> { unsafe impl<T: Component> SystemParamState for OptionResState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self(ResState::init(world, system_state, ())) Self(ResState::init(world, system_meta, ()))
} }
fn default_config() {} fn default_config() {}
@ -320,7 +340,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -329,7 +349,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResState<T> {
.map(|column| Res { .map(|column| Res {
value: &*column.get_data_ptr().cast::<T>().as_ptr(), value: &*column.get_data_ptr().cast::<T>().as_ptr(),
ticks: column.get_ticks_unchecked(0), ticks: column.get_ticks_unchecked(0),
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
}) })
} }
@ -345,22 +365,22 @@ impl<'a, T: Component> SystemParam for ResMut<'a, T> {
type Fetch = ResMutState<T>; type Fetch = ResMutState<T>;
} }
// SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemState. If this Res // SAFE: Res ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this Res
// conflicts with any prior access, a panic will occur. // conflicts with any prior access, a panic will occur.
unsafe impl<T: Component> SystemParamState for ResMutState<T> { unsafe impl<T: Component> SystemParamState for ResMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
let component_id = world.initialize_resource::<T>(); let component_id = world.initialize_resource::<T>();
let combined_access = system_state.component_access_set.combined_access_mut(); let combined_access = system_meta.component_access_set.combined_access_mut();
if combined_access.has_write(component_id) { if combined_access.has_write(component_id) {
panic!( panic!(
"ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} else if combined_access.has_read(component_id) { } else if combined_access.has_read(component_id) {
panic!( panic!(
"ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} }
combined_access.add_write(component_id); combined_access.add_write(component_id);
@ -368,7 +388,7 @@ unsafe impl<T: Component> SystemParamState for ResMutState<T> {
let archetype_component_id = resource_archetype let archetype_component_id = resource_archetype
.get_archetype_component_id(component_id) .get_archetype_component_id(component_id)
.unwrap(); .unwrap();
system_state system_meta
.archetype_component_access .archetype_component_access
.add_write(archetype_component_id); .add_write(archetype_component_id);
Self { Self {
@ -386,7 +406,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResMutState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -395,7 +415,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResMutState<T> {
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"Resource requested by {} does not exist: {}", "Resource requested by {} does not exist: {}",
system_state.name, system_meta.name,
std::any::type_name::<T>() std::any::type_name::<T>()
) )
}); });
@ -403,7 +423,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for ResMutState<T> {
value: value.value, value: value.value,
ticks: Ticks { ticks: Ticks {
component_ticks: value.ticks.component_ticks, component_ticks: value.ticks.component_ticks,
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
}, },
} }
@ -420,8 +440,8 @@ impl<'a, T: Component> SystemParam for Option<ResMut<'a, T>> {
unsafe impl<T: Component> SystemParamState for OptionResMutState<T> { unsafe impl<T: Component> SystemParamState for OptionResMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self(ResMutState::init(world, system_state, ())) Self(ResMutState::init(world, system_meta, ()))
} }
fn default_config() {} fn default_config() {}
@ -433,7 +453,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -443,7 +463,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for OptionResMutState<T> {
value: value.value, value: value.value,
ticks: Ticks { ticks: Ticks {
component_ticks: value.ticks.component_ticks, component_ticks: value.ticks.component_ticks,
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
}, },
}) })
@ -454,11 +474,14 @@ impl<'a> SystemParam for Commands<'a> {
type Fetch = CommandQueue; type Fetch = CommandQueue;
} }
// SAFE: Commands only accesses internal state
unsafe impl ReadOnlySystemParamFetch for CommandQueue {}
// SAFE: only local state is accessed // SAFE: only local state is accessed
unsafe impl SystemParamState for CommandQueue { unsafe impl SystemParamState for CommandQueue {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Default::default() Default::default()
} }
@ -475,7 +498,7 @@ impl<'a> SystemParamFetch<'a> for CommandQueue {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -511,6 +534,9 @@ impl<'a> SystemParamFetch<'a> for CommandQueue {
/// ``` /// ```
pub struct Local<'a, T: Component>(&'a mut T); pub struct Local<'a, T: Component>(&'a mut T);
// SAFE: Local only accesses internal state
unsafe impl<T: Component> ReadOnlySystemParamFetch for LocalState<T> {}
impl<'a, T: Component> Debug for Local<'a, T> impl<'a, T: Component> Debug for Local<'a, T>
where where
T: Debug, T: Debug,
@ -547,7 +573,7 @@ impl<'a, T: Component + FromWorld> SystemParam for Local<'a, T> {
unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> { unsafe impl<T: Component + FromWorld> SystemParamState for LocalState<T> {
type Config = Option<T>; type Config = Option<T>;
fn init(world: &mut World, _system_state: &mut SystemState, config: Self::Config) -> Self { fn init(world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self {
Self(config.unwrap_or_else(|| T::from_world(world))) Self(config.unwrap_or_else(|| T::from_world(world)))
} }
@ -562,7 +588,7 @@ impl<'a, T: Component + FromWorld> SystemParamFetch<'a> for LocalState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
_world: &'a World, _world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -601,6 +627,9 @@ impl<'a, T> RemovedComponents<'a, T> {
} }
} }
// SAFE: Only reads World components
unsafe impl<T: Component> ReadOnlySystemParamFetch for RemovedComponentsState<T> {}
/// The [`SystemParamState`] of [`RemovedComponents`]. /// The [`SystemParamState`] of [`RemovedComponents`].
pub struct RemovedComponentsState<T> { pub struct RemovedComponentsState<T> {
component_id: ComponentId, component_id: ComponentId,
@ -616,7 +645,7 @@ impl<'a, T: Component> SystemParam for RemovedComponents<'a, T> {
unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> { unsafe impl<T: Component> SystemParamState for RemovedComponentsState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self { Self {
component_id: world.components.get_or_insert_id::<T>(), component_id: world.components.get_or_insert_id::<T>(),
marker: PhantomData, marker: PhantomData,
@ -632,7 +661,7 @@ impl<'a, T: Component> SystemParamFetch<'a> for RemovedComponentsState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -661,6 +690,9 @@ pub struct NonSend<'w, T> {
change_tick: u32, change_tick: u32,
} }
// SAFE: Only reads a single World non-send resource
unsafe impl<T> ReadOnlySystemParamFetch for NonSendState<T> {}
impl<'w, T> Debug for NonSend<'w, T> impl<'w, T> Debug for NonSend<'w, T>
where where
T: Debug, T: Debug,
@ -703,20 +735,20 @@ impl<'a, T: 'static> SystemParam for NonSend<'a, T> {
type Fetch = NonSendState<T>; type Fetch = NonSendState<T>;
} }
// SAFE: NonSendComponentId and ArchetypeComponentId access is applied to SystemState. If this // SAFE: NonSendComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
// NonSend conflicts with any prior access, a panic will occur. // NonSend conflicts with any prior access, a panic will occur.
unsafe impl<T: 'static> SystemParamState for NonSendState<T> { unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
system_state.set_non_send(); system_meta.set_non_send();
let component_id = world.initialize_non_send_resource::<T>(); let component_id = world.initialize_non_send_resource::<T>();
let combined_access = system_state.component_access_set.combined_access_mut(); let combined_access = system_meta.component_access_set.combined_access_mut();
if combined_access.has_write(component_id) { if combined_access.has_write(component_id) {
panic!( panic!(
"NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} }
combined_access.add_read(component_id); combined_access.add_read(component_id);
@ -724,7 +756,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendState<T> {
let archetype_component_id = resource_archetype let archetype_component_id = resource_archetype
.get_archetype_component_id(component_id) .get_archetype_component_id(component_id)
.unwrap(); .unwrap();
system_state system_meta
.archetype_component_access .archetype_component_access
.add_read(archetype_component_id); .add_read(archetype_component_id);
Self { Self {
@ -742,7 +774,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -752,7 +784,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"Non-send resource requested by {} does not exist: {}", "Non-send resource requested by {} does not exist: {}",
system_state.name, system_meta.name,
std::any::type_name::<T>() std::any::type_name::<T>()
) )
}); });
@ -760,7 +792,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendState<T> {
NonSend { NonSend {
value: &*column.get_data_ptr().cast::<T>().as_ptr(), value: &*column.get_data_ptr().cast::<T>().as_ptr(),
ticks: column.get_ticks_unchecked(0).clone(), ticks: column.get_ticks_unchecked(0).clone(),
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
} }
} }
@ -831,24 +863,24 @@ impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> {
type Fetch = NonSendMutState<T>; type Fetch = NonSendMutState<T>;
} }
// SAFE: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemState. If this // SAFE: NonSendMut ComponentId and ArchetypeComponentId access is applied to SystemMeta. If this
// NonSendMut conflicts with any prior access, a panic will occur. // NonSendMut conflicts with any prior access, a panic will occur.
unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> { unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
type Config = (); type Config = ();
fn init(world: &mut World, system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(world: &mut World, system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
system_state.set_non_send(); system_meta.set_non_send();
let component_id = world.components.get_or_insert_non_send_resource_id::<T>(); let component_id = world.components.get_or_insert_non_send_resource_id::<T>();
let combined_access = system_state.component_access_set.combined_access_mut(); let combined_access = system_meta.component_access_set.combined_access_mut();
if combined_access.has_write(component_id) { if combined_access.has_write(component_id) {
panic!( panic!(
"NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} else if combined_access.has_read(component_id) { } else if combined_access.has_read(component_id) {
panic!( panic!(
"NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.", "NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Allowing this would break Rust's mutability rules. Consider removing the duplicate access.",
std::any::type_name::<T>(), system_state.name); std::any::type_name::<T>(), system_meta.name);
} }
combined_access.add_write(component_id); combined_access.add_write(component_id);
@ -856,7 +888,7 @@ unsafe impl<T: 'static> SystemParamState for NonSendMutState<T> {
let archetype_component_id = resource_archetype let archetype_component_id = resource_archetype
.get_archetype_component_id(component_id) .get_archetype_component_id(component_id)
.unwrap(); .unwrap();
system_state system_meta
.archetype_component_access .archetype_component_access
.add_write(archetype_component_id); .add_write(archetype_component_id);
Self { Self {
@ -874,7 +906,7 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState<T> {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -884,14 +916,14 @@ impl<'a, T: 'static> SystemParamFetch<'a> for NonSendMutState<T> {
.unwrap_or_else(|| { .unwrap_or_else(|| {
panic!( panic!(
"Non-send resource requested by {} does not exist: {}", "Non-send resource requested by {} does not exist: {}",
system_state.name, system_meta.name,
std::any::type_name::<T>() std::any::type_name::<T>()
) )
}); });
NonSendMut { NonSendMut {
value: &mut *column.get_data_ptr().cast::<T>().as_ptr(), value: &mut *column.get_data_ptr().cast::<T>().as_ptr(),
ticks: &mut *column.get_ticks_mut_ptr_unchecked(0), ticks: &mut *column.get_ticks_mut_ptr_unchecked(0),
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
} }
} }
@ -901,6 +933,9 @@ impl<'a> SystemParam for &'a Archetypes {
type Fetch = ArchetypesState; type Fetch = ArchetypesState;
} }
// SAFE: Only reads World archetypes
unsafe impl ReadOnlySystemParamFetch for ArchetypesState {}
/// The [`SystemParamState`] of [`Archetypes`]. /// The [`SystemParamState`] of [`Archetypes`].
pub struct ArchetypesState; pub struct ArchetypesState;
@ -908,7 +943,7 @@ pub struct ArchetypesState;
unsafe impl SystemParamState for ArchetypesState { unsafe impl SystemParamState for ArchetypesState {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self Self
} }
@ -921,7 +956,7 @@ impl<'a> SystemParamFetch<'a> for ArchetypesState {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
_state: &'a mut Self, _state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -933,6 +968,9 @@ impl<'a> SystemParam for &'a Components {
type Fetch = ComponentsState; type Fetch = ComponentsState;
} }
// SAFE: Only reads World components
unsafe impl ReadOnlySystemParamFetch for ComponentsState {}
/// The [`SystemParamState`] of [`Components`]. /// The [`SystemParamState`] of [`Components`].
pub struct ComponentsState; pub struct ComponentsState;
@ -940,7 +978,7 @@ pub struct ComponentsState;
unsafe impl SystemParamState for ComponentsState { unsafe impl SystemParamState for ComponentsState {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self Self
} }
@ -953,7 +991,7 @@ impl<'a> SystemParamFetch<'a> for ComponentsState {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
_state: &'a mut Self, _state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -965,6 +1003,9 @@ impl<'a> SystemParam for &'a Entities {
type Fetch = EntitiesState; type Fetch = EntitiesState;
} }
// SAFE: Only reads World entities
unsafe impl ReadOnlySystemParamFetch for EntitiesState {}
/// The [`SystemParamState`] of [`Entities`]. /// The [`SystemParamState`] of [`Entities`].
pub struct EntitiesState; pub struct EntitiesState;
@ -972,7 +1013,7 @@ pub struct EntitiesState;
unsafe impl SystemParamState for EntitiesState { unsafe impl SystemParamState for EntitiesState {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self Self
} }
@ -985,7 +1026,7 @@ impl<'a> SystemParamFetch<'a> for EntitiesState {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
_state: &'a mut Self, _state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -997,6 +1038,9 @@ impl<'a> SystemParam for &'a Bundles {
type Fetch = BundlesState; type Fetch = BundlesState;
} }
// SAFE: Only reads World bundles
unsafe impl ReadOnlySystemParamFetch for BundlesState {}
/// The [`SystemParamState`] of [`Bundles`]. /// The [`SystemParamState`] of [`Bundles`].
pub struct BundlesState; pub struct BundlesState;
@ -1004,7 +1048,7 @@ pub struct BundlesState;
unsafe impl SystemParamState for BundlesState { unsafe impl SystemParamState for BundlesState {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self Self
} }
@ -1017,7 +1061,7 @@ impl<'a> SystemParamFetch<'a> for BundlesState {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
_state: &'a mut Self, _state: &'a mut Self,
_system_state: &'a SystemState, _system_meta: &SystemMeta,
world: &'a World, world: &'a World,
_change_tick: u32, _change_tick: u32,
) -> Self::Item { ) -> Self::Item {
@ -1031,6 +1075,9 @@ pub struct SystemChangeTick {
pub change_tick: u32, pub change_tick: u32,
} }
// SAFE: Only reads internal system state
unsafe impl ReadOnlySystemParamFetch for SystemChangeTickState {}
impl SystemParam for SystemChangeTick { impl SystemParam for SystemChangeTick {
type Fetch = SystemChangeTickState; type Fetch = SystemChangeTickState;
} }
@ -1041,7 +1088,7 @@ pub struct SystemChangeTickState {}
unsafe impl SystemParamState for SystemChangeTickState { unsafe impl SystemParamState for SystemChangeTickState {
type Config = (); type Config = ();
fn init(_world: &mut World, _system_state: &mut SystemState, _config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, _config: Self::Config) -> Self {
Self {} Self {}
} }
@ -1053,12 +1100,12 @@ impl<'a> SystemParamFetch<'a> for SystemChangeTickState {
unsafe fn get_param( unsafe fn get_param(
_state: &mut Self, _state: &mut Self,
system_state: &SystemState, system_meta: &SystemMeta,
_world: &World, _world: &World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
SystemChangeTick { SystemChangeTick {
last_change_tick: system_state.last_change_tick, last_change_tick: system_meta.last_change_tick,
change_tick, change_tick,
} }
} }
@ -1069,6 +1116,10 @@ macro_rules! impl_system_param_tuple {
impl<$($param: SystemParam),*> SystemParam for ($($param,)*) { impl<$($param: SystemParam),*> SystemParam for ($($param,)*) {
type Fetch = ($($param::Fetch,)*); type Fetch = ($($param::Fetch,)*);
} }
// SAFE: tuple consists only of ReadOnlySystemParamFetches
unsafe impl<$($param: ReadOnlySystemParamFetch),*> ReadOnlySystemParamFetch for ($($param,)*) {}
#[allow(unused_variables)] #[allow(unused_variables)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) { impl<'a, $($param: SystemParamFetch<'a>),*> SystemParamFetch<'a> for ($($param,)*) {
@ -1077,13 +1128,13 @@ macro_rules! impl_system_param_tuple {
#[inline] #[inline]
unsafe fn get_param( unsafe fn get_param(
state: &'a mut Self, state: &'a mut Self,
system_state: &'a SystemState, system_meta: &SystemMeta,
world: &'a World, world: &'a World,
change_tick: u32, change_tick: u32,
) -> Self::Item { ) -> Self::Item {
let ($($param,)*) = state; let ($($param,)*) = state;
($($param::get_param($param, system_state, world, change_tick),)*) ($($param::get_param($param, system_meta, world, change_tick),)*)
} }
} }
@ -1092,15 +1143,15 @@ macro_rules! impl_system_param_tuple {
unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) { unsafe impl<$($param: SystemParamState),*> SystemParamState for ($($param,)*) {
type Config = ($(<$param as SystemParamState>::Config,)*); type Config = ($(<$param as SystemParamState>::Config,)*);
#[inline] #[inline]
fn init(_world: &mut World, _system_state: &mut SystemState, config: Self::Config) -> Self { fn init(_world: &mut World, _system_meta: &mut SystemMeta, config: Self::Config) -> Self {
let ($($param,)*) = config; let ($($param,)*) = config;
(($($param::init(_world, _system_state, $param),)*)) (($($param::init(_world, _system_meta, $param),)*))
} }
#[inline] #[inline]
fn new_archetype(&mut self, _archetype: &Archetype, _system_state: &mut SystemState) { fn new_archetype(&mut self, _archetype: &Archetype, _system_meta: &mut SystemMeta) {
let ($($param,)*) = self; let ($($param,)*) = self;
$($param.new_archetype(_archetype, _system_state);)* $($param.new_archetype(_archetype, _system_meta);)*
} }
#[inline] #[inline]