Add EntityRefExcept and EntityMutExcept world queries, in preparation for generalized animation. (#15207)
				
					
				
			This commit adds two new `WorldQuery` types: `EntityRefExcept` and `EntityMutExcept`. These types work just like `EntityRef` and `EntityMut`, but they prevent access to a statically-specified list of components. For example, `EntityMutExcept<(AnimationPlayer, Handle<AnimationGraph>)>` provides mutable access to all components except for `AnimationPlayer` and `Handle<AnimationGraph>`. These types are useful when you need to be able to process arbitrary queries while iterating over the results of another `EntityMut` query. The motivating use case is *generalized animation*, which is an upcoming feature that allows animation of any component property, not just rotation, translation, scaling, or morph weights. To implement this, we must change the current `AnyOf<(&mut Transform, &mut MorphWeights)>` to instead be `EntityMutExcept<(AnimationPlayer, Handle<AnimationGraph>)>`. It's possible to use `FilteredEntityMut` in conjunction with a dynamically-generated system instead, but `FilteredEntityMut` isn't optimized for the use case of a large number of allowed components coupled with a small set of disallowed components. No amount of optimization of `FilteredEntityMut` produced acceptable performance on the `many_foxes` benchmark. `Query<EntityMut, Without<AnimationPlayer>>` will not suffice either, as it's legal and idiomatic for an `AnimationTarget` and an `AnimationPlayer` to coexist on the same entity. An alternate proposal was to implement a somewhat-more-general `Except<Q, CL>` feature, where Q is a `WorldQuery` and CL is a `ComponentList`. I wasn't able to implement that proposal in a reasonable way, because of the fact that methods like `EntityMut::get_mut` and `EntityRef::get` are inherent methods instead of methods on `WorldQuery`, and therefore there was no way to delegate methods like `get` and `get_mut` to the inner query in a generic way. Refactoring those methods into a trait would probably be possible. However, I didn't see a use case for a hypothetical `Except` with arbitrary queries: `Query<Except<(&Transform, &Visibility), Visibility>>` would just be a complicated equivalent to `Query<&Transform>`, for instance. So, out of a desire for simplicity, I omitted a generic `Except` mechanism. I've tested the performance of generalized animation on `many_foxes` and found that, with this patch, `animate_targets` has a 7.4% slowdown over `main`. With `FilteredEntityMut` optimized to use `Arc<Access>`, the slowdown is 75.6%, due to contention on the reference count. Without `Arc<Access>`, the slowdown is even worse, over 2x. ## Testing New tests have been added that check that `EntityRefExcept` and `EntityMutExcept` allow and disallow access to components properly and that the query engine can correctly reject conflicting queries involving those types. A Tracy profile of `many_foxes` with 10,000 foxes showing generalized animation using `FilteredEntityMut` (red) vs. main (yellow) is as follows:  A Tracy profile of `many_foxes` with 10,000 foxes showing generalized animation using this `EntityMutExcept` (yellow) vs. main (red) is as follows: 
This commit is contained in:
		
							parent
							
								
									b45d83ebda
								
							
						
					
					
						commit
						3c41586154
					
				| @ -34,6 +34,7 @@ serde = { version = "1", optional = true, default-features = false } | |||||||
| thiserror = "1.0" | thiserror = "1.0" | ||||||
| nonmax = "0.5" | nonmax = "0.5" | ||||||
| arrayvec = { version = "0.7.4", optional = true } | arrayvec = { version = "0.7.4", optional = true } | ||||||
|  | smallvec = "1" | ||||||
| 
 | 
 | ||||||
| [dev-dependencies] | [dev-dependencies] | ||||||
| rand = "0.8" | rand = "0.8" | ||||||
|  | |||||||
| @ -499,6 +499,16 @@ impl Archetype { | |||||||
|         self.components.len() |         self.components.len() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Gets an iterator of all of the components in the archetype, along with
 | ||||||
|  |     /// their archetype component ID.
 | ||||||
|  |     pub(crate) fn components_with_archetype_component_id( | ||||||
|  |         &self, | ||||||
|  |     ) -> impl Iterator<Item = (ComponentId, ArchetypeComponentId)> + '_ { | ||||||
|  |         self.components | ||||||
|  |             .iter() | ||||||
|  |             .map(|(component_id, info)| (*component_id, info.archetype_component_id)) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Fetches an immutable reference to the archetype's [`Edges`], a cache of
 |     /// Fetches an immutable reference to the archetype's [`Edges`], a cache of
 | ||||||
|     /// archetypal relationships.
 |     /// archetypal relationships.
 | ||||||
|     #[inline] |     #[inline] | ||||||
|  | |||||||
| @ -49,20 +49,22 @@ impl<'a, T: SparseSetIndex + Debug> Debug for FormattedBitSet<'a, T> { | |||||||
| /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions.
 | /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions.
 | ||||||
| #[derive(Eq, PartialEq)] | #[derive(Eq, PartialEq)] | ||||||
| pub struct Access<T: SparseSetIndex> { | pub struct Access<T: SparseSetIndex> { | ||||||
|     /// All accessed components.
 |     /// All accessed components, or forbidden components if
 | ||||||
|  |     /// `Self::component_read_and_writes_inverted` is set.
 | ||||||
|     component_read_and_writes: FixedBitSet, |     component_read_and_writes: FixedBitSet, | ||||||
|     /// The exclusively-accessed components.
 |     /// All exclusively-accessed components, or components that may not be
 | ||||||
|  |     /// exclusively accessed if `Self::component_writes_inverted` is set.
 | ||||||
|     component_writes: FixedBitSet, |     component_writes: FixedBitSet, | ||||||
|     /// All accessed resources.
 |     /// All accessed resources.
 | ||||||
|     resource_read_and_writes: FixedBitSet, |     resource_read_and_writes: FixedBitSet, | ||||||
|     /// The exclusively-accessed resources.
 |     /// The exclusively-accessed resources.
 | ||||||
|     resource_writes: FixedBitSet, |     resource_writes: FixedBitSet, | ||||||
|     /// Is `true` if this has access to all components.
 |     /// Is `true` if this component can read all components *except* those
 | ||||||
|     /// (Note that this does not include `Resources`)
 |     /// present in `Self::component_read_and_writes`.
 | ||||||
|     reads_all_components: bool, |     component_read_and_writes_inverted: bool, | ||||||
|     /// Is `true` if this has mutable access to all components.
 |     /// Is `true` if this component can write to all components *except* those
 | ||||||
|     /// (Note that this does not include `Resources`)
 |     /// present in `Self::component_writes`.
 | ||||||
|     writes_all_components: bool, |     component_writes_inverted: bool, | ||||||
|     /// Is `true` if this has access to all resources.
 |     /// Is `true` if this has access to all resources.
 | ||||||
|     /// This field is a performance optimization for `&World` (also harder to mess up for soundness).
 |     /// This field is a performance optimization for `&World` (also harder to mess up for soundness).
 | ||||||
|     reads_all_resources: bool, |     reads_all_resources: bool, | ||||||
| @ -82,8 +84,8 @@ impl<T: SparseSetIndex> Clone for Access<T> { | |||||||
|             component_writes: self.component_writes.clone(), |             component_writes: self.component_writes.clone(), | ||||||
|             resource_read_and_writes: self.resource_read_and_writes.clone(), |             resource_read_and_writes: self.resource_read_and_writes.clone(), | ||||||
|             resource_writes: self.resource_writes.clone(), |             resource_writes: self.resource_writes.clone(), | ||||||
|             reads_all_components: self.reads_all_components, |             component_read_and_writes_inverted: self.component_read_and_writes_inverted, | ||||||
|             writes_all_components: self.writes_all_components, |             component_writes_inverted: self.component_writes_inverted, | ||||||
|             reads_all_resources: self.reads_all_resources, |             reads_all_resources: self.reads_all_resources, | ||||||
|             writes_all_resources: self.writes_all_resources, |             writes_all_resources: self.writes_all_resources, | ||||||
|             archetypal: self.archetypal.clone(), |             archetypal: self.archetypal.clone(), | ||||||
| @ -98,8 +100,8 @@ impl<T: SparseSetIndex> Clone for Access<T> { | |||||||
|         self.resource_read_and_writes |         self.resource_read_and_writes | ||||||
|             .clone_from(&source.resource_read_and_writes); |             .clone_from(&source.resource_read_and_writes); | ||||||
|         self.resource_writes.clone_from(&source.resource_writes); |         self.resource_writes.clone_from(&source.resource_writes); | ||||||
|         self.reads_all_components = source.reads_all_components; |         self.component_read_and_writes_inverted = source.component_read_and_writes_inverted; | ||||||
|         self.writes_all_components = source.writes_all_components; |         self.component_writes_inverted = source.component_writes_inverted; | ||||||
|         self.reads_all_resources = source.reads_all_resources; |         self.reads_all_resources = source.reads_all_resources; | ||||||
|         self.writes_all_resources = source.writes_all_resources; |         self.writes_all_resources = source.writes_all_resources; | ||||||
|         self.archetypal.clone_from(&source.archetypal); |         self.archetypal.clone_from(&source.archetypal); | ||||||
| @ -125,8 +127,11 @@ impl<T: SparseSetIndex + Debug> Debug for Access<T> { | |||||||
|                 "resource_writes", |                 "resource_writes", | ||||||
|                 &FormattedBitSet::<T>::new(&self.resource_writes), |                 &FormattedBitSet::<T>::new(&self.resource_writes), | ||||||
|             ) |             ) | ||||||
|             .field("reads_all_components", &self.reads_all_components) |             .field( | ||||||
|             .field("writes_all_components", &self.writes_all_components) |                 "component_read_and_writes_inverted", | ||||||
|  |                 &self.component_read_and_writes_inverted, | ||||||
|  |             ) | ||||||
|  |             .field("component_writes_inverted", &self.component_writes_inverted) | ||||||
|             .field("reads_all_resources", &self.reads_all_resources) |             .field("reads_all_resources", &self.reads_all_resources) | ||||||
|             .field("writes_all_resources", &self.writes_all_resources) |             .field("writes_all_resources", &self.writes_all_resources) | ||||||
|             .field("archetypal", &FormattedBitSet::<T>::new(&self.archetypal)) |             .field("archetypal", &FormattedBitSet::<T>::new(&self.archetypal)) | ||||||
| @ -146,8 +151,8 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|         Self { |         Self { | ||||||
|             reads_all_resources: false, |             reads_all_resources: false, | ||||||
|             writes_all_resources: false, |             writes_all_resources: false, | ||||||
|             reads_all_components: false, |             component_read_and_writes_inverted: false, | ||||||
|             writes_all_components: false, |             component_writes_inverted: false, | ||||||
|             component_read_and_writes: FixedBitSet::new(), |             component_read_and_writes: FixedBitSet::new(), | ||||||
|             component_writes: FixedBitSet::new(), |             component_writes: FixedBitSet::new(), | ||||||
|             resource_read_and_writes: FixedBitSet::new(), |             resource_read_and_writes: FixedBitSet::new(), | ||||||
| @ -157,18 +162,33 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn add_component_sparse_set_index_read(&mut self, index: usize) { | ||||||
|  |         if !self.component_read_and_writes_inverted { | ||||||
|  |             self.component_read_and_writes.grow_and_insert(index); | ||||||
|  |         } else if index < self.component_read_and_writes.len() { | ||||||
|  |             self.component_read_and_writes.remove(index); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn add_component_sparse_set_index_write(&mut self, index: usize) { | ||||||
|  |         if !self.component_writes_inverted { | ||||||
|  |             self.component_writes.grow_and_insert(index); | ||||||
|  |         } else if index < self.component_writes.len() { | ||||||
|  |             self.component_writes.remove(index); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Adds access to the component given by `index`.
 |     /// Adds access to the component given by `index`.
 | ||||||
|     pub fn add_component_read(&mut self, index: T) { |     pub fn add_component_read(&mut self, index: T) { | ||||||
|         self.component_read_and_writes |         let sparse_set_index = index.sparse_set_index(); | ||||||
|             .grow_and_insert(index.sparse_set_index()); |         self.add_component_sparse_set_index_read(sparse_set_index); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Adds exclusive access to the component given by `index`.
 |     /// Adds exclusive access to the component given by `index`.
 | ||||||
|     pub fn add_component_write(&mut self, index: T) { |     pub fn add_component_write(&mut self, index: T) { | ||||||
|         self.component_read_and_writes |         let sparse_set_index = index.sparse_set_index(); | ||||||
|             .grow_and_insert(index.sparse_set_index()); |         self.add_component_sparse_set_index_read(sparse_set_index); | ||||||
|         self.component_writes |         self.add_component_sparse_set_index_write(sparse_set_index); | ||||||
|             .grow_and_insert(index.sparse_set_index()); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Adds access to the resource given by `index`.
 |     /// Adds access to the resource given by `index`.
 | ||||||
| @ -185,6 +205,49 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|             .grow_and_insert(index.sparse_set_index()); |             .grow_and_insert(index.sparse_set_index()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn remove_component_sparse_set_index_read(&mut self, index: usize) { | ||||||
|  |         if self.component_read_and_writes_inverted { | ||||||
|  |             self.component_read_and_writes.grow_and_insert(index); | ||||||
|  |         } else if index < self.component_read_and_writes.len() { | ||||||
|  |             self.component_read_and_writes.remove(index); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn remove_component_sparse_set_index_write(&mut self, index: usize) { | ||||||
|  |         if self.component_writes_inverted { | ||||||
|  |             self.component_writes.grow_and_insert(index); | ||||||
|  |         } else if index < self.component_writes.len() { | ||||||
|  |             self.component_writes.remove(index); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Removes both read and write access to the component given by `index`.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Because this method corresponds to the set difference operator ∖, it can
 | ||||||
|  |     /// create complicated logical formulas that you should verify correctness
 | ||||||
|  |     /// of. For example, A ∪ (B ∖ A) isn't equivalent to (A ∪ B) ∖ A, so you
 | ||||||
|  |     /// can't replace a call to `remove_component_read` followed by a call to
 | ||||||
|  |     /// `extend` with a call to `extend` followed by a call to
 | ||||||
|  |     /// `remove_component_read`.
 | ||||||
|  |     pub fn remove_component_read(&mut self, index: T) { | ||||||
|  |         let sparse_set_index = index.sparse_set_index(); | ||||||
|  |         self.remove_component_sparse_set_index_write(sparse_set_index); | ||||||
|  |         self.remove_component_sparse_set_index_read(sparse_set_index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Removes write access to the component given by `index`.
 | ||||||
|  |     ///
 | ||||||
|  |     /// Because this method corresponds to the set difference operator ∖, it can
 | ||||||
|  |     /// create complicated logical formulas that you should verify correctness
 | ||||||
|  |     /// of. For example, A ∪ (B ∖ A) isn't equivalent to (A ∪ B) ∖ A, so you
 | ||||||
|  |     /// can't replace a call to `remove_component_write` followed by a call to
 | ||||||
|  |     /// `extend` with a call to `extend` followed by a call to
 | ||||||
|  |     /// `remove_component_write`.
 | ||||||
|  |     pub fn remove_component_write(&mut self, index: T) { | ||||||
|  |         let sparse_set_index = index.sparse_set_index(); | ||||||
|  |         self.remove_component_sparse_set_index_write(sparse_set_index); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Adds an archetypal (indirect) access to the component given by `index`.
 |     /// Adds an archetypal (indirect) access to the component given by `index`.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// This is for components 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),
 | ||||||
| @ -199,25 +262,25 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this can access the component given by `index`.
 |     /// Returns `true` if this can access the component given by `index`.
 | ||||||
|     pub fn has_component_read(&self, index: T) -> bool { |     pub fn has_component_read(&self, index: T) -> bool { | ||||||
|         self.reads_all_components |         self.component_read_and_writes_inverted | ||||||
|             || self |             ^ self | ||||||
|                 .component_read_and_writes |                 .component_read_and_writes | ||||||
|                 .contains(index.sparse_set_index()) |                 .contains(index.sparse_set_index()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this can access any component.
 |     /// Returns `true` if this can access any component.
 | ||||||
|     pub fn has_any_component_read(&self) -> bool { |     pub fn has_any_component_read(&self) -> bool { | ||||||
|         self.reads_all_components || !self.component_read_and_writes.is_clear() |         self.component_read_and_writes_inverted || !self.component_read_and_writes.is_clear() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this can exclusively access the component given by `index`.
 |     /// Returns `true` if this can exclusively access the component given by `index`.
 | ||||||
|     pub fn has_component_write(&self, index: T) -> bool { |     pub fn has_component_write(&self, index: T) -> bool { | ||||||
|         self.writes_all_components || self.component_writes.contains(index.sparse_set_index()) |         self.component_writes_inverted ^ self.component_writes.contains(index.sparse_set_index()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this accesses any component mutably.
 |     /// Returns `true` if this accesses any component mutably.
 | ||||||
|     pub fn has_any_component_write(&self) -> bool { |     pub fn has_any_component_write(&self) -> bool { | ||||||
|         self.writes_all_components || !self.component_writes.is_clear() |         self.component_writes_inverted || !self.component_writes.is_clear() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this can access the resource given by `index`.
 |     /// Returns `true` if this can access the resource given by `index`.
 | ||||||
| @ -258,14 +321,16 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     /// Sets this as having access to all components (i.e. `EntityRef`).
 |     /// Sets this as having access to all components (i.e. `EntityRef`).
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn read_all_components(&mut self) { |     pub fn read_all_components(&mut self) { | ||||||
|         self.reads_all_components = true; |         self.component_read_and_writes_inverted = true; | ||||||
|  |         self.component_read_and_writes.clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Sets this as having mutable access to all components (i.e. `EntityMut`).
 |     /// Sets this as having mutable access to all components (i.e. `EntityMut`).
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn write_all_components(&mut self) { |     pub fn write_all_components(&mut self) { | ||||||
|         self.reads_all_components = true; |         self.read_all_components(); | ||||||
|         self.writes_all_components = true; |         self.component_writes_inverted = true; | ||||||
|  |         self.component_writes.clear(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Sets this as having access to all resources (i.e. `&World`).
 |     /// Sets this as having access to all resources (i.e. `&World`).
 | ||||||
| @ -298,13 +363,13 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     /// Returns `true` if this has access to all components (i.e. `EntityRef`).
 |     /// Returns `true` if this has access to all components (i.e. `EntityRef`).
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn has_read_all_components(&self) -> bool { |     pub fn has_read_all_components(&self) -> bool { | ||||||
|         self.reads_all_components |         self.component_read_and_writes_inverted && self.component_read_and_writes.is_clear() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this has write access to all components (i.e. `EntityMut`).
 |     /// Returns `true` if this has write access to all components (i.e. `EntityMut`).
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn has_write_all_components(&self) -> bool { |     pub fn has_write_all_components(&self) -> bool { | ||||||
|         self.writes_all_components |         self.component_writes_inverted && self.component_writes.is_clear() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns `true` if this has access to all resources (i.e. `EntityRef`).
 |     /// Returns `true` if this has access to all resources (i.e. `EntityRef`).
 | ||||||
| @ -332,7 +397,7 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     /// Removes all writes.
 |     /// Removes all writes.
 | ||||||
|     pub fn clear_writes(&mut self) { |     pub fn clear_writes(&mut self) { | ||||||
|         self.writes_all_resources = false; |         self.writes_all_resources = false; | ||||||
|         self.writes_all_components = false; |         self.component_writes_inverted = false; | ||||||
|         self.component_writes.clear(); |         self.component_writes.clear(); | ||||||
|         self.resource_writes.clear(); |         self.resource_writes.clear(); | ||||||
|     } |     } | ||||||
| @ -341,8 +406,8 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     pub fn clear(&mut self) { |     pub fn clear(&mut self) { | ||||||
|         self.reads_all_resources = false; |         self.reads_all_resources = false; | ||||||
|         self.writes_all_resources = false; |         self.writes_all_resources = false; | ||||||
|         self.reads_all_components = false; |         self.component_read_and_writes_inverted = false; | ||||||
|         self.writes_all_components = false; |         self.component_writes_inverted = false; | ||||||
|         self.component_read_and_writes.clear(); |         self.component_read_and_writes.clear(); | ||||||
|         self.component_writes.clear(); |         self.component_writes.clear(); | ||||||
|         self.resource_read_and_writes.clear(); |         self.resource_read_and_writes.clear(); | ||||||
| @ -351,13 +416,72 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
| 
 | 
 | ||||||
|     /// Adds all access from `other`.
 |     /// Adds all access from `other`.
 | ||||||
|     pub fn extend(&mut self, other: &Access<T>) { |     pub fn extend(&mut self, other: &Access<T>) { | ||||||
|  |         let component_read_and_writes_inverted = | ||||||
|  |             self.component_read_and_writes_inverted || other.component_read_and_writes_inverted; | ||||||
|  |         let component_writes_inverted = | ||||||
|  |             self.component_writes_inverted || other.component_writes_inverted; | ||||||
|  | 
 | ||||||
|  |         match ( | ||||||
|  |             self.component_read_and_writes_inverted, | ||||||
|  |             other.component_read_and_writes_inverted, | ||||||
|  |         ) { | ||||||
|  |             (true, true) => { | ||||||
|  |                 self.component_read_and_writes | ||||||
|  |                     .intersect_with(&other.component_read_and_writes); | ||||||
|  |             } | ||||||
|  |             (true, false) => { | ||||||
|  |                 self.component_read_and_writes | ||||||
|  |                     .difference_with(&other.component_read_and_writes); | ||||||
|  |             } | ||||||
|  |             (false, true) => { | ||||||
|  |                 // We have to grow here because the new bits are going to get flipped to 1.
 | ||||||
|  |                 self.component_read_and_writes.grow( | ||||||
|  |                     self.component_read_and_writes | ||||||
|  |                         .len() | ||||||
|  |                         .max(other.component_read_and_writes.len()), | ||||||
|  |                 ); | ||||||
|  |                 self.component_read_and_writes.toggle_range(..); | ||||||
|  |                 self.component_read_and_writes | ||||||
|  |                     .intersect_with(&other.component_read_and_writes); | ||||||
|  |             } | ||||||
|  |             (false, false) => { | ||||||
|  |                 self.component_read_and_writes | ||||||
|  |                     .union_with(&other.component_read_and_writes); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         match ( | ||||||
|  |             self.component_writes_inverted, | ||||||
|  |             other.component_writes_inverted, | ||||||
|  |         ) { | ||||||
|  |             (true, true) => { | ||||||
|  |                 self.component_writes | ||||||
|  |                     .intersect_with(&other.component_writes); | ||||||
|  |             } | ||||||
|  |             (true, false) => { | ||||||
|  |                 self.component_writes | ||||||
|  |                     .difference_with(&other.component_writes); | ||||||
|  |             } | ||||||
|  |             (false, true) => { | ||||||
|  |                 // We have to grow here because the new bits are going to get flipped to 1.
 | ||||||
|  |                 self.component_writes.grow( | ||||||
|  |                     self.component_writes | ||||||
|  |                         .len() | ||||||
|  |                         .max(other.component_writes.len()), | ||||||
|  |                 ); | ||||||
|  |                 self.component_writes.toggle_range(..); | ||||||
|  |                 self.component_writes | ||||||
|  |                     .intersect_with(&other.component_writes); | ||||||
|  |             } | ||||||
|  |             (false, false) => { | ||||||
|  |                 self.component_writes.union_with(&other.component_writes); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         self.reads_all_resources = self.reads_all_resources || other.reads_all_resources; |         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.writes_all_resources = self.writes_all_resources || other.writes_all_resources; | ||||||
|         self.reads_all_components = self.reads_all_components || other.reads_all_components; |         self.component_read_and_writes_inverted = component_read_and_writes_inverted; | ||||||
|         self.writes_all_components = self.writes_all_components || other.writes_all_components; |         self.component_writes_inverted = component_writes_inverted; | ||||||
|         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 |         self.resource_read_and_writes | ||||||
|             .union_with(&other.resource_read_and_writes); |             .union_with(&other.resource_read_and_writes); | ||||||
|         self.resource_writes.union_with(&other.resource_writes); |         self.resource_writes.union_with(&other.resource_writes); | ||||||
| @ -369,27 +493,48 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     /// [`Access`] instances are incompatible if one can write
 |     /// [`Access`] instances are incompatible if one can write
 | ||||||
|     /// an element that the other can read or write.
 |     /// an element that the other can read or write.
 | ||||||
|     pub fn is_components_compatible(&self, other: &Access<T>) -> bool { |     pub fn is_components_compatible(&self, other: &Access<T>) -> bool { | ||||||
|         if self.writes_all_components { |         // We have a conflict if we write and they read or write, or if they
 | ||||||
|             return !other.has_any_component_read(); |         // write and we read or write.
 | ||||||
|  |         for ( | ||||||
|  |             lhs_writes, | ||||||
|  |             rhs_reads_and_writes, | ||||||
|  |             lhs_writes_inverted, | ||||||
|  |             rhs_reads_and_writes_inverted, | ||||||
|  |         ) in [ | ||||||
|  |             ( | ||||||
|  |                 &self.component_writes, | ||||||
|  |                 &other.component_read_and_writes, | ||||||
|  |                 self.component_writes_inverted, | ||||||
|  |                 other.component_read_and_writes_inverted, | ||||||
|  |             ), | ||||||
|  |             ( | ||||||
|  |                 &other.component_writes, | ||||||
|  |                 &self.component_read_and_writes, | ||||||
|  |                 other.component_writes_inverted, | ||||||
|  |                 self.component_read_and_writes_inverted, | ||||||
|  |             ), | ||||||
|  |         ] { | ||||||
|  |             match (lhs_writes_inverted, rhs_reads_and_writes_inverted) { | ||||||
|  |                 (true, true) => return false, | ||||||
|  |                 (false, true) => { | ||||||
|  |                     if !lhs_writes.is_subset(rhs_reads_and_writes) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 (true, false) => { | ||||||
|  |                     if !rhs_reads_and_writes.is_subset(lhs_writes) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 (false, false) => { | ||||||
|  |                     if !lhs_writes.is_disjoint(rhs_reads_and_writes) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if other.writes_all_components { |         true | ||||||
|             return !self.has_any_component_read(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if self.reads_all_components { |  | ||||||
|             return !other.has_any_component_write(); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if other.reads_all_components { |  | ||||||
|             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,
 |     /// Returns `true` if the access and `other` can be active at the same time,
 | ||||||
| @ -432,25 +577,48 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     /// Returns `true` if the set's component access is a subset of another, i.e. `other`'s component access
 |     /// 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`.
 |     /// contains at least all the values in `self`.
 | ||||||
|     pub fn is_subset_components(&self, other: &Access<T>) -> bool { |     pub fn is_subset_components(&self, other: &Access<T>) -> bool { | ||||||
|         if self.writes_all_components { |         for ( | ||||||
|             return other.writes_all_components; |             our_components, | ||||||
|  |             their_components, | ||||||
|  |             our_components_inverted, | ||||||
|  |             their_components_inverted, | ||||||
|  |         ) in [ | ||||||
|  |             ( | ||||||
|  |                 &self.component_read_and_writes, | ||||||
|  |                 &other.component_read_and_writes, | ||||||
|  |                 self.component_read_and_writes_inverted, | ||||||
|  |                 other.component_read_and_writes_inverted, | ||||||
|  |             ), | ||||||
|  |             ( | ||||||
|  |                 &self.component_writes, | ||||||
|  |                 &other.component_writes, | ||||||
|  |                 self.component_writes_inverted, | ||||||
|  |                 other.component_writes_inverted, | ||||||
|  |             ), | ||||||
|  |         ] { | ||||||
|  |             match (our_components_inverted, their_components_inverted) { | ||||||
|  |                 (true, true) => { | ||||||
|  |                     if !their_components.is_subset(our_components) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 (true, false) => { | ||||||
|  |                     return false; | ||||||
|  |                 } | ||||||
|  |                 (false, true) => { | ||||||
|  |                     if !our_components.is_disjoint(their_components) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 (false, false) => { | ||||||
|  |                     if !our_components.is_subset(their_components) { | ||||||
|  |                         return false; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if other.writes_all_components { |         true | ||||||
|             return true; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         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
 |     /// Returns `true` if the set's resource access is a subset of another, i.e. `other`'s resource access
 | ||||||
| @ -483,30 +651,52 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|         self.is_subset_components(other) && self.is_subset_resources(other) |         self.is_subset_components(other) && self.is_subset_resources(other) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     fn get_component_conflicts(&self, other: &Access<T>) -> AccessConflicts { | ||||||
|  |         let mut conflicts = FixedBitSet::new(); | ||||||
|  | 
 | ||||||
|  |         // We have a conflict if we write and they read or write, or if they
 | ||||||
|  |         // write and we read or write.
 | ||||||
|  |         for ( | ||||||
|  |             lhs_writes, | ||||||
|  |             rhs_reads_and_writes, | ||||||
|  |             lhs_writes_inverted, | ||||||
|  |             rhs_reads_and_writes_inverted, | ||||||
|  |         ) in [ | ||||||
|  |             ( | ||||||
|  |                 &self.component_writes, | ||||||
|  |                 &other.component_read_and_writes, | ||||||
|  |                 self.component_writes_inverted, | ||||||
|  |                 other.component_read_and_writes_inverted, | ||||||
|  |             ), | ||||||
|  |             ( | ||||||
|  |                 &other.component_writes, | ||||||
|  |                 &self.component_read_and_writes, | ||||||
|  |                 other.component_writes_inverted, | ||||||
|  |                 self.component_read_and_writes_inverted, | ||||||
|  |             ), | ||||||
|  |         ] { | ||||||
|  |             // There's no way that I can see to do this without a temporary.
 | ||||||
|  |             // Neither CNF nor DNF allows us to avoid one.
 | ||||||
|  |             let temp_conflicts: FixedBitSet = | ||||||
|  |                 match (lhs_writes_inverted, rhs_reads_and_writes_inverted) { | ||||||
|  |                     (true, true) => return AccessConflicts::All, | ||||||
|  |                     (false, true) => lhs_writes.difference(rhs_reads_and_writes).collect(), | ||||||
|  |                     (true, false) => rhs_reads_and_writes.difference(lhs_writes).collect(), | ||||||
|  |                     (false, false) => lhs_writes.intersection(rhs_reads_and_writes).collect(), | ||||||
|  |                 }; | ||||||
|  |             conflicts.union_with(&temp_conflicts); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         AccessConflicts::Individual(conflicts) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /// Returns a vector of elements that the access and `other` cannot access at the same time.
 |     /// Returns a vector of elements that the access and `other` cannot access at the same time.
 | ||||||
|     pub fn get_conflicts(&self, other: &Access<T>) -> AccessConflicts { |     pub fn get_conflicts(&self, other: &Access<T>) -> AccessConflicts { | ||||||
|         let mut conflicts = FixedBitSet::new(); |         let mut conflicts = match self.get_component_conflicts(other) { | ||||||
|         if self.reads_all_components { |             AccessConflicts::All => return AccessConflicts::All, | ||||||
|             if other.writes_all_components { |             AccessConflicts::Individual(conflicts) => conflicts, | ||||||
|                 return AccessConflicts::All; |         }; | ||||||
|             } |  | ||||||
|             conflicts.extend(other.component_writes.ones()); |  | ||||||
|         } |  | ||||||
| 
 | 
 | ||||||
|         if other.reads_all_components { |  | ||||||
|             if self.writes_all_components { |  | ||||||
|                 return AccessConflicts::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 { |         if self.reads_all_resources { | ||||||
|             if other.writes_all_resources { |             if other.writes_all_resources { | ||||||
|                 return AccessConflicts::All; |                 return AccessConflicts::All; | ||||||
| @ -528,14 +718,6 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|             conflicts.extend(self.resource_read_and_writes.ones()); |             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( |         conflicts.extend( | ||||||
|             self.resource_writes |             self.resource_writes | ||||||
|                 .intersection(&other.resource_read_and_writes), |                 .intersection(&other.resource_read_and_writes), | ||||||
| @ -547,25 +729,6 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|         AccessConflicts::Individual(conflicts) |         AccessConflicts::Individual(conflicts) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns the indices of the components this has access to.
 |  | ||||||
|     pub fn component_reads_and_writes(&self) -> impl Iterator<Item = T> + '_ { |  | ||||||
|         self.component_read_and_writes |  | ||||||
|             .ones() |  | ||||||
|             .map(T::get_sparse_set_index) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns the indices of the components this has non-exclusive access to.
 |  | ||||||
|     pub fn component_reads(&self) -> impl Iterator<Item = T> + '_ { |  | ||||||
|         self.component_read_and_writes |  | ||||||
|             .difference(&self.component_writes) |  | ||||||
|             .map(T::get_sparse_set_index) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns the indices of the components this has exclusive access to.
 |  | ||||||
|     pub fn component_writes(&self) -> impl Iterator<Item = T> + '_ { |  | ||||||
|         self.component_writes.ones().map(T::get_sparse_set_index) |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns the indices of the components that this has an archetypal access to.
 |     /// Returns the indices of the components that this has an archetypal access to.
 | ||||||
|     ///
 |     ///
 | ||||||
|     /// These are components 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),
 | ||||||
| @ -577,6 +740,40 @@ impl<T: SparseSetIndex> Access<T> { | |||||||
|     pub fn archetypal(&self) -> impl Iterator<Item = T> + '_ { |     pub fn archetypal(&self) -> impl Iterator<Item = T> + '_ { | ||||||
|         self.archetypal.ones().map(T::get_sparse_set_index) |         self.archetypal.ones().map(T::get_sparse_set_index) | ||||||
|     } |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns an iterator over the component IDs that this `Access` either
 | ||||||
|  |     /// reads and writes or can't read or write.
 | ||||||
|  |     ///
 | ||||||
|  |     /// The returned flag specifies whether the list consists of the components
 | ||||||
|  |     /// that the access *can* read or write (false) or whether the list consists
 | ||||||
|  |     /// of the components that the access *can't* read or write (true).
 | ||||||
|  |     ///
 | ||||||
|  |     /// Because this method depends on internal implementation details of
 | ||||||
|  |     /// `Access`, it's not recommended. Prefer to manage your own lists of
 | ||||||
|  |     /// accessible components if your application needs to do that.
 | ||||||
|  |     #[doc(hidden)] | ||||||
|  |     #[deprecated] | ||||||
|  |     pub fn component_reads_and_writes(&self) -> (impl Iterator<Item = T> + '_, bool) { | ||||||
|  |         ( | ||||||
|  |             self.component_read_and_writes | ||||||
|  |                 .ones() | ||||||
|  |                 .map(T::get_sparse_set_index), | ||||||
|  |             self.component_read_and_writes_inverted, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns an iterator over the component IDs that this `Access` either
 | ||||||
|  |     /// writes or can't write.
 | ||||||
|  |     ///
 | ||||||
|  |     /// The returned flag specifies whether the list consists of the components
 | ||||||
|  |     /// that the access *can* write (false) or whether the list consists of the
 | ||||||
|  |     /// components that the access *can't* write (true).
 | ||||||
|  |     pub(crate) fn component_writes(&self) -> (impl Iterator<Item = T> + '_, bool) { | ||||||
|  |         ( | ||||||
|  |             self.component_writes.ones().map(T::get_sparse_set_index), | ||||||
|  |             self.component_writes_inverted, | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /// An [`Access`] that has been filtered to include and exclude certain combinations of elements.
 | /// An [`Access`] that has been filtered to include and exclude certain combinations of elements.
 | ||||||
|  | |||||||
| @ -79,10 +79,14 @@ impl<'w, D: QueryData, F: QueryFilter> QueryBuilder<'w, D, F> { | |||||||
|                 .map_or(false, |info| info.storage_type() == StorageType::Table) |                 .map_or(false, |info| info.storage_type() == StorageType::Table) | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         self.access |         #[allow(deprecated)] | ||||||
|             .access() |         let (mut component_reads_and_writes, component_reads_and_writes_inverted) = | ||||||
|             .component_reads_and_writes() |             self.access.access().component_reads_and_writes(); | ||||||
|             .all(is_dense) |         if component_reads_and_writes_inverted { | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         component_reads_and_writes.all(is_dense) | ||||||
|             && self.access.access().archetypal().all(is_dense) |             && self.access.access().archetypal().all(is_dense) | ||||||
|             && !self.access.access().has_read_all_components() |             && !self.access.access().has_read_all_components() | ||||||
|             && self.access.with_filters().all(is_dense) |             && self.access.with_filters().all(is_dense) | ||||||
|  | |||||||
| @ -1,17 +1,19 @@ | |||||||
| use crate::{ | use crate::{ | ||||||
|     archetype::{Archetype, Archetypes}, |     archetype::{Archetype, Archetypes}, | ||||||
|  |     bundle::Bundle, | ||||||
|     change_detection::{MaybeThinSlicePtrLocation, Ticks, TicksMut}, |     change_detection::{MaybeThinSlicePtrLocation, Ticks, TicksMut}, | ||||||
|     component::{Component, ComponentId, Components, StorageType, Tick}, |     component::{Component, ComponentId, Components, StorageType, Tick}, | ||||||
|     entity::{Entities, Entity, EntityLocation}, |     entity::{Entities, Entity, EntityLocation}, | ||||||
|     query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, |     query::{Access, DebugCheckedUnwrap, FilteredAccess, WorldQuery}, | ||||||
|     storage::{ComponentSparseSet, Table, TableRow}, |     storage::{ComponentSparseSet, Table, TableRow}, | ||||||
|     world::{ |     world::{ | ||||||
|         unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityRef, FilteredEntityMut, |         unsafe_world_cell::UnsafeWorldCell, EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, | ||||||
|         FilteredEntityRef, Mut, Ref, World, |         FilteredEntityMut, FilteredEntityRef, Mut, Ref, World, | ||||||
|     }, |     }, | ||||||
| }; | }; | ||||||
| use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; | use bevy_ptr::{ThinSlicePtr, UnsafeCellDeref}; | ||||||
| use bevy_utils::all_tuples; | use bevy_utils::all_tuples; | ||||||
|  | use smallvec::SmallVec; | ||||||
| use std::{cell::UnsafeCell, marker::PhantomData}; | use std::{cell::UnsafeCell, marker::PhantomData}; | ||||||
| 
 | 
 | ||||||
| /// Types that can be fetched from a [`World`] using a [`Query`].
 | /// Types that can be fetched from a [`World`] using a [`Query`].
 | ||||||
| @ -626,27 +628,15 @@ unsafe impl<'a> WorldQuery for FilteredEntityRef<'a> { | |||||||
|     unsafe fn set_archetype<'w>( |     unsafe fn set_archetype<'w>( | ||||||
|         fetch: &mut Self::Fetch<'w>, |         fetch: &mut Self::Fetch<'w>, | ||||||
|         state: &Self::State, |         state: &Self::State, | ||||||
|         archetype: &'w Archetype, |         _: &'w Archetype, | ||||||
|         _table: &Table, |         _table: &Table, | ||||||
|     ) { |     ) { | ||||||
|         let mut access = Access::default(); |         fetch.1.clone_from(&state.access); | ||||||
|         state.access.component_reads().for_each(|id| { |  | ||||||
|             if archetype.contains(id) { |  | ||||||
|                 access.add_component_read(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         fetch.1 = access; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { |     unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { | ||||||
|         let mut access = Access::default(); |         fetch.1.clone_from(&state.access); | ||||||
|         state.access.component_reads().for_each(|id| { |  | ||||||
|             if table.has_column(id) { |  | ||||||
|                 access.add_component_read(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         fetch.1 = access; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[inline] |     #[inline] | ||||||
| @ -733,37 +723,15 @@ unsafe impl<'a> WorldQuery for FilteredEntityMut<'a> { | |||||||
|     unsafe fn set_archetype<'w>( |     unsafe fn set_archetype<'w>( | ||||||
|         fetch: &mut Self::Fetch<'w>, |         fetch: &mut Self::Fetch<'w>, | ||||||
|         state: &Self::State, |         state: &Self::State, | ||||||
|         archetype: &'w Archetype, |         _: &'w Archetype, | ||||||
|         _table: &Table, |         _table: &Table, | ||||||
|     ) { |     ) { | ||||||
|         let mut access = Access::default(); |         fetch.1.clone_from(&state.access); | ||||||
|         state.access.component_reads().for_each(|id| { |  | ||||||
|             if archetype.contains(id) { |  | ||||||
|                 access.add_component_read(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         state.access.component_writes().for_each(|id| { |  | ||||||
|             if archetype.contains(id) { |  | ||||||
|                 access.add_component_write(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         fetch.1 = access; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, table: &'w Table) { |     unsafe fn set_table<'w>(fetch: &mut Self::Fetch<'w>, state: &Self::State, _: &'w Table) { | ||||||
|         let mut access = Access::default(); |         fetch.1.clone_from(&state.access); | ||||||
|         state.access.component_reads().for_each(|id| { |  | ||||||
|             if table.has_column(id) { |  | ||||||
|                 access.add_component_read(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         state.access.component_writes().for_each(|id| { |  | ||||||
|             if table.has_column(id) { |  | ||||||
|                 access.add_component_write(id); |  | ||||||
|             } |  | ||||||
|         }); |  | ||||||
|         fetch.1 = access; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[inline] |     #[inline] | ||||||
| @ -815,6 +783,201 @@ unsafe impl<'a> QueryData for FilteredEntityMut<'a> { | |||||||
|     type ReadOnly = FilteredEntityRef<'a>; |     type ReadOnly = FilteredEntityRef<'a>; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// SAFETY: `EntityRefExcept` guards access to all components in the bundle `B`
 | ||||||
|  | /// and populates `Access` values so that queries that conflict with this access
 | ||||||
|  | /// are rejected.
 | ||||||
|  | unsafe impl<'a, B> WorldQuery for EntityRefExcept<'a, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     type Fetch<'w> = UnsafeWorldCell<'w>; | ||||||
|  |     type Item<'w> = EntityRefExcept<'w, B>; | ||||||
|  |     type State = SmallVec<[ComponentId; 4]>; | ||||||
|  | 
 | ||||||
|  |     fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { | ||||||
|  |         item | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { | ||||||
|  |         fetch | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsafe fn init_fetch<'w>( | ||||||
|  |         world: UnsafeWorldCell<'w>, | ||||||
|  |         _: &Self::State, | ||||||
|  |         _: Tick, | ||||||
|  |         _: Tick, | ||||||
|  |     ) -> Self::Fetch<'w> { | ||||||
|  |         world | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const IS_DENSE: bool = true; | ||||||
|  | 
 | ||||||
|  |     unsafe fn set_archetype<'w>( | ||||||
|  |         _: &mut Self::Fetch<'w>, | ||||||
|  |         _: &Self::State, | ||||||
|  |         _: &'w Archetype, | ||||||
|  |         _: &'w Table, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} | ||||||
|  | 
 | ||||||
|  |     unsafe fn fetch<'w>( | ||||||
|  |         world: &mut Self::Fetch<'w>, | ||||||
|  |         entity: Entity, | ||||||
|  |         _: TableRow, | ||||||
|  |     ) -> Self::Item<'w> { | ||||||
|  |         let cell = world.get_entity(entity).unwrap(); | ||||||
|  |         EntityRefExcept::new(cell) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn update_component_access( | ||||||
|  |         state: &Self::State, | ||||||
|  |         filtered_access: &mut FilteredAccess<ComponentId>, | ||||||
|  |     ) { | ||||||
|  |         let mut my_access = Access::new(); | ||||||
|  |         my_access.read_all_components(); | ||||||
|  |         for id in state { | ||||||
|  |             my_access.remove_component_read(*id); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let access = filtered_access.access_mut(); | ||||||
|  |         assert!( | ||||||
|  |             access.is_compatible(&my_access), | ||||||
|  |             "`EntityRefExcept<{}>` conflicts with a previous access in this query.", | ||||||
|  |             std::any::type_name::<B>(), | ||||||
|  |         ); | ||||||
|  |         access.extend(&my_access); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn init_state(world: &mut World) -> Self::State { | ||||||
|  |         Self::get_state(world.components()).unwrap() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_state(components: &Components) -> Option<Self::State> { | ||||||
|  |         let mut ids = SmallVec::new(); | ||||||
|  |         B::get_component_ids(components, &mut |maybe_id| { | ||||||
|  |             if let Some(id) = maybe_id { | ||||||
|  |                 ids.push(id); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         Some(ids) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn matches_component_set(_: &Self::State, _: &impl Fn(ComponentId) -> bool) -> bool { | ||||||
|  |         true | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// SAFETY: `Self` is the same as `Self::ReadOnly`.
 | ||||||
|  | unsafe impl<'a, B> QueryData for EntityRefExcept<'a, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     type ReadOnly = Self; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// SAFETY: `EntityRefExcept` enforces read-only access to its contained
 | ||||||
|  | /// components.
 | ||||||
|  | unsafe impl<'a, B> ReadOnlyQueryData for EntityRefExcept<'a, B> where B: Bundle {} | ||||||
|  | 
 | ||||||
|  | /// SAFETY: `EntityMutExcept` guards access to all components in the bundle `B`
 | ||||||
|  | /// and populates `Access` values so that queries that conflict with this access
 | ||||||
|  | /// are rejected.
 | ||||||
|  | unsafe impl<'a, B> WorldQuery for EntityMutExcept<'a, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     type Fetch<'w> = UnsafeWorldCell<'w>; | ||||||
|  |     type Item<'w> = EntityMutExcept<'w, B>; | ||||||
|  |     type State = SmallVec<[ComponentId; 4]>; | ||||||
|  | 
 | ||||||
|  |     fn shrink<'wlong: 'wshort, 'wshort>(item: Self::Item<'wlong>) -> Self::Item<'wshort> { | ||||||
|  |         item | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> { | ||||||
|  |         fetch | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsafe fn init_fetch<'w>( | ||||||
|  |         world: UnsafeWorldCell<'w>, | ||||||
|  |         _: &Self::State, | ||||||
|  |         _: Tick, | ||||||
|  |         _: Tick, | ||||||
|  |     ) -> Self::Fetch<'w> { | ||||||
|  |         world | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const IS_DENSE: bool = true; | ||||||
|  | 
 | ||||||
|  |     unsafe fn set_archetype<'w>( | ||||||
|  |         _: &mut Self::Fetch<'w>, | ||||||
|  |         _: &Self::State, | ||||||
|  |         _: &'w Archetype, | ||||||
|  |         _: &'w Table, | ||||||
|  |     ) { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     unsafe fn set_table<'w>(_: &mut Self::Fetch<'w>, _: &Self::State, _: &'w Table) {} | ||||||
|  | 
 | ||||||
|  |     unsafe fn fetch<'w>( | ||||||
|  |         world: &mut Self::Fetch<'w>, | ||||||
|  |         entity: Entity, | ||||||
|  |         _: TableRow, | ||||||
|  |     ) -> Self::Item<'w> { | ||||||
|  |         let cell = world.get_entity(entity).unwrap(); | ||||||
|  |         EntityMutExcept::new(cell) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn update_component_access( | ||||||
|  |         state: &Self::State, | ||||||
|  |         filtered_access: &mut FilteredAccess<ComponentId>, | ||||||
|  |     ) { | ||||||
|  |         let mut my_access = Access::new(); | ||||||
|  |         my_access.write_all_components(); | ||||||
|  |         for id in state { | ||||||
|  |             my_access.remove_component_read(*id); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let access = filtered_access.access_mut(); | ||||||
|  |         assert!( | ||||||
|  |             access.is_compatible(&my_access), | ||||||
|  |             "`EntityMutExcept<{}>` conflicts with a previous access in this query.", | ||||||
|  |             std::any::type_name::<B>() | ||||||
|  |         ); | ||||||
|  |         access.extend(&my_access); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn init_state(world: &mut World) -> Self::State { | ||||||
|  |         Self::get_state(world.components()).unwrap() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn get_state(components: &Components) -> Option<Self::State> { | ||||||
|  |         let mut ids = SmallVec::new(); | ||||||
|  |         B::get_component_ids(components, &mut |maybe_id| { | ||||||
|  |             if let Some(id) = maybe_id { | ||||||
|  |                 ids.push(id); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |         Some(ids) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     fn matches_component_set(_: &Self::State, _: &impl Fn(ComponentId) -> bool) -> bool { | ||||||
|  |         true | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// SAFETY: All accesses that `EntityRefExcept` provides are also accesses that
 | ||||||
|  | /// `EntityMutExcept` provides.
 | ||||||
|  | unsafe impl<'a, B> QueryData for EntityMutExcept<'a, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     type ReadOnly = EntityRefExcept<'a, B>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// SAFETY:
 | /// SAFETY:
 | ||||||
| /// `update_component_access` and `update_archetype_component_access` do nothing.
 | /// `update_component_access` and `update_archetype_component_access` do nothing.
 | ||||||
| /// This is sound because `fetch` does not access components.
 | /// This is sound because `fetch` does not access components.
 | ||||||
|  | |||||||
| @ -508,22 +508,46 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> { | |||||||
|         archetype: &Archetype, |         archetype: &Archetype, | ||||||
|         access: &mut Access<ArchetypeComponentId>, |         access: &mut Access<ArchetypeComponentId>, | ||||||
|     ) { |     ) { | ||||||
|         self.component_access |         // As a fast path, we can iterate directly over the components involved
 | ||||||
|             .access |         // if the `access` isn't inverted.
 | ||||||
|             .component_reads() |         #[allow(deprecated)] | ||||||
|             .for_each(|id| { |         let (component_reads_and_writes, component_reads_and_writes_inverted) = | ||||||
|  |             self.component_access.access.component_reads_and_writes(); | ||||||
|  |         let (component_writes, component_writes_inverted) = | ||||||
|  |             self.component_access.access.component_writes(); | ||||||
|  | 
 | ||||||
|  |         if !component_reads_and_writes_inverted && !component_writes_inverted { | ||||||
|  |             component_reads_and_writes.for_each(|id| { | ||||||
|                 if let Some(id) = archetype.get_archetype_component_id(id) { |                 if let Some(id) = archetype.get_archetype_component_id(id) { | ||||||
|                     access.add_component_read(id); |                     access.add_component_read(id); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|         self.component_access |             component_writes.for_each(|id| { | ||||||
|             .access |  | ||||||
|             .component_writes() |  | ||||||
|             .for_each(|id| { |  | ||||||
|                 if let Some(id) = archetype.get_archetype_component_id(id) { |                 if let Some(id) = archetype.get_archetype_component_id(id) { | ||||||
|                     access.add_component_write(id); |                     access.add_component_write(id); | ||||||
|                 } |                 } | ||||||
|             }); |             }); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (component_id, archetype_component_id) in | ||||||
|  |             archetype.components_with_archetype_component_id() | ||||||
|  |         { | ||||||
|  |             if self | ||||||
|  |                 .component_access | ||||||
|  |                 .access | ||||||
|  |                 .has_component_read(component_id) | ||||||
|  |             { | ||||||
|  |                 access.add_component_read(archetype_component_id); | ||||||
|  |             } | ||||||
|  |             if self | ||||||
|  |                 .component_access | ||||||
|  |                 .access | ||||||
|  |                 .has_component_write(component_id) | ||||||
|  |             { | ||||||
|  |                 access.add_component_write(archetype_component_id); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Use this to transform a [`QueryState`] into a more generic [`QueryState`].
 |     /// Use this to transform a [`QueryState`] into a more generic [`QueryState`].
 | ||||||
|  | |||||||
| @ -1493,13 +1493,10 @@ mod tests { | |||||||
|         // set up system and verify its access is empty
 |         // set up system and verify its access is empty
 | ||||||
|         system.initialize(&mut world); |         system.initialize(&mut world); | ||||||
|         system.update_archetype_component_access(world.as_unsafe_world_cell()); |         system.update_archetype_component_access(world.as_unsafe_world_cell()); | ||||||
|         assert_eq!( |         let archetype_component_access = system.archetype_component_access(); | ||||||
|             system |         assert!(expected_ids | ||||||
|                 .archetype_component_access() |             .iter() | ||||||
|                 .component_reads() |             .all(|id| archetype_component_access.has_component_read(*id))); | ||||||
|                 .collect::<HashSet<_>>(), |  | ||||||
|             expected_ids |  | ||||||
|         ); |  | ||||||
| 
 | 
 | ||||||
|         // add some entities with archetypes that should match and save their ids
 |         // add some entities with archetypes that should match and save their ids
 | ||||||
|         expected_ids.insert( |         expected_ids.insert( | ||||||
| @ -1523,13 +1520,10 @@ mod tests { | |||||||
| 
 | 
 | ||||||
|         // update system and verify its accesses are correct
 |         // update system and verify its accesses are correct
 | ||||||
|         system.update_archetype_component_access(world.as_unsafe_world_cell()); |         system.update_archetype_component_access(world.as_unsafe_world_cell()); | ||||||
|         assert_eq!( |         let archetype_component_access = system.archetype_component_access(); | ||||||
|             system |         assert!(expected_ids | ||||||
|                 .archetype_component_access() |             .iter() | ||||||
|                 .component_reads() |             .all(|id| archetype_component_access.has_component_read(*id))); | ||||||
|                 .collect::<HashSet<_>>(), |  | ||||||
|             expected_ids |  | ||||||
|         ); |  | ||||||
| 
 | 
 | ||||||
|         // one more round
 |         // one more round
 | ||||||
|         expected_ids.insert( |         expected_ids.insert( | ||||||
| @ -1541,13 +1535,10 @@ mod tests { | |||||||
|         ); |         ); | ||||||
|         world.spawn((A, B, D)); |         world.spawn((A, B, D)); | ||||||
|         system.update_archetype_component_access(world.as_unsafe_world_cell()); |         system.update_archetype_component_access(world.as_unsafe_world_cell()); | ||||||
|         assert_eq!( |         let archetype_component_access = system.archetype_component_access(); | ||||||
|             system |         assert!(expected_ids | ||||||
|                 .archetype_component_access() |             .iter() | ||||||
|                 .component_reads() |             .all(|id| archetype_component_access.has_component_read(*id))); | ||||||
|                 .collect::<HashSet<_>>(), |  | ||||||
|             expected_ids |  | ||||||
|         ); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     #[test] |     #[test] | ||||||
|  | |||||||
| @ -1927,12 +1927,6 @@ impl<'w> FilteredEntityRef<'w> { | |||||||
|         self.entity.archetype() |         self.entity.archetype() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns an iterator over the component ids that are accessed by self.
 |  | ||||||
|     #[inline] |  | ||||||
|     pub fn accessed_components(&self) -> impl Iterator<Item = ComponentId> + '_ { |  | ||||||
|         self.access.component_reads_and_writes() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns a reference to the underlying [`Access`].
 |     /// Returns a reference to the underlying [`Access`].
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn access(&self) -> &Access<ComponentId> { |     pub fn access(&self) -> &Access<ComponentId> { | ||||||
| @ -2184,12 +2178,6 @@ impl<'w> FilteredEntityMut<'w> { | |||||||
|         self.entity.archetype() |         self.entity.archetype() | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /// Returns an iterator over the component ids that are accessed by self.
 |  | ||||||
|     #[inline] |  | ||||||
|     pub fn accessed_components(&self) -> impl Iterator<Item = ComponentId> + '_ { |  | ||||||
|         self.access.component_reads_and_writes() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     /// Returns a reference to the underlying [`Access`].
 |     /// Returns a reference to the underlying [`Access`].
 | ||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn access(&self) -> &Access<ComponentId> { |     pub fn access(&self) -> &Access<ComponentId> { | ||||||
| @ -2384,6 +2372,180 @@ pub enum TryFromFilteredError { | |||||||
|     MissingWriteAllAccess, |     MissingWriteAllAccess, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Provides read-only access to a single entity and all its components, save
 | ||||||
|  | /// for an explicitly-enumerated set.
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct EntityRefExcept<'w, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     entity: UnsafeEntityCell<'w>, | ||||||
|  |     phantom: PhantomData<B>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'w, B> EntityRefExcept<'w, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>) -> Self { | ||||||
|  |         Self { | ||||||
|  |             entity, | ||||||
|  |             phantom: PhantomData, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets access to the component of type `C` for the current entity. Returns
 | ||||||
|  |     /// `None` if the component doesn't have a component of that type or if the
 | ||||||
|  |     /// type is one of the excluded components.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn get<C>(&self) -> Option<&'w C> | ||||||
|  |     where | ||||||
|  |         C: Component, | ||||||
|  |     { | ||||||
|  |         let components = self.entity.world().components(); | ||||||
|  |         let id = components.component_id::<C>()?; | ||||||
|  |         if bundle_contains_component::<B>(components, id) { | ||||||
|  |             None | ||||||
|  |         } else { | ||||||
|  |             // SAFETY: We have read access for all components that weren't
 | ||||||
|  |             // covered by the `contains` check above.
 | ||||||
|  |             unsafe { self.entity.get() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets access to the component of type `C` for the current entity,
 | ||||||
|  |     /// including change detection information. Returns `None` if the component
 | ||||||
|  |     /// doesn't have a component of that type or if the type is one of the
 | ||||||
|  |     /// excluded components.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn get_ref<C>(&self) -> Option<Ref<'w, C>> | ||||||
|  |     where | ||||||
|  |         C: Component, | ||||||
|  |     { | ||||||
|  |         let components = self.entity.world().components(); | ||||||
|  |         let id = components.component_id::<C>()?; | ||||||
|  |         if bundle_contains_component::<B>(components, id) { | ||||||
|  |             None | ||||||
|  |         } else { | ||||||
|  |             // SAFETY: We have read access for all components that weren't
 | ||||||
|  |             // covered by the `contains` check above.
 | ||||||
|  |             unsafe { self.entity.get_ref() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'a, B> From<&'a EntityMutExcept<'_, B>> for EntityRefExcept<'a, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     fn from(entity_mut: &'a EntityMutExcept<'_, B>) -> Self { | ||||||
|  |         // SAFETY: All accesses that `EntityRefExcept` provides are also
 | ||||||
|  |         // accesses that `EntityMutExcept` provides.
 | ||||||
|  |         unsafe { EntityRefExcept::new(entity_mut.entity) } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /// Provides mutable access to all components of an entity, with the exception
 | ||||||
|  | /// of an explicit set.
 | ||||||
|  | ///
 | ||||||
|  | /// This is a rather niche type that should only be used if you need access to
 | ||||||
|  | /// *all* components of an entity, while still allowing you to consult other
 | ||||||
|  | /// queries that might match entities that this query also matches. If you don't
 | ||||||
|  | /// need access to all components, prefer a standard query with a
 | ||||||
|  | /// [`crate::query::Without`] filter.
 | ||||||
|  | #[derive(Clone)] | ||||||
|  | pub struct EntityMutExcept<'w, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     entity: UnsafeEntityCell<'w>, | ||||||
|  |     phantom: PhantomData<B>, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl<'w, B> EntityMutExcept<'w, B> | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     pub(crate) unsafe fn new(entity: UnsafeEntityCell<'w>) -> Self { | ||||||
|  |         Self { | ||||||
|  |             entity, | ||||||
|  |             phantom: PhantomData, | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Returns a new instance with a shorter lifetime.
 | ||||||
|  |     ///
 | ||||||
|  |     /// This is useful if you have `&mut EntityMutExcept`, but you need
 | ||||||
|  |     /// `EntityMutExcept`.
 | ||||||
|  |     pub fn reborrow(&mut self) -> EntityMutExcept<'_, B> { | ||||||
|  |         // SAFETY: We have exclusive access to the entire entity and the
 | ||||||
|  |         // applicable components.
 | ||||||
|  |         unsafe { Self::new(self.entity) } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets read-only access to all of the entity's components, except for the
 | ||||||
|  |     /// ones in `CL`.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn as_readonly(&self) -> EntityRefExcept<'_, B> { | ||||||
|  |         EntityRefExcept::from(self) | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets access to the component of type `C` for the current entity. Returns
 | ||||||
|  |     /// `None` if the component doesn't have a component of that type or if the
 | ||||||
|  |     /// type is one of the excluded components.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn get<C>(&self) -> Option<&'_ C> | ||||||
|  |     where | ||||||
|  |         C: Component, | ||||||
|  |     { | ||||||
|  |         self.as_readonly().get() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets access to the component of type `C` for the current entity,
 | ||||||
|  |     /// including change detection information. Returns `None` if the component
 | ||||||
|  |     /// doesn't have a component of that type or if the type is one of the
 | ||||||
|  |     /// excluded components.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn get_ref<C>(&self) -> Option<Ref<'_, C>> | ||||||
|  |     where | ||||||
|  |         C: Component, | ||||||
|  |     { | ||||||
|  |         self.as_readonly().get_ref() | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Gets mutable access to the component of type `C` for the current entity.
 | ||||||
|  |     /// Returns `None` if the component doesn't have a component of that type or
 | ||||||
|  |     /// if the type is one of the excluded components.
 | ||||||
|  |     #[inline] | ||||||
|  |     pub fn get_mut<C>(&mut self) -> Option<Mut<'_, C>> | ||||||
|  |     where | ||||||
|  |         C: Component, | ||||||
|  |     { | ||||||
|  |         let components = self.entity.world().components(); | ||||||
|  |         let id = components.component_id::<C>()?; | ||||||
|  |         if bundle_contains_component::<B>(components, id) { | ||||||
|  |             None | ||||||
|  |         } else { | ||||||
|  |             // SAFETY: We have write access for all components that weren't
 | ||||||
|  |             // covered by the `contains` check above.
 | ||||||
|  |             unsafe { self.entity.get_mut() } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | fn bundle_contains_component<B>(components: &Components, query_id: ComponentId) -> bool | ||||||
|  | where | ||||||
|  |     B: Bundle, | ||||||
|  | { | ||||||
|  |     let mut found = false; | ||||||
|  |     B::get_component_ids(components, &mut |maybe_id| { | ||||||
|  |         if let Some(id) = maybe_id { | ||||||
|  |             found = found || id == query_id; | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  |     found | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Inserts a dynamic [`Bundle`] into the entity.
 | /// Inserts a dynamic [`Bundle`] into the entity.
 | ||||||
| ///
 | ///
 | ||||||
| /// # Safety
 | /// # Safety
 | ||||||
| @ -2598,9 +2760,12 @@ mod tests { | |||||||
|     use bevy_ptr::OwningPtr; |     use bevy_ptr::OwningPtr; | ||||||
|     use std::panic::AssertUnwindSafe; |     use std::panic::AssertUnwindSafe; | ||||||
| 
 | 
 | ||||||
|  |     use crate::system::RunSystemOnce as _; | ||||||
|     use crate::world::{FilteredEntityMut, FilteredEntityRef}; |     use crate::world::{FilteredEntityMut, FilteredEntityRef}; | ||||||
|     use crate::{self as bevy_ecs, component::ComponentId, prelude::*, system::assert_is_system}; |     use crate::{self as bevy_ecs, component::ComponentId, prelude::*, system::assert_is_system}; | ||||||
| 
 | 
 | ||||||
|  |     use super::{EntityMutExcept, EntityRefExcept}; | ||||||
|  | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn sorted_remove() { |     fn sorted_remove() { | ||||||
|         let mut a = vec![1, 2, 3, 4, 5, 6, 7]; |         let mut a = vec![1, 2, 3, 4, 5, 6, 7]; | ||||||
| @ -2993,6 +3158,164 @@ mod tests { | |||||||
|         world.spawn_empty().remove_by_id(test_component_id); |         world.spawn_empty().remove_by_id(test_component_id); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /// Tests that components can be accessed through an `EntityRefExcept`.
 | ||||||
|  |     #[test] | ||||||
|  |     fn entity_ref_except() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.init_component::<TestComponent>(); | ||||||
|  |         world.init_component::<TestComponent2>(); | ||||||
|  | 
 | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         let mut query = world.query::<EntityRefExcept<TestComponent>>(); | ||||||
|  | 
 | ||||||
|  |         let mut found = false; | ||||||
|  |         for entity_ref in query.iter_mut(&mut world) { | ||||||
|  |             found = true; | ||||||
|  |             assert!(entity_ref.get::<TestComponent>().is_none()); | ||||||
|  |             assert!(entity_ref.get_ref::<TestComponent>().is_none()); | ||||||
|  |             assert!(matches!( | ||||||
|  |                 entity_ref.get::<TestComponent2>(), | ||||||
|  |                 Some(TestComponent2(0)) | ||||||
|  |             )); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         assert!(found); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that a single query can't both contain a mutable reference to a
 | ||||||
|  |     // component C and an `EntityRefExcept` that doesn't include C among its
 | ||||||
|  |     // exclusions.
 | ||||||
|  |     #[test] | ||||||
|  |     #[should_panic] | ||||||
|  |     fn entity_ref_except_conflicts_with_self() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         // This should panic, because we have a mutable borrow on
 | ||||||
|  |         // `TestComponent` but have a simultaneous indirect immutable borrow on
 | ||||||
|  |         // that component via `EntityRefExcept`.
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<(&mut TestComponent, EntityRefExcept<TestComponent2>)>) {} | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that an `EntityRefExcept` that doesn't include a component C among
 | ||||||
|  |     // its exclusions can't coexist with a mutable query for that component.
 | ||||||
|  |     #[test] | ||||||
|  |     #[should_panic] | ||||||
|  |     fn entity_ref_except_conflicts_with_other() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         // This should panic, because we have a mutable borrow on
 | ||||||
|  |         // `TestComponent` but have a simultaneous indirect immutable borrow on
 | ||||||
|  |         // that component via `EntityRefExcept`.
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<&mut TestComponent>, _: Query<EntityRefExcept<TestComponent2>>) {} | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that an `EntityRefExcept` with an exception for some component C can
 | ||||||
|  |     // coexist with a query for that component C.
 | ||||||
|  |     #[test] | ||||||
|  |     fn entity_ref_except_doesnt_conflict() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<&mut TestComponent>, query: Query<EntityRefExcept<TestComponent>>) { | ||||||
|  |             for entity_ref in query.iter() { | ||||||
|  |                 assert!(matches!( | ||||||
|  |                     entity_ref.get::<TestComponent2>(), | ||||||
|  |                     Some(TestComponent2(0)) | ||||||
|  |                 )); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /// Tests that components can be mutably accessed through an
 | ||||||
|  |     /// `EntityMutExcept`.
 | ||||||
|  |     #[test] | ||||||
|  |     fn entity_mut_except() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         let mut query = world.query::<EntityMutExcept<TestComponent>>(); | ||||||
|  | 
 | ||||||
|  |         let mut found = false; | ||||||
|  |         for mut entity_mut in query.iter_mut(&mut world) { | ||||||
|  |             found = true; | ||||||
|  |             assert!(entity_mut.get::<TestComponent>().is_none()); | ||||||
|  |             assert!(entity_mut.get_ref::<TestComponent>().is_none()); | ||||||
|  |             assert!(entity_mut.get_mut::<TestComponent>().is_none()); | ||||||
|  |             assert!(matches!( | ||||||
|  |                 entity_mut.get::<TestComponent2>(), | ||||||
|  |                 Some(TestComponent2(0)) | ||||||
|  |             )); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         assert!(found); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that a single query can't both contain a mutable reference to a
 | ||||||
|  |     // component C and an `EntityMutExcept` that doesn't include C among its
 | ||||||
|  |     // exclusions.
 | ||||||
|  |     #[test] | ||||||
|  |     #[should_panic] | ||||||
|  |     fn entity_mut_except_conflicts_with_self() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         // This should panic, because we have a mutable borrow on
 | ||||||
|  |         // `TestComponent` but have a simultaneous indirect immutable borrow on
 | ||||||
|  |         // that component via `EntityRefExcept`.
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<(&mut TestComponent, EntityMutExcept<TestComponent2>)>) {} | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that an `EntityMutExcept` that doesn't include a component C among
 | ||||||
|  |     // its exclusions can't coexist with a query for that component.
 | ||||||
|  |     #[test] | ||||||
|  |     #[should_panic] | ||||||
|  |     fn entity_mut_except_conflicts_with_other() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         // This should panic, because we have a mutable borrow on
 | ||||||
|  |         // `TestComponent` but have a simultaneous indirect immutable borrow on
 | ||||||
|  |         // that component via `EntityRefExcept`.
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<&mut TestComponent>, mut query: Query<EntityMutExcept<TestComponent2>>) { | ||||||
|  |             for mut entity_mut in query.iter_mut() { | ||||||
|  |                 assert!(entity_mut | ||||||
|  |                     .get_mut::<TestComponent2>() | ||||||
|  |                     .is_some_and(|component| component.0 == 0)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Test that an `EntityMutExcept` with an exception for some component C can
 | ||||||
|  |     // coexist with a query for that component C.
 | ||||||
|  |     #[test] | ||||||
|  |     fn entity_mut_except_doesnt_conflict() { | ||||||
|  |         let mut world = World::new(); | ||||||
|  |         world.spawn(TestComponent(0)).insert(TestComponent2(0)); | ||||||
|  | 
 | ||||||
|  |         world.run_system_once(system); | ||||||
|  | 
 | ||||||
|  |         fn system(_: Query<&mut TestComponent>, mut query: Query<EntityMutExcept<TestComponent>>) { | ||||||
|  |             for mut entity_mut in query.iter_mut() { | ||||||
|  |                 assert!(entity_mut | ||||||
|  |                     .get_mut::<TestComponent2>() | ||||||
|  |                     .is_some_and(|component| component.0 == 0)); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[derive(Component)] |     #[derive(Component)] | ||||||
|     struct A; |     struct A; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -19,8 +19,8 @@ pub use crate::{ | |||||||
| pub use component_constants::*; | pub use component_constants::*; | ||||||
| pub use deferred_world::DeferredWorld; | pub use deferred_world::DeferredWorld; | ||||||
| pub use entity_ref::{ | pub use entity_ref::{ | ||||||
|     EntityMut, EntityRef, EntityWorldMut, Entry, FilteredEntityMut, FilteredEntityRef, |     EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, EntityWorldMut, Entry, | ||||||
|     OccupiedEntry, VacantEntry, |     FilteredEntityMut, FilteredEntityRef, OccupiedEntry, VacantEntry, | ||||||
| }; | }; | ||||||
| pub use identifier::WorldId; | pub use identifier::WorldId; | ||||||
| pub use spawn_batch::*; | pub use spawn_batch::*; | ||||||
|  | |||||||
| @ -150,8 +150,11 @@ fn main() { | |||||||
|                 let mut query = builder.build(); |                 let mut query = builder.build(); | ||||||
| 
 | 
 | ||||||
|                 query.iter_mut(&mut world).for_each(|filtered_entity| { |                 query.iter_mut(&mut world).for_each(|filtered_entity| { | ||||||
|  |                     #[allow(deprecated)] | ||||||
|                     let terms = filtered_entity |                     let terms = filtered_entity | ||||||
|                         .accessed_components() |                         .access() | ||||||
|  |                         .component_reads_and_writes() | ||||||
|  |                         .0 | ||||||
|                         .map(|id| { |                         .map(|id| { | ||||||
|                             let ptr = filtered_entity.get_by_id(id).unwrap(); |                             let ptr = filtered_entity.get_by_id(id).unwrap(); | ||||||
|                             let info = component_info.get(&id).unwrap(); |                             let info = component_info.get(&id).unwrap(); | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Patrick Walton
						Patrick Walton