From ab3dfd0c694bfa0b9188bfb577ce0119cae457c1 Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Wed, 31 Jul 2024 12:12:19 -0400 Subject: [PATCH 1/6] separate component/resource access --- crates/bevy_ecs/src/query/access.rs | 397 +++++++++++++++------ crates/bevy_ecs/src/query/fetch.rs | 44 +-- crates/bevy_ecs/src/query/filter.rs | 4 +- crates/bevy_ecs/src/query/state.rs | 8 +- crates/bevy_ecs/src/system/mod.rs | 6 +- crates/bevy_ecs/src/system/system_param.rs | 20 +- crates/bevy_ecs/src/world/entity_ref.rs | 20 +- examples/ecs/dynamic.rs | 2 +- 8 files changed, 334 insertions(+), 167 deletions(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 54a70690a126f..322d11cd56362 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -49,39 +49,58 @@ impl<'a, T: SparseSetIndex + fmt::Debug> fmt::Debug for FormattedBitSet<'a, T> { /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions. #[derive(Eq, PartialEq)] pub struct Access { - /// All accessed elements. - reads_and_writes: FixedBitSet, - /// The exclusively-accessed elements. - writes: FixedBitSet, + /// All accessed components. + component_read_and_writes: FixedBitSet, + /// The exclusively-accessed components. + component_writes: FixedBitSet, + /// All accessed resources. + resource_read_and_writes: FixedBitSet, + /// The exclusively-accessed resources. + resource_writes: FixedBitSet, + /// Is `true` if this has access to all components. + /// (Note that this does not include `Resources`) + reads_all_components: bool, + /// Is `true` if this has mutable access to all components. + /// (Note that this does not include `Resources`) + writes_all_components: bool, /// Is `true` if this has access to all elements in the collection. /// This field is a performance optimization for `&World` (also harder to mess up for soundness). - reads_all: bool, + reads_all_resources: bool, /// Is `true` if this has mutable access to all elements in the collection. /// If this is true, then `reads_all` must also be true. - writes_all: bool, + writes_all_resources: bool, // Elements that are not accessed, but whose presence in an archetype affect query results. archetypal: FixedBitSet, marker: PhantomData, } + // This is needed since `#[derive(Clone)]` does not generate optimized `clone_from`. impl Clone for Access { fn clone(&self) -> Self { Self { - reads_and_writes: self.reads_and_writes.clone(), - writes: self.writes.clone(), - reads_all: self.reads_all, - writes_all: self.writes_all, + component_read_and_writes: self.component_read_and_writes.clone(), + component_writes: self.component_writes.clone(), + resource_read_and_writes: self.resource_read_and_writes.clone(), + resource_writes: self.resource_writes.clone(), + reads_all_components: self.reads_all_components, + writes_all_components: self.writes_all_components, + reads_all_resources: self.reads_all_resources, + writes_all_resources: self.writes_all_resources, archetypal: self.archetypal.clone(), marker: PhantomData, } } fn clone_from(&mut self, source: &Self) { - self.reads_and_writes.clone_from(&source.reads_and_writes); - self.writes.clone_from(&source.writes); - self.reads_all = source.reads_all; - self.writes_all = source.writes_all; + self.component_read_and_writes.clone_from(&source.component_read_and_writes); + self.component_writes.clone_from(&source.component_writes); + self.resource_read_and_writes.clone_from(&source.resource_read_and_writes); + self.resource_writes.clone_from(&source.resource_writes); + self.reads_all_components = source.reads_all_components; + self.writes_all_components = source.writes_all_components; + self.reads_all_resources = source.reads_all_resources; + self.writes_all_resources = source.writes_all_resources; self.archetypal.clone_from(&source.archetypal); } } @@ -90,12 +109,19 @@ impl fmt::Debug for Access { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Access") .field( - "read_and_writes", - &FormattedBitSet::::new(&self.reads_and_writes), + "component_read_and_writes", + &FormattedBitSet::::new(&self.component_read_and_writes), + ) + .field("component_writes", &FormattedBitSet::::new(&self.component_writes)) + .field( + "resource_read_and_writes", + &FormattedBitSet::::new(&self.resource_read_and_writes), ) - .field("writes", &FormattedBitSet::::new(&self.writes)) - .field("reads_all", &self.reads_all) - .field("writes_all", &self.writes_all) + .field("resource_writes", &FormattedBitSet::::new(&self.resource_writes)) + .field("reads_all_components", &self.reads_all_components) + .field("writes_all_components", &self.writes_all_components) + .field("reads_all_resources", &self.reads_all_resources) + .field("writes_all_resources", &self.writes_all_resources) .field("archetypal", &FormattedBitSet::::new(&self.archetypal)) .finish() } @@ -111,26 +137,43 @@ impl Access { /// Creates an empty [`Access`] collection. pub const fn new() -> Self { Self { - reads_all: false, - writes_all: false, - reads_and_writes: FixedBitSet::new(), - writes: FixedBitSet::new(), + reads_all_resources: false, + writes_all_resources: false, + reads_all_components: false, + writes_all_components: false, + component_read_and_writes: FixedBitSet::new(), + component_writes: FixedBitSet::new(), + resource_read_and_writes: FixedBitSet::default(), + resource_writes: FixedBitSet::default(), archetypal: FixedBitSet::new(), marker: PhantomData, } } - /// Adds access to the element given by `index`. - pub fn add_read(&mut self, index: T) { - self.reads_and_writes + /// Adds access to the component given by `index`. + pub fn add_component_read(&mut self, index: T) { + self.component_read_and_writes .grow_and_insert(index.sparse_set_index()); } - /// Adds exclusive access to the element given by `index`. - pub fn add_write(&mut self, index: T) { - self.reads_and_writes + /// Adds exclusive access to the component given by `index`. + pub fn add_component_write(&mut self, index: T) { + self.component_read_and_writes + .grow_and_insert(index.sparse_set_index()); + self.component_writes.grow_and_insert(index.sparse_set_index()); + } + + /// Adds access to the resource given by `index`. + pub fn add_resource_read(&mut self, index: T) { + self.resource_read_and_writes .grow_and_insert(index.sparse_set_index()); - self.writes.grow_and_insert(index.sparse_set_index()); + } + + /// Adds exclusive access to the resource given by `index`. + pub fn add_resource_write(&mut self, index: T) { + self.resource_read_and_writes + .grow_and_insert(index.sparse_set_index()); + self.resource_writes.grow_and_insert(index.sparse_set_index()); } /// Adds an archetypal (indirect) access to the element given by `index`. @@ -145,24 +188,44 @@ impl Access { self.archetypal.grow_and_insert(index.sparse_set_index()); } - /// Returns `true` if this can access the element given by `index`. - pub fn has_read(&self, index: T) -> bool { - self.reads_all || self.reads_and_writes.contains(index.sparse_set_index()) + /// Returns `true` if this can access the component given by `index`. + pub fn has_component_read(&self, index: T) -> bool { + self.reads_all_components || self.component_read_and_writes.contains(index.sparse_set_index()) } - /// Returns `true` if this can access anything. - pub fn has_any_read(&self) -> bool { - self.reads_all || !self.reads_and_writes.is_clear() + /// Returns `true` if this can access any component. + pub fn has_any_component_read(&self) -> bool { + self.reads_all_components || !self.component_read_and_writes.is_clear() } - /// Returns `true` if this can exclusively access the element given by `index`. - pub fn has_write(&self, index: T) -> bool { - self.writes_all || self.writes.contains(index.sparse_set_index()) + /// Returns `true` if this can exclusively access the component given by `index`. + pub fn has_component_write(&self, index: T) -> bool { + self.writes_all_components || self.component_writes.contains(index.sparse_set_index()) } /// Returns `true` if this accesses anything mutably. - pub fn has_any_write(&self) -> bool { - self.writes_all || !self.writes.is_clear() + pub fn has_any_component_write(&self) -> bool { + self.writes_all_components || !self.component_writes.is_clear() + } + + /// Returns `true` if this can access the resource given by `index`. + pub fn has_resource_read(&self, index: T) -> bool { + self.reads_all_resources || self.resource_read_and_writes.contains(index.sparse_set_index()) + } + + /// Returns `true` if this can access any resource. + pub fn has_any_resource_read(&self) -> bool { + self.reads_all_resources || !self.resource_read_and_writes.is_clear() + } + + /// Returns `true` if this can exclusively access the resource given by `index`. + pub fn has_resource_write(&self, index: T) -> bool { + self.writes_all_resources || self.resource_writes.contains(index.sparse_set_index()) + } + + /// Returns `true` if this accesses any resource mutably. + pub fn has_any_resource_write(&self) -> bool { + self.writes_all_resources || !self.component_writes.is_clear() } /// Returns true if this has an archetypal (indirect) access to the element given by `index`. @@ -177,47 +240,110 @@ impl Access { self.archetypal.contains(index.sparse_set_index()) } + /// Sets this as having access to all components (i.e. `EntityRef`). + #[inline] + pub fn read_all_components(&mut self) { + self.reads_all_components = true; + } + + /// Sets this as having mutable access to all components (i.e. `EntityMut`). + #[inline] + pub fn write_all_components(&mut self) { + self.reads_all_components = true; + self.writes_all_components = true; + } + + /// Sets this as having access to all resources (i.e. `&World`). + #[inline] + pub fn read_all_resources(&mut self) { + self.reads_all_resources = true; + } + + /// Sets this as having mutable access to all resources (i.e. `&mut World`). + #[inline] + pub fn write_all_resources(&mut self) { + self.reads_all_resources = true; + self.writes_all_resources = true; + } + /// Sets this as having access to all indexed elements (i.e. `&World`). + #[inline] pub fn read_all(&mut self) { - self.reads_all = true; + self.read_all_components(); + self.reads_all_resources(); } - /// Sets this as having mutable access to all indexed elements (i.e. `EntityMut`). + /// Sets this as having mutable access to all indexed elements (i.e. `&mut World`). + #[inline] pub fn write_all(&mut self) { - self.reads_all = true; - self.writes_all = true; + self.write_all_components(); + self.write_all_resources(); + } + + /// Returns `true` if this has access to all components (i.e. `EntityRef`). + #[inline] + pub fn has_read_all_components(&self) -> bool { + self.reads_all_components + } + + /// Returns `true` if this has write access to all components (i.e. `EntityMut`). + #[inline] + pub fn has_write_all_components(&self) -> bool { + self.writes_all_components + } + + /// Returns `true` if this has access to all resources (i.e. `EntityRef`). + #[inline] + pub fn has_read_all_resources(&self) -> bool { + self.reads_all_resources + } + + /// Returns `true` if this has write access to all resources (i.e. `EntityMut`). + #[inline] + pub fn has_write_all_resources(&self) -> bool { + self.writes_all_resources } /// Returns `true` if this has access to all indexed elements (i.e. `&World`). pub fn has_read_all(&self) -> bool { - self.reads_all + self.has_read_all_components() && self.has_read_all_resources() } - /// Returns `true` if this has write access to all indexed elements (i.e. `EntityMut`). + /// Returns `true` if this has write access to all indexed elements (i.e. `&mut World`). pub fn has_write_all(&self) -> bool { - self.writes_all + self.has_write_all_components() && self.has_write_all_resources() } /// Removes all writes. pub fn clear_writes(&mut self) { - self.writes_all = false; - self.writes.clear(); + self.writes_all_resources = false; + self.writes_all_components = false; + self.component_writes.clear(); + self.resource_writes.clear(); } /// Removes all accesses. pub fn clear(&mut self) { - self.reads_all = false; - self.writes_all = false; - self.reads_and_writes.clear(); - self.writes.clear(); + self.reads_all_resources = false; + self.writes_all_resources = false; + self.reads_all_components = false; + self.writes_all_components = false; + self.component_read_and_writes.clear(); + self.component_writes.clear(); + self.resource_read_and_writes.clear(); + self.resource_writes.clear(); } /// Adds all access from `other`. pub fn extend(&mut self, other: &Access) { - self.reads_all = self.reads_all || other.reads_all; - self.writes_all = self.writes_all || other.writes_all; - self.reads_and_writes.union_with(&other.reads_and_writes); - self.writes.union_with(&other.writes); + self.reads_all_resources = self.reads_all_resources || other.reads_all_resources; + self.writes_all_resources = self.writes_all_resources || other.writes_all_resources; + self.reads_all_components = self.reads_all_components || other.reads_all_components; + self.writes_all_components = self.writes_all_components || other.writes_all_components; + self.component_read_and_writes.union_with(&other.component_read_and_writes); + self.component_writes.union_with(&other.component_writes); + self.resource_read_and_writes.union_with(&other.resource_read_and_writes); + self.resource_writes.union_with(&other.resource_writes); } /// Returns `true` if the access and `other` can be active at the same time. @@ -225,93 +351,134 @@ impl Access { /// [`Access`] instances are incompatible if one can write /// an element that the other can read or write. pub fn is_compatible(&self, other: &Access) -> bool { - if self.writes_all { - return !other.has_any_read(); + if self.writes_all_components { + return !other.has_any_component_read(); } - if other.writes_all { - return !self.has_any_read(); + if other.writes_all_components { + return !self.has_any_component_read(); } - if self.reads_all { - return !other.has_any_write(); + if self.reads_all_components { + return !other.has_any_component_write(); } - if other.reads_all { - return !self.has_any_write(); + if other.reads_all_components { + return !self.has_any_component_write(); } - self.writes.is_disjoint(&other.reads_and_writes) - && other.writes.is_disjoint(&self.reads_and_writes) + if self.writes_all_resources { + return !other.has_any_resource_read(); + } + + if other.writes_all_resources { + return !self.has_any_resource_read(); + } + + if self.reads_all_resources { + return !other.has_any_resource_write(); + } + + if other.reads_all_resources { + return !self.has_any_resource_write(); + } + + self.component_writes.is_disjoint(&other.component_read_and_writes) + && self.resource_writes.is_disjoint(&other.resource_read_and_writes) + && other.component_writes.is_disjoint(&self.component_read_and_writes) + && other.resource_writes.is_disjoint(&self.resource_read_and_writes) } /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &Access) -> bool { - if self.writes_all { - return other.writes_all; - } - if other.writes_all { + if other.writes_all_components && other.reads_all_components { return true; } - - if self.reads_all { - return other.reads_all; + + if self.writes_all_components && !other.writes_all_components { + return false; } - - if other.reads_all { - return self.writes.is_subset(&other.writes); + if self.reads_all_components && !other.reads_all_components { + return false; + } + if self.writes_all_resources && !other.writes_all_resources { + return false; + } + if self.reads_all_resources && !other.reads_all_resources { + return false; } - self.reads_and_writes.is_subset(&other.reads_and_writes) - && self.writes.is_subset(&other.writes) + (other.reads_all_components || self.component_read_and_writes.is_subset(&other.component_read_and_writes)) + && (other.writes_all_components || self.component_writes.is_subset(&other.component_writes)) + && (other.reads_all_resources || self.resource_read_and_writes.is_subset(&other.resource_read_and_writes)) + && (other.writes_all_resources || self.resource_writes.is_subset(&other.resource_writes)) } /// Returns a vector of elements that the access and `other` cannot access at the same time. pub fn get_conflicts(&self, other: &Access) -> Vec { let mut conflicts = FixedBitSet::default(); - if self.reads_all { + if self.reads_all_components { + // QUESTION: How to handle `other.writes_all`? + conflicts.extend(other.component_writes.ones()); + } + + if other.reads_all_components { + // QUESTION: How to handle `self.writes_all`. + conflicts.extend(self.component_writes.ones()); + } + + if self.writes_all_components { + conflicts.extend(other.component_read_and_writes.ones()); + } + + if other.writes_all_components { + conflicts.extend(self.component_read_and_writes.ones()); + } + if self.reads_all_resources { // QUESTION: How to handle `other.writes_all`? - conflicts.extend(other.writes.ones()); + conflicts.extend(other.component_writes.ones()); } - if other.reads_all { + if other.reads_all_resources { // QUESTION: How to handle `self.writes_all`. - conflicts.extend(self.writes.ones()); + conflicts.extend(self.component_writes.ones()); } - if self.writes_all { - conflicts.extend(other.reads_and_writes.ones()); + if self.writes_all_resources { + conflicts.extend(other.component_read_and_writes.ones()); } - if other.writes_all { - conflicts.extend(self.reads_and_writes.ones()); + if other.writes_all_resources { + conflicts.extend(self.component_read_and_writes.ones()); } - conflicts.extend(self.writes.intersection(&other.reads_and_writes)); - conflicts.extend(self.reads_and_writes.intersection(&other.writes)); + conflicts.extend(self.component_writes.intersection(&other.component_read_and_writes)); + conflicts.extend(self.component_read_and_writes.intersection(&other.component_writes)); + conflicts.extend(self.resource_writes.intersection(&other.resource_read_and_writes)); + conflicts.extend(self.resource_read_and_writes.intersection(&other.resource_writes)); conflicts .ones() .map(SparseSetIndex::get_sparse_set_index) .collect() } - /// Returns the indices of the elements this has access to. - pub fn reads_and_writes(&self) -> impl Iterator + '_ { - self.reads_and_writes.ones().map(T::get_sparse_set_index) + /// Returns the indices of the components this has access to. + pub fn component_reads_and_writes(&self) -> impl Iterator + '_ { + self.component_read_and_writes.ones().map(T::get_sparse_set_index) } - /// Returns the indices of the elements this has non-exclusive access to. - pub fn reads(&self) -> impl Iterator + '_ { - self.reads_and_writes - .difference(&self.writes) + /// Returns the indices of the components this has non-exclusive access to. + pub fn component_reads(&self) -> impl Iterator + '_ { + self.component_read_and_writes + .difference(&self.component_writes) .map(T::get_sparse_set_index) } - /// Returns the indices of the elements this has exclusive access to. - pub fn writes(&self) -> impl Iterator + '_ { - self.writes.ones().map(T::get_sparse_set_index) + /// Returns the indices of the components this has exclusive access to. + pub fn component_writes(&self) -> impl Iterator + '_ { + self.component_writes.ones().map(T::get_sparse_set_index) } /// Returns the indices of the elements that this has an archetypal access to. @@ -422,14 +589,14 @@ impl FilteredAccess { /// Adds access to the element given by `index`. pub fn add_read(&mut self, index: T) { - self.access.add_read(index.clone()); + self.access.add_component_read(index.clone()); self.add_required(index.clone()); self.and_with(index); } /// Adds exclusive access to the element given by `index`. pub fn add_write(&mut self, index: T) { - self.access.add_write(index.clone()); + self.access.add_component_write(index.clone()); self.add_required(index.clone()); self.and_with(index); } @@ -769,9 +936,9 @@ mod tests { fn create_sample_access() -> Access { let mut access = Access::::default(); - access.add_read(1); - access.add_read(2); - access.add_write(3); + access.add_component_read(1); + access.add_component_read(2); + access.add_component_write(3); access.add_archetypal(5); access.read_all(); @@ -821,8 +988,8 @@ mod tests { let original: Access = create_sample_access(); let mut cloned = Access::::default(); - cloned.add_write(7); - cloned.add_read(4); + cloned.add_component_write(7); + cloned.add_component_read(4); cloned.add_archetypal(8); cloned.write_all(); @@ -900,7 +1067,7 @@ mod tests { fn read_all_access_conflicts() { // read_all / single write let mut access_a = Access::::default(); - access_a.add_write(0); + access_a.add_component_write(0); let mut access_b = Access::::default(); access_b.read_all(); @@ -920,24 +1087,24 @@ mod tests { #[test] fn access_get_conflicts() { let mut access_a = Access::::default(); - access_a.add_read(0); - access_a.add_read(1); + access_a.add_component_read(0); + access_a.add_component_read(1); let mut access_b = Access::::default(); - access_b.add_read(0); - access_b.add_write(1); + access_b.add_component_read(0); + access_b.add_component_write(1); assert_eq!(access_a.get_conflicts(&access_b), vec![1]); let mut access_c = Access::::default(); - access_c.add_write(0); - access_c.add_write(1); + access_c.add_component_write(0); + access_c.add_component_write(1); assert_eq!(access_a.get_conflicts(&access_c), vec![0, 1]); assert_eq!(access_b.get_conflicts(&access_c), vec![0, 1]); let mut access_d = Access::::default(); - access_d.add_read(0); + access_d.add_component_read(0); assert_eq!(access_d.get_conflicts(&access_a), vec![]); assert_eq!(access_d.get_conflicts(&access_b), vec![]); diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index e3d51405771bc..0d57bf6503678 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -480,7 +480,7 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { assert!( - !access.access().has_any_write(), + !access.access().has_any_component_write(), "EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", ); access.read_all(); @@ -556,7 +556,7 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { fn update_component_access(_state: &Self::State, access: &mut FilteredAccess) { assert!( - !access.access().has_any_read(), + !access.access().has_any_component_read(), "EntityMut conflicts with a previous access in this query. Exclusive access cannot coincide with any other accesses.", ); access.write_all(); @@ -612,9 +612,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { _table: &Table, ) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if archetype.contains(id) { - access.add_read(id); + access.add_component_read(id); } }); fetch.1 = access; @@ -623,9 +623,9 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { #[inline] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if table.has_column(id) { - access.add_read(id); + access.add_component_read(id); } }); fetch.1 = access; @@ -715,14 +715,14 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { _table: &Table, ) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if archetype.contains(id) { - access.add_read(id); + access.add_component_read(id); } }); - state.access.writes().for_each(|id| { + state.access.component_writes().for_each(|id| { if archetype.contains(id) { - access.add_write(id); + access.add_component_write(id); } }); fetch.1 = access; @@ -731,14 +731,14 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { #[inline] unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { let mut access = Access::default(); - state.access.reads().for_each(|id| { + state.access.component_reads().for_each(|id| { if table.has_column(id) { - access.add_read(id); + access.add_component_read(id); } }); - state.access.writes().for_each(|id| { + state.access.component_writes().for_each(|id| { if table.has_column(id) { - access.add_write(id); + access.add_component_write(id); } }); fetch.1 = access; @@ -988,9 +988,9 @@ unsafe impl WorldQuery for &T { access: &mut FilteredAccess, ) { assert!( - !access.access().has_write(component_id), + !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - std::any::type_name::(), + std::any::type_name::(), ); access.add_read(component_id); } @@ -1183,9 +1183,9 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { access: &mut FilteredAccess, ) { assert!( - !access.access().has_write(component_id), + !access.access().has_component_write(component_id), "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", - std::any::type_name::(), + std::any::type_name::(), ); access.add_read(component_id); } @@ -1378,9 +1378,9 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { access: &mut FilteredAccess, ) { assert!( - !access.access().has_read(component_id), + !access.access().has_component_read(component_id), "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", - std::any::type_name::(), + std::any::type_name::(), ); access.add_write(component_id); } @@ -1476,9 +1476,9 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { // Update component access here instead of in `<&mut T as WorldQuery>` to avoid erroneously referencing // `&mut T` in error message. assert!( - !access.access().has_read(component_id), + !access.access().has_component_read(component_id), "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", - std::any::type_name::(), + std::any::type_name::(), ); access.add_write(component_id); } diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 0e4d9cc405097..029ecd5212749 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -688,7 +688,7 @@ unsafe impl WorldQuery for Added { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { - if access.access().has_write(id) { + if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } access.add_read(id); @@ -899,7 +899,7 @@ unsafe impl WorldQuery for Changed { #[inline] fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess) { - if access.access().has_write(id) { + if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } access.add_read(id); diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index dcb20250c2466..cda184c202877 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -456,14 +456,14 @@ impl QueryState { archetype: &Archetype, access: &mut Access, ) { - self.component_access.access.reads().for_each(|id| { + self.component_access.access.component_reads().for_each(|id| { if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_read(id); + access.add_component_read(id); } }); - self.component_access.access.writes().for_each(|id| { + self.component_access.access.component_writes().for_each(|id| { if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_write(id); + access.add_component_write(id); } }); } diff --git a/crates/bevy_ecs/src/system/mod.rs b/crates/bevy_ecs/src/system/mod.rs index 071e02f9d3ca4..88faa30cdf4b8 100644 --- a/crates/bevy_ecs/src/system/mod.rs +++ b/crates/bevy_ecs/src/system/mod.rs @@ -1495,7 +1495,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); @@ -1525,7 +1525,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); @@ -1543,7 +1543,7 @@ mod tests { assert_eq!( system .archetype_component_access() - .reads() + .component_reads() .collect::>(), expected_ids ); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 8898e34d092e1..9aa2dc5922f42 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -504,7 +504,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_write(component_id), + !combined_access.has_component_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, @@ -518,7 +518,7 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { .unwrap(); system_meta .archetype_component_access - .add_read(archetype_component_id); + .add_component_read(archetype_component_id); component_id } @@ -600,11 +600,11 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { world.initialize_resource_internal(component_id); let combined_access = system_meta.component_access_set.combined_access(); - if combined_access.has_write(component_id) { + if combined_access.has_component_write(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); - } else if combined_access.has_read(component_id) { + } else if combined_access.has_component_read(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); @@ -618,7 +618,7 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { .unwrap(); system_meta .archetype_component_access - .add_write(archetype_component_id); + .add_component_write(archetype_component_id); component_id } @@ -1153,7 +1153,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_write(component_id), + !combined_access.has_component_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, @@ -1167,7 +1167,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { .unwrap(); system_meta .archetype_component_access - .add_read(archetype_component_id); + .add_component_read(archetype_component_id); component_id } @@ -1246,11 +1246,11 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { world.initialize_non_send_internal(component_id); let combined_access = system_meta.component_access_set.combined_access(); - if combined_access.has_write(component_id) { + if combined_access.has_component_write(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); - } else if combined_access.has_read(component_id) { + } else if combined_access.has_component_read(component_id) { panic!( "error[B0002]: NonSendMut<{}> in system {} conflicts with a previous immutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); @@ -1264,7 +1264,7 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { .unwrap(); system_meta .archetype_component_access - .add_write(archetype_component_id); + .add_component_write(archetype_component_id); component_id } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1dd522c17c802..34dc767341ba6 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -1860,7 +1860,7 @@ impl<'w> FilteredEntityRef<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] pub fn components(&self) -> impl Iterator + '_ { - self.access.reads_and_writes() + self.access.component_reads_and_writes() } /// Returns a reference to the underlying [`Access`]. @@ -1912,7 +1912,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get(&self) -> Option<&'w T> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get() }) .flatten() @@ -1926,7 +1926,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get_ref(&self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_ref() }) .flatten() @@ -1938,7 +1938,7 @@ impl<'w> FilteredEntityRef<'w> { pub fn get_change_ticks(&self) -> Option { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_read(id) + .has_component_read(id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_change_ticks::() }) .flatten() @@ -1953,7 +1953,7 @@ impl<'w> FilteredEntityRef<'w> { #[inline] pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option { self.access - .has_read(component_id) + .has_component_read(component_id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_change_ticks_by_id(component_id) }) .flatten() @@ -1970,7 +1970,7 @@ impl<'w> FilteredEntityRef<'w> { #[inline] pub fn get_by_id(&self, component_id: ComponentId) -> Option> { self.access - .has_read(component_id) + .has_component_read(component_id) // SAFETY: We have read access .then(|| unsafe { self.entity.get_by_id(component_id) }) .flatten() @@ -2117,7 +2117,7 @@ impl<'w> FilteredEntityMut<'w> { /// Returns an iterator over the component ids that are accessed by self. #[inline] pub fn components(&self) -> impl Iterator + '_ { - self.access.reads_and_writes() + self.access.component_reads_and_writes() } /// Returns a reference to the underlying [`Access`]. @@ -2185,7 +2185,7 @@ impl<'w> FilteredEntityMut<'w> { pub fn get_mut(&mut self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_write(id) + .has_component_write(id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut() }) .flatten() @@ -2198,7 +2198,7 @@ impl<'w> FilteredEntityMut<'w> { pub fn into_mut(self) -> Option> { let id = self.entity.world().components().get_id(TypeId::of::())?; self.access - .has_write(id) + .has_component_write(id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut() }) .flatten() @@ -2246,7 +2246,7 @@ impl<'w> FilteredEntityMut<'w> { #[inline] pub fn get_mut_by_id(&mut self, component_id: ComponentId) -> Option> { self.access - .has_write(component_id) + .has_component_write(component_id) // SAFETY: We have write access .then(|| unsafe { self.entity.get_mut_by_id(component_id) }) .flatten() diff --git a/examples/ecs/dynamic.rs b/examples/ecs/dynamic.rs index 14a9d35ce3023..f13f3751740c8 100644 --- a/examples/ecs/dynamic.rs +++ b/examples/ecs/dynamic.rs @@ -168,7 +168,7 @@ fn main() { }; // If we have write access, increment each value once - if filtered_entity.access().has_write(id) { + if filtered_entity.access().has_component_write(id) { data.iter_mut().for_each(|data| { *data += 1; }); From c59ffa1bdcd322de13a1ecd4a3c983d5903e111d Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Wed, 31 Jul 2024 12:59:57 -0400 Subject: [PATCH 2/6] fix resource vs component access --- crates/bevy_ecs/src/lib.rs | 4 +- crates/bevy_ecs/src/query/access.rs | 116 ++++++++++++++------- crates/bevy_ecs/src/query/builder.rs | 4 +- crates/bevy_ecs/src/query/fetch.rs | 16 +-- crates/bevy_ecs/src/query/filter.rs | 4 +- crates/bevy_ecs/src/schedule/mod.rs | 58 +++++++++++ crates/bevy_ecs/src/system/system_param.rs | 24 ++--- 7 files changed, 160 insertions(+), 66 deletions(-) diff --git a/crates/bevy_ecs/src/lib.rs b/crates/bevy_ecs/src/lib.rs index 406beb92d9593..98dc2bcfe3fe9 100644 --- a/crates/bevy_ecs/src/lib.rs +++ b/crates/bevy_ecs/src/lib.rs @@ -1391,8 +1391,8 @@ mod tests { let mut expected = FilteredAccess::::default(); let a_id = world.components.get_id(TypeId::of::()).unwrap(); let b_id = world.components.get_id(TypeId::of::()).unwrap(); - expected.add_write(a_id); - expected.add_read(b_id); + expected.add_component_write(a_id); + expected.add_component_read(b_id); assert!( query.component_access.eq(&expected), "ComponentId access from query fetch and query filter should be combined" diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 322d11cd56362..3b07b40d4bd29 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -143,8 +143,8 @@ impl Access { writes_all_components: false, component_read_and_writes: FixedBitSet::new(), component_writes: FixedBitSet::new(), - resource_read_and_writes: FixedBitSet::default(), - resource_writes: FixedBitSet::default(), + resource_read_and_writes: FixedBitSet::new(), + resource_writes: FixedBitSet::new(), archetypal: FixedBitSet::new(), marker: PhantomData, } @@ -225,7 +225,7 @@ impl Access { /// Returns `true` if this accesses any resource mutably. pub fn has_any_resource_write(&self) -> bool { - self.writes_all_resources || !self.component_writes.is_clear() + self.writes_all_resources || !self.resource_writes.is_clear() } /// Returns true if this has an archetypal (indirect) access to the element given by `index`. @@ -270,7 +270,7 @@ impl Access { #[inline] pub fn read_all(&mut self) { self.read_all_components(); - self.reads_all_resources(); + self.read_all_resources(); } /// Sets this as having mutable access to all indexed elements (i.e. `&mut World`). @@ -350,7 +350,7 @@ impl Access { /// /// [`Access`] instances are incompatible if one can write /// an element that the other can read or write. - pub fn is_compatible(&self, other: &Access) -> bool { + pub fn is_components_compatible(&self, other: &Access) -> bool { if self.writes_all_components { return !other.has_any_component_read(); } @@ -367,6 +367,15 @@ impl Access { return !self.has_any_component_write(); } + self.component_writes.is_disjoint(&other.component_read_and_writes) + && other.component_writes.is_disjoint(&self.component_read_and_writes) + } + + /// Returns `true` if the access and `other` can be active at the same time. + /// + /// [`Access`] instances are incompatible if one can write + /// an element that the other can read or write. + pub fn is_resources_compatible(&self, other: &Access) -> bool { if self.writes_all_resources { return !other.has_any_resource_read(); } @@ -383,12 +392,18 @@ impl Access { return !self.has_any_resource_write(); } - self.component_writes.is_disjoint(&other.component_read_and_writes) - && self.resource_writes.is_disjoint(&other.resource_read_and_writes) - && other.component_writes.is_disjoint(&self.component_read_and_writes) + self.resource_writes.is_disjoint(&other.resource_read_and_writes) && other.resource_writes.is_disjoint(&self.resource_read_and_writes) } + /// Returns `true` if the access and `other` can be active at the same time. + /// + /// [`Access`] instances are incompatible if one can write + /// an element that the other can read or write. + pub fn is_compatible(&self, other: &Access) -> bool { + self.is_components_compatible(other) && self.is_resources_compatible(other) + } + /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &Access) -> bool { @@ -438,20 +453,20 @@ impl Access { } if self.reads_all_resources { // QUESTION: How to handle `other.writes_all`? - conflicts.extend(other.component_writes.ones()); + conflicts.extend(other.resource_writes.ones()); } if other.reads_all_resources { // QUESTION: How to handle `self.writes_all`. - conflicts.extend(self.component_writes.ones()); + conflicts.extend(self.resource_writes.ones()); } if self.writes_all_resources { - conflicts.extend(other.component_read_and_writes.ones()); + conflicts.extend(other.resource_read_and_writes.ones()); } if other.writes_all_resources { - conflicts.extend(self.component_read_and_writes.ones()); + conflicts.extend(self.resource_read_and_writes.ones()); } conflicts.extend(self.component_writes.intersection(&other.component_read_and_writes)); @@ -587,20 +602,31 @@ impl FilteredAccess { &mut self.access } - /// Adds access to the element given by `index`. - pub fn add_read(&mut self, index: T) { + + /// Adds access to the component given by `index`. + pub fn add_component_read(&mut self, index: T) { self.access.add_component_read(index.clone()); self.add_required(index.clone()); self.and_with(index); } /// Adds exclusive access to the element given by `index`. - pub fn add_write(&mut self, index: T) { + pub fn add_component_write(&mut self, index: T) { self.access.add_component_write(index.clone()); self.add_required(index.clone()); self.and_with(index); } + /// Adds access to the resource given by `index`. + pub fn add_resource_read(&mut self, index: T) { + self.access.add_resource_read(index.clone()); + } + + /// Adds exclusive access to the resource given by `index`. + pub fn add_resource_write(&mut self, index: T) { + self.access.add_resource_write(index.clone()); + } + fn add_required(&mut self, index: T) { self.required.grow_and_insert(index.sparse_set_index()); } @@ -711,6 +737,16 @@ impl FilteredAccess { self.access.write_all(); } + /// Sets the underlying unfiltered access as having access to all components. + pub fn read_all_components(&mut self) { + self.access.read_all_components(); + } + + /// Sets the underlying unfiltered access as having mutable access to all components. + pub fn write_all_components(&mut self) { + self.access.write_all_components(); + } + /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &FilteredAccess) -> bool { @@ -879,16 +915,16 @@ impl FilteredAccessSet { } /// Adds a read access without filters to the set. - pub(crate) fn add_unfiltered_read(&mut self, index: T) { + pub(crate) fn add_unfiltered_resource_read(&mut self, index: T) { let mut filter = FilteredAccess::default(); - filter.add_read(index); + filter.add_resource_read(index); self.add(filter); } /// Adds a write access without filters to the set. - pub(crate) fn add_unfiltered_write(&mut self, index: T) { + pub(crate) fn add_unfiltered_resource_write(&mut self, index: T) { let mut filter = FilteredAccess::default(); - filter.add_write(index); + filter.add_resource_write(index); self.add(filter); } @@ -948,8 +984,8 @@ mod tests { fn create_sample_filtered_access() -> FilteredAccess { let mut filtered_access = FilteredAccess::::default(); - filtered_access.add_write(1); - filtered_access.add_read(2); + filtered_access.add_component_write(1); + filtered_access.add_component_read(2); filtered_access.add_required(3); filtered_access.and_with(4); @@ -968,8 +1004,8 @@ mod tests { fn create_sample_filtered_access_set() -> FilteredAccessSet { let mut filtered_access_set = FilteredAccessSet::::default(); - filtered_access_set.add_unfiltered_read(2); - filtered_access_set.add_unfiltered_write(4); + filtered_access_set.add_unfiltered_resource_read(2); + filtered_access_set.add_unfiltered_resource_write(4); filtered_access_set.read_all(); filtered_access_set @@ -1011,8 +1047,8 @@ mod tests { let original: FilteredAccess = create_sample_filtered_access(); let mut cloned = FilteredAccess::::default(); - cloned.add_write(7); - cloned.add_read(4); + cloned.add_component_write(7); + cloned.add_component_read(4); cloned.append_or(&FilteredAccess::default()); cloned.clone_from(&original); @@ -1054,8 +1090,8 @@ mod tests { let original: FilteredAccessSet = create_sample_filtered_access_set(); let mut cloned = FilteredAccessSet::::default(); - cloned.add_unfiltered_read(7); - cloned.add_unfiltered_write(9); + cloned.add_unfiltered_resource_read(7); + cloned.add_unfiltered_resource_write(9); cloned.write_all(); cloned.clone_from(&original); @@ -1114,10 +1150,10 @@ mod tests { #[test] fn filtered_combined_access() { let mut access_a = FilteredAccessSet::::default(); - access_a.add_unfiltered_read(1); + access_a.add_unfiltered_resource_read(1); let mut filter_b = FilteredAccess::::default(); - filter_b.add_write(1); + filter_b.add_resource_write(1); let conflicts = access_a.get_conflicts_single(&filter_b); assert_eq!( @@ -1130,22 +1166,22 @@ mod tests { #[test] fn filtered_access_extend() { let mut access_a = FilteredAccess::::default(); - access_a.add_read(0); - access_a.add_read(1); + access_a.add_component_read(0); + access_a.add_component_read(1); access_a.and_with(2); let mut access_b = FilteredAccess::::default(); - access_b.add_read(0); - access_b.add_write(3); + access_b.add_component_read(0); + access_b.add_component_write(3); access_b.and_without(4); access_a.extend(&access_b); let mut expected = FilteredAccess::::default(); - expected.add_read(0); - expected.add_read(1); + expected.add_component_read(0); + expected.add_component_read(1); expected.and_with(2); - expected.add_write(3); + expected.add_component_write(3); expected.and_without(4); assert!(access_a.eq(&expected)); @@ -1155,8 +1191,8 @@ mod tests { fn filtered_access_extend_or() { let mut access_a = FilteredAccess::::default(); // Exclusive access to `(&mut A, &mut B)`. - access_a.add_write(0); - access_a.add_write(1); + access_a.add_component_write(0); + access_a.add_component_write(1); // Filter by `With`. let mut access_b = FilteredAccess::::default(); @@ -1177,8 +1213,8 @@ mod tests { // The intention here is to test that exclusive access implied by `add_write` // forms correct normalized access structs when extended with `Or` filters. let mut expected = FilteredAccess::::default(); - expected.add_write(0); - expected.add_write(1); + expected.add_component_write(0); + expected.add_component_write(1); // The resulted access is expected to represent `Or<((With, With, With), (With, With, With, Without))>`. expected.filter_sets = vec![ AccessFilters { diff --git a/crates/bevy_ecs/src/query/builder.rs b/crates/bevy_ecs/src/query/builder.rs index 101371d00400f..3076b5d54e090 100644 --- a/crates/bevy_ecs/src/query/builder.rs +++ b/crates/bevy_ecs/src/query/builder.rs @@ -142,14 +142,14 @@ impl<'w, D: QueryData, F: QueryFilter> QueryBuilder<'w, D, F> { /// Adds `&T` to the [`FilteredAccess`] of self. pub fn ref_id(&mut self, id: ComponentId) -> &mut Self { self.with_id(id); - self.access.add_read(id); + self.access.add_component_read(id); self } /// Adds `&mut T` to the [`FilteredAccess`] of self. pub fn mut_id(&mut self, id: ComponentId) -> &mut Self { self.with_id(id); - self.access.add_write(id); + self.access.add_component_write(id); self } diff --git a/crates/bevy_ecs/src/query/fetch.rs b/crates/bevy_ecs/src/query/fetch.rs index 0d57bf6503678..7e9dd2f9c4f02 100644 --- a/crates/bevy_ecs/src/query/fetch.rs +++ b/crates/bevy_ecs/src/query/fetch.rs @@ -483,7 +483,7 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> { !access.access().has_any_component_write(), "EntityRef conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", ); - access.read_all(); + access.read_all_components(); } fn init_state(_world: &mut World) {} @@ -559,7 +559,7 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> { !access.access().has_any_component_read(), "EntityMut conflicts with a previous access in this query. Exclusive access cannot coincide with any other accesses.", ); - access.write_all(); + access.write_all_components(); } fn init_state(_world: &mut World) {} @@ -600,7 +600,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { _this_run: Tick, ) -> Self::Fetch<'w> { let mut access = Access::default(); - access.read_all(); + access.read_all_components(); (world, access) } @@ -703,7 +703,7 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { _this_run: Tick, ) -> Self::Fetch<'w> { let mut access = Access::default(); - access.write_all(); + access.write_all_components(); (world, access) } @@ -992,7 +992,7 @@ unsafe impl WorldQuery for &T { "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", std::any::type_name::(), ); - access.add_read(component_id); + access.add_component_read(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1187,7 +1187,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> { "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.", std::any::type_name::(), ); - access.add_read(component_id); + access.add_component_read(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1382,7 +1382,7 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T { "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.", std::any::type_name::(), ); - access.add_write(component_id); + access.add_component_write(component_id); } fn init_state(world: &mut World) -> ComponentId { @@ -1480,7 +1480,7 @@ unsafe impl<'__w, T: Component> WorldQuery for Mut<'__w, T> { "Mut<{}> conflicts with a previous access in this query. Mutable component access mut be unique.", std::any::type_name::(), ); - access.add_write(component_id); + access.add_component_write(component_id); } // Forwarded to `&mut T` diff --git a/crates/bevy_ecs/src/query/filter.rs b/crates/bevy_ecs/src/query/filter.rs index 029ecd5212749..f7f9617d51434 100644 --- a/crates/bevy_ecs/src/query/filter.rs +++ b/crates/bevy_ecs/src/query/filter.rs @@ -691,7 +691,7 @@ unsafe impl WorldQuery for Added { if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } - access.add_read(id); + access.add_component_read(id); } fn init_state(world: &mut World) -> ComponentId { @@ -902,7 +902,7 @@ unsafe impl WorldQuery for Changed { if access.access().has_component_write(id) { panic!("$state_name<{}> conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",std::any::type_name::()); } - access.add_read(id); + access.add_component_read(id); } fn init_state(world: &mut World) -> ComponentId { diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index 184bef250a6b5..b79c5d603d894 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -746,6 +746,10 @@ mod tests { fn write_component_system(_query: Query<&mut A>) {} fn with_filtered_component_system(_query: Query<&mut A, With>) {} fn without_filtered_component_system(_query: Query<&mut A, Without>) {} + + fn entity_ref_system(_query: Query) {} + + fn entity_mut_system(_query: Query) {} fn event_reader_system(_reader: EventReader) {} fn event_writer_system(_writer: EventWriter) {} fn event_resource_system(_events: ResMut>) {} @@ -788,6 +792,8 @@ mod tests { nonsend_system, read_component_system, read_component_system, + entity_ref_system, + entity_ref_system, event_reader_system, event_reader_system, read_world_system, @@ -893,6 +899,58 @@ mod tests { assert_eq!(schedule.graph().conflicting_systems().len(), 3); } + #[test] + fn resource_mut_and_entity_ref() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((resmut_system, entity_ref_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + + #[test] + fn resource_and_entity_mut() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((res_system, nonsend_system, entity_mut_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + + #[test] + fn write_component_and_entity_ref() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((write_component_system, entity_ref_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 1); + } + + #[test] + fn read_component_and_entity_mut() { + let mut world = World::new(); + world.insert_resource(R); + + let mut schedule = Schedule::default(); + schedule.add_systems((read_component_system, entity_mut_system)); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 1); + } + #[test] fn exclusive() { let mut world = World::new(); diff --git a/crates/bevy_ecs/src/system/system_param.rs b/crates/bevy_ecs/src/system/system_param.rs index 9aa2dc5922f42..1ae9f9a0ae00b 100644 --- a/crates/bevy_ecs/src/system/system_param.rs +++ b/crates/bevy_ecs/src/system/system_param.rs @@ -504,21 +504,21 @@ unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_component_write(component_id), + !combined_access.has_resource_write(component_id), "error[B0002]: Res<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, ); system_meta .component_access_set - .add_unfiltered_read(component_id); + .add_unfiltered_resource_read(component_id); let archetype_component_id = world .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_component_read(archetype_component_id); + .add_resource_read(archetype_component_id); component_id } @@ -600,25 +600,25 @@ unsafe impl<'a, T: Resource> SystemParam for ResMut<'a, T> { world.initialize_resource_internal(component_id); let combined_access = system_meta.component_access_set.combined_access(); - if combined_access.has_component_write(component_id) { + if combined_access.has_resource_write(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous ResMut<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); - } else if combined_access.has_component_read(component_id) { + } else if combined_access.has_resource_read(component_id) { panic!( "error[B0002]: ResMut<{}> in system {} conflicts with a previous Res<{0}> access. Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name); } system_meta .component_access_set - .add_unfiltered_write(component_id); + .add_unfiltered_resource_write(component_id); let archetype_component_id = world .get_resource_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_component_write(archetype_component_id); + .add_resource_write(archetype_component_id); component_id } @@ -1153,21 +1153,21 @@ unsafe impl<'a, T: 'static> SystemParam for NonSend<'a, T> { let combined_access = system_meta.component_access_set.combined_access(); assert!( - !combined_access.has_component_write(component_id), + !combined_access.has_resource_write(component_id), "error[B0002]: NonSend<{}> in system {} conflicts with a previous mutable resource access ({0}). Consider removing the duplicate access. See: https://bevyengine.org/learn/errors/b0002", std::any::type_name::(), system_meta.name, ); system_meta .component_access_set - .add_unfiltered_read(component_id); + .add_unfiltered_resource_read(component_id); let archetype_component_id = world .get_non_send_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_component_read(archetype_component_id); + .add_resource_read(archetype_component_id); component_id } @@ -1257,14 +1257,14 @@ unsafe impl<'a, T: 'static> SystemParam for NonSendMut<'a, T> { } system_meta .component_access_set - .add_unfiltered_write(component_id); + .add_unfiltered_resource_write(component_id); let archetype_component_id = world .get_non_send_archetype_component_id(component_id) .unwrap(); system_meta .archetype_component_access - .add_component_write(archetype_component_id); + .add_resource_write(archetype_component_id); component_id } From 34bb10a26c5092a0df2658e854aaf5d7b6d6dc86 Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Wed, 31 Jul 2024 13:36:01 -0400 Subject: [PATCH 3/6] cargo fmt --- crates/bevy_ecs/src/query/access.rs | 97 +++++++++++++++++++++-------- crates/bevy_ecs/src/query/state.rs | 26 +++++--- 2 files changed, 86 insertions(+), 37 deletions(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index 3b07b40d4bd29..bc724ef126a7f 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -74,7 +74,6 @@ pub struct Access { marker: PhantomData, } - // This is needed since `#[derive(Clone)]` does not generate optimized `clone_from`. impl Clone for Access { fn clone(&self) -> Self { @@ -93,9 +92,11 @@ impl Clone for Access { } fn clone_from(&mut self, source: &Self) { - self.component_read_and_writes.clone_from(&source.component_read_and_writes); + self.component_read_and_writes + .clone_from(&source.component_read_and_writes); self.component_writes.clone_from(&source.component_writes); - self.resource_read_and_writes.clone_from(&source.resource_read_and_writes); + self.resource_read_and_writes + .clone_from(&source.resource_read_and_writes); self.resource_writes.clone_from(&source.resource_writes); self.reads_all_components = source.reads_all_components; self.writes_all_components = source.writes_all_components; @@ -112,12 +113,18 @@ impl fmt::Debug for Access { "component_read_and_writes", &FormattedBitSet::::new(&self.component_read_and_writes), ) - .field("component_writes", &FormattedBitSet::::new(&self.component_writes)) + .field( + "component_writes", + &FormattedBitSet::::new(&self.component_writes), + ) .field( "resource_read_and_writes", &FormattedBitSet::::new(&self.resource_read_and_writes), ) - .field("resource_writes", &FormattedBitSet::::new(&self.resource_writes)) + .field( + "resource_writes", + &FormattedBitSet::::new(&self.resource_writes), + ) .field("reads_all_components", &self.reads_all_components) .field("writes_all_components", &self.writes_all_components) .field("reads_all_resources", &self.reads_all_resources) @@ -160,7 +167,8 @@ impl Access { pub fn add_component_write(&mut self, index: T) { self.component_read_and_writes .grow_and_insert(index.sparse_set_index()); - self.component_writes.grow_and_insert(index.sparse_set_index()); + self.component_writes + .grow_and_insert(index.sparse_set_index()); } /// Adds access to the resource given by `index`. @@ -173,7 +181,8 @@ impl Access { pub fn add_resource_write(&mut self, index: T) { self.resource_read_and_writes .grow_and_insert(index.sparse_set_index()); - self.resource_writes.grow_and_insert(index.sparse_set_index()); + self.resource_writes + .grow_and_insert(index.sparse_set_index()); } /// Adds an archetypal (indirect) access to the element given by `index`. @@ -190,7 +199,10 @@ impl Access { /// Returns `true` if this can access the component given by `index`. pub fn has_component_read(&self, index: T) -> bool { - self.reads_all_components || self.component_read_and_writes.contains(index.sparse_set_index()) + self.reads_all_components + || self + .component_read_and_writes + .contains(index.sparse_set_index()) } /// Returns `true` if this can access any component. @@ -210,7 +222,10 @@ impl Access { /// Returns `true` if this can access the resource given by `index`. pub fn has_resource_read(&self, index: T) -> bool { - self.reads_all_resources || self.resource_read_and_writes.contains(index.sparse_set_index()) + self.reads_all_resources + || self + .resource_read_and_writes + .contains(index.sparse_set_index()) } /// Returns `true` if this can access any resource. @@ -340,9 +355,11 @@ impl Access { self.writes_all_resources = self.writes_all_resources || other.writes_all_resources; self.reads_all_components = self.reads_all_components || other.reads_all_components; self.writes_all_components = self.writes_all_components || other.writes_all_components; - self.component_read_and_writes.union_with(&other.component_read_and_writes); + self.component_read_and_writes + .union_with(&other.component_read_and_writes); self.component_writes.union_with(&other.component_writes); - self.resource_read_and_writes.union_with(&other.resource_read_and_writes); + self.resource_read_and_writes + .union_with(&other.resource_read_and_writes); self.resource_writes.union_with(&other.resource_writes); } @@ -367,8 +384,11 @@ impl Access { return !self.has_any_component_write(); } - self.component_writes.is_disjoint(&other.component_read_and_writes) - && other.component_writes.is_disjoint(&self.component_read_and_writes) + self.component_writes + .is_disjoint(&other.component_read_and_writes) + && other + .component_writes + .is_disjoint(&self.component_read_and_writes) } /// Returns `true` if the access and `other` can be active at the same time. @@ -392,8 +412,11 @@ impl Access { return !self.has_any_resource_write(); } - self.resource_writes.is_disjoint(&other.resource_read_and_writes) - && other.resource_writes.is_disjoint(&self.resource_read_and_writes) + self.resource_writes + .is_disjoint(&other.resource_read_and_writes) + && other + .resource_writes + .is_disjoint(&self.resource_read_and_writes) } /// Returns `true` if the access and `other` can be active at the same time. @@ -407,11 +430,10 @@ impl Access { /// Returns `true` if the set is a subset of another, i.e. `other` contains /// at least all the values in `self`. pub fn is_subset(&self, other: &Access) -> bool { - if other.writes_all_components && other.reads_all_components { return true; } - + if self.writes_all_components && !other.writes_all_components { return false; } @@ -425,10 +447,18 @@ impl Access { return false; } - (other.reads_all_components || self.component_read_and_writes.is_subset(&other.component_read_and_writes)) - && (other.writes_all_components || self.component_writes.is_subset(&other.component_writes)) - && (other.reads_all_resources || self.resource_read_and_writes.is_subset(&other.resource_read_and_writes)) - && (other.writes_all_resources || self.resource_writes.is_subset(&other.resource_writes)) + (other.reads_all_components + || self + .component_read_and_writes + .is_subset(&other.component_read_and_writes)) + && (other.writes_all_components + || self.component_writes.is_subset(&other.component_writes)) + && (other.reads_all_resources + || self + .resource_read_and_writes + .is_subset(&other.resource_read_and_writes)) + && (other.writes_all_resources + || self.resource_writes.is_subset(&other.resource_writes)) } /// Returns a vector of elements that the access and `other` cannot access at the same time. @@ -469,10 +499,22 @@ impl Access { conflicts.extend(self.resource_read_and_writes.ones()); } - conflicts.extend(self.component_writes.intersection(&other.component_read_and_writes)); - conflicts.extend(self.component_read_and_writes.intersection(&other.component_writes)); - conflicts.extend(self.resource_writes.intersection(&other.resource_read_and_writes)); - conflicts.extend(self.resource_read_and_writes.intersection(&other.resource_writes)); + conflicts.extend( + self.component_writes + .intersection(&other.component_read_and_writes), + ); + conflicts.extend( + self.component_read_and_writes + .intersection(&other.component_writes), + ); + conflicts.extend( + self.resource_writes + .intersection(&other.resource_read_and_writes), + ); + conflicts.extend( + self.resource_read_and_writes + .intersection(&other.resource_writes), + ); conflicts .ones() .map(SparseSetIndex::get_sparse_set_index) @@ -481,7 +523,9 @@ impl Access { /// Returns the indices of the components this has access to. pub fn component_reads_and_writes(&self) -> impl Iterator + '_ { - self.component_read_and_writes.ones().map(T::get_sparse_set_index) + self.component_read_and_writes + .ones() + .map(T::get_sparse_set_index) } /// Returns the indices of the components this has non-exclusive access to. @@ -602,7 +646,6 @@ impl FilteredAccess { &mut self.access } - /// Adds access to the component given by `index`. pub fn add_component_read(&mut self, index: T) { self.access.add_component_read(index.clone()); diff --git a/crates/bevy_ecs/src/query/state.rs b/crates/bevy_ecs/src/query/state.rs index cda184c202877..3f40e0234edd3 100644 --- a/crates/bevy_ecs/src/query/state.rs +++ b/crates/bevy_ecs/src/query/state.rs @@ -456,16 +456,22 @@ impl QueryState { archetype: &Archetype, access: &mut Access, ) { - self.component_access.access.component_reads().for_each(|id| { - if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_component_read(id); - } - }); - self.component_access.access.component_writes().for_each(|id| { - if let Some(id) = archetype.get_archetype_component_id(id) { - access.add_component_write(id); - } - }); + self.component_access + .access + .component_reads() + .for_each(|id| { + if let Some(id) = archetype.get_archetype_component_id(id) { + access.add_component_read(id); + } + }); + self.component_access + .access + .component_writes() + .for_each(|id| { + if let Some(id) = archetype.get_archetype_component_id(id) { + access.add_component_write(id); + } + }); } /// Use this to transform a [`QueryState`] into a more generic [`QueryState`]. From c63f893d8b292594c4c13d503d25948ff4fa561d Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Wed, 31 Jul 2024 15:11:32 -0400 Subject: [PATCH 4/6] update docs --- crates/bevy_ecs/src/query/access.rs | 98 ++++++++++++++++++----------- 1 file changed, 60 insertions(+), 38 deletions(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index bc724ef126a7f..d900111e1e846 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -63,13 +63,13 @@ pub struct Access { /// Is `true` if this has mutable access to all components. /// (Note that this does not include `Resources`) writes_all_components: bool, - /// Is `true` if this has access to all elements in the collection. + /// Is `true` if this has access to all resources. /// This field is a performance optimization for `&World` (also harder to mess up for soundness). reads_all_resources: bool, - /// Is `true` if this has mutable access to all elements in the collection. + /// Is `true` if this has mutable access to all resources. /// If this is true, then `reads_all` must also be true. writes_all_resources: bool, - // Elements that are not accessed, but whose presence in an archetype affect query results. + // Components that are not accessed, but whose presence in an archetype affect query results. archetypal: FixedBitSet, marker: PhantomData, } @@ -185,9 +185,9 @@ impl Access { .grow_and_insert(index.sparse_set_index()); } - /// Adds an archetypal (indirect) access to the element given by `index`. + /// Adds an archetypal (indirect) access to the component given by `index`. /// - /// This is for elements whose values are not accessed (and thus will never cause conflicts), + /// This is for components whose values are not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -215,7 +215,7 @@ impl Access { self.writes_all_components || self.component_writes.contains(index.sparse_set_index()) } - /// Returns `true` if this accesses anything mutably. + /// Returns `true` if this accesses any component mutably. pub fn has_any_component_write(&self) -> bool { self.writes_all_components || !self.component_writes.is_clear() } @@ -243,9 +243,9 @@ impl Access { self.writes_all_resources || !self.resource_writes.is_clear() } - /// Returns true if this has an archetypal (indirect) access to the element given by `index`. + /// Returns true if this has an archetypal (indirect) access to the component given by `index`. /// - /// This is an element whose value is not accessed (and thus will never cause conflicts), + /// This is a component whose value is not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -363,7 +363,8 @@ impl Access { self.resource_writes.union_with(&other.resource_writes); } - /// Returns `true` if the access and `other` can be active at the same time. + /// Returns `true` if the access and `other` can be active at the same time, + /// only looking at their component access. /// /// [`Access`] instances are incompatible if one can write /// an element that the other can read or write. @@ -391,7 +392,8 @@ impl Access { .is_disjoint(&self.component_read_and_writes) } - /// Returns `true` if the access and `other` can be active at the same time. + /// Returns `true` if the access and `other` can be active at the same time, + /// only looking at their resource access. /// /// [`Access`] instances are incompatible if one can write /// an element that the other can read or write. @@ -427,38 +429,58 @@ impl Access { self.is_components_compatible(other) && self.is_resources_compatible(other) } - /// Returns `true` if the set is a subset of another, i.e. `other` contains - /// at least all the values in `self`. - pub fn is_subset(&self, other: &Access) -> bool { - if other.writes_all_components && other.reads_all_components { + /// Returns `true` if the set's component access is a subset of another, i.e. `other`'s component access + /// contains at least all the values in `self`. + pub fn is_subset_components(&self, other: &Access) -> bool { + if self.writes_all_components { + return other.writes_all_components; + } + + if other.writes_all_components { return true; } - if self.writes_all_components && !other.writes_all_components { - return false; + if self.reads_all_components { + return other.reads_all_components; + } + + if other.reads_all_components { + return self.component_writes.is_subset(&other.component_writes); + } + + self.component_read_and_writes + .is_subset(&other.component_read_and_writes) + && self.component_writes.is_subset(&other.component_writes) + } + + /// Returns `true` if the set's resource access is a subset of another, i.e. `other`'s resource access + /// contains at least all the values in `self`. + pub fn is_subset_resources(&self, other: &Access) -> bool { + if self.writes_all_resources { + return other.writes_all_resources; } - if self.reads_all_components && !other.reads_all_components { - return false; + + if other.writes_all_resources { + return true; } - if self.writes_all_resources && !other.writes_all_resources { - return false; + + if self.reads_all_resources { + return other.reads_all_resources; } - if self.reads_all_resources && !other.reads_all_resources { - return false; + + if other.reads_all_resources { + return self.resource_writes.is_subset(&other.resource_writes); } - (other.reads_all_components - || self - .component_read_and_writes - .is_subset(&other.component_read_and_writes)) - && (other.writes_all_components - || self.component_writes.is_subset(&other.component_writes)) - && (other.reads_all_resources - || self - .resource_read_and_writes - .is_subset(&other.resource_read_and_writes)) - && (other.writes_all_resources - || self.resource_writes.is_subset(&other.resource_writes)) + self.resource_read_and_writes + .is_subset(&other.resource_read_and_writes) + && self.resource_writes.is_subset(&other.resource_writes) + } + + /// Returns `true` if the set is a subset of another, i.e. `other` contains + /// at least all the values in `self`. + pub fn is_subset(&self, other: &Access) -> bool { + self.is_subset_components(other) && self.is_subset_resources(other) } /// Returns a vector of elements that the access and `other` cannot access at the same time. @@ -540,9 +562,9 @@ impl Access { self.component_writes.ones().map(T::get_sparse_set_index) } - /// Returns the indices of the elements that this has an archetypal access to. + /// Returns the indices of the components that this has an archetypal access to. /// - /// These are elements whose values are not accessed (and thus will never cause conflicts), + /// These are components whose values are not accessed (and thus will never cause conflicts), /// but whose presence in an archetype may affect query results. /// /// Currently, this is only used for [`Has`]. @@ -957,14 +979,14 @@ impl FilteredAccessSet { self.filtered_accesses.push(filtered_access); } - /// Adds a read access without filters to the set. + /// Adds a read access to a resource to the set. pub(crate) fn add_unfiltered_resource_read(&mut self, index: T) { let mut filter = FilteredAccess::default(); filter.add_resource_read(index); self.add(filter); } - /// Adds a write access without filters to the set. + /// Adds a write access to a resource to the set. pub(crate) fn add_unfiltered_resource_write(&mut self, index: T) { let mut filter = FilteredAccess::default(); filter.add_resource_write(index); From 8563cb342da659a4cc4714814dad9c4a4d887e17 Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Wed, 31 Jul 2024 15:14:19 -0400 Subject: [PATCH 5/6] update doc --- crates/bevy_ecs/src/query/access.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_ecs/src/query/access.rs b/crates/bevy_ecs/src/query/access.rs index d900111e1e846..07fb201a5b979 100644 --- a/crates/bevy_ecs/src/query/access.rs +++ b/crates/bevy_ecs/src/query/access.rs @@ -675,7 +675,7 @@ impl FilteredAccess { self.and_with(index); } - /// Adds exclusive access to the element given by `index`. + /// Adds exclusive access to the component given by `index`. pub fn add_component_write(&mut self, index: T) { self.access.add_component_write(index.clone()); self.add_required(index.clone()); From daf6981215289dc3f87254712f3d3cb8b0ccdc50 Mon Sep 17 00:00:00 2001 From: cBournhonesque Date: Thu, 1 Aug 2024 13:43:17 -0400 Subject: [PATCH 6/6] add test for a resource that is also a component --- crates/bevy_ecs/src/schedule/mod.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/bevy_ecs/src/schedule/mod.rs b/crates/bevy_ecs/src/schedule/mod.rs index b79c5d603d894..28aa65c59d2b9 100644 --- a/crates/bevy_ecs/src/schedule/mod.rs +++ b/crates/bevy_ecs/src/schedule/mod.rs @@ -737,6 +737,9 @@ mod tests { #[derive(Event)] struct E; + #[derive(Resource, Component)] + struct RC; + fn empty_system() {} fn res_system(_res: Res) {} fn resmut_system(_res: ResMut) {} @@ -746,9 +749,7 @@ mod tests { fn write_component_system(_query: Query<&mut A>) {} fn with_filtered_component_system(_query: Query<&mut A, With>) {} fn without_filtered_component_system(_query: Query<&mut A, Without>) {} - fn entity_ref_system(_query: Query) {} - fn entity_mut_system(_query: Query) {} fn event_reader_system(_reader: EventReader) {} fn event_writer_system(_writer: EventWriter) {} @@ -899,6 +900,21 @@ mod tests { assert_eq!(schedule.graph().conflicting_systems().len(), 3); } + /// Test that when a struct is both a Resource and a Component, they do not + /// conflict with each other. + #[test] + fn shared_resource_mut_component() { + let mut world = World::new(); + world.insert_resource(RC); + + let mut schedule = Schedule::default(); + schedule.add_systems((|_: ResMut| {}, |_: Query<&mut RC>| {})); + + let _ = schedule.initialize(&mut world); + + assert_eq!(schedule.graph().conflicting_systems().len(), 0); + } + #[test] fn resource_mut_and_entity_ref() { let mut world = World::new();