Separate component and resource access (#14561)
# Objective - Fixes https://github.com/bevyengine/bevy/issues/13139 - Fixes https://github.com/bevyengine/bevy/issues/7255 - Separates component from resource access so that we can correctly handles edge cases like the issue above - Inspired from https://github.com/bevyengine/bevy/pull/14472 ## Solution - Update access to have `component` fields and `resource` fields ## Testing - Added some unit tests
This commit is contained in:
parent
0d0f77a7ab
commit
3a664b052d
@ -1420,8 +1420,8 @@ mod tests {
|
||||
let mut expected = FilteredAccess::<ComponentId>::default();
|
||||
let a_id = world.components.get_id(TypeId::of::<A>()).unwrap();
|
||||
let b_id = world.components.get_id(TypeId::of::<B>()).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"
|
||||
|
@ -49,17 +49,27 @@ 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<T: SparseSetIndex> {
|
||||
/// All accessed elements.
|
||||
reads_and_writes: FixedBitSet,
|
||||
/// The exclusively-accessed elements.
|
||||
writes: FixedBitSet,
|
||||
/// Is `true` if this has access to all elements in the collection.
|
||||
/// 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 resources.
|
||||
/// This field is a performance optimization for `&World` (also harder to mess up for soundness).
|
||||
reads_all: bool,
|
||||
/// Is `true` if this has mutable access to all elements in the collection.
|
||||
reads_all_resources: bool,
|
||||
/// Is `true` if this has mutable access to all resources.
|
||||
/// If this is true, then `reads_all` must also be true.
|
||||
writes_all: bool,
|
||||
// Elements that are not accessed, but whose presence in an archetype affect query results.
|
||||
writes_all_resources: bool,
|
||||
// Components that are not accessed, but whose presence in an archetype affect query results.
|
||||
archetypal: FixedBitSet,
|
||||
marker: PhantomData<T>,
|
||||
}
|
||||
@ -68,20 +78,30 @@ pub struct Access<T: SparseSetIndex> {
|
||||
impl<T: SparseSetIndex> Clone for Access<T> {
|
||||
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 +110,25 @@ impl<T: SparseSetIndex + fmt::Debug> fmt::Debug for Access<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Access")
|
||||
.field(
|
||||
"read_and_writes",
|
||||
&FormattedBitSet::<T>::new(&self.reads_and_writes),
|
||||
"component_read_and_writes",
|
||||
&FormattedBitSet::<T>::new(&self.component_read_and_writes),
|
||||
)
|
||||
.field("writes", &FormattedBitSet::<T>::new(&self.writes))
|
||||
.field("reads_all", &self.reads_all)
|
||||
.field("writes_all", &self.writes_all)
|
||||
.field(
|
||||
"component_writes",
|
||||
&FormattedBitSet::<T>::new(&self.component_writes),
|
||||
)
|
||||
.field(
|
||||
"resource_read_and_writes",
|
||||
&FormattedBitSet::<T>::new(&self.resource_read_and_writes),
|
||||
)
|
||||
.field(
|
||||
"resource_writes",
|
||||
&FormattedBitSet::<T>::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::<T>::new(&self.archetypal))
|
||||
.finish()
|
||||
}
|
||||
@ -111,31 +144,50 @@ impl<T: SparseSetIndex> Access<T> {
|
||||
/// 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::new(),
|
||||
resource_writes: FixedBitSet::new(),
|
||||
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());
|
||||
self.writes.grow_and_insert(index.sparse_set_index());
|
||||
}
|
||||
|
||||
/// Adds an archetypal (indirect) access to the element given by `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());
|
||||
}
|
||||
|
||||
/// 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 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<T>`].
|
||||
@ -145,29 +197,55 @@ impl<T: SparseSetIndex> Access<T> {
|
||||
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()
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// Returns true if this has an archetypal (indirect) access to the element given by `index`.
|
||||
/// 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.resource_writes.is_clear()
|
||||
}
|
||||
|
||||
/// 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<T>`].
|
||||
@ -177,47 +255,170 @@ impl<T: SparseSetIndex> Access<T> {
|
||||
self.archetypal.contains(index.sparse_set_index())
|
||||
}
|
||||
|
||||
/// Sets this as having access to all indexed elements (i.e. `&World`).
|
||||
pub fn read_all(&mut self) {
|
||||
self.reads_all = true;
|
||||
/// 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 indexed elements (i.e. `EntityMut`).
|
||||
/// 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.read_all_components();
|
||||
self.read_all_resources();
|
||||
}
|
||||
|
||||
/// 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<T>) {
|
||||
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,
|
||||
/// only looking at their component access.
|
||||
///
|
||||
/// [`Access`] instances are incompatible if one can write
|
||||
/// an element that the other can read or write.
|
||||
pub fn is_components_compatible(&self, other: &Access<T>) -> bool {
|
||||
if self.writes_all_components {
|
||||
return !other.has_any_component_read();
|
||||
}
|
||||
|
||||
if other.writes_all_components {
|
||||
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,
|
||||
/// only looking at their resource access.
|
||||
///
|
||||
/// [`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<T>) -> bool {
|
||||
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.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.
|
||||
@ -225,98 +426,145 @@ impl<T: SparseSetIndex> Access<T> {
|
||||
/// [`Access`] instances are incompatible if one can write
|
||||
/// an element that the other can read or write.
|
||||
pub fn is_compatible(&self, other: &Access<T>) -> bool {
|
||||
if self.writes_all {
|
||||
return !other.has_any_read();
|
||||
self.is_components_compatible(other) && self.is_resources_compatible(other)
|
||||
}
|
||||
|
||||
/// 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<T>) -> bool {
|
||||
if self.writes_all_components {
|
||||
return other.writes_all_components;
|
||||
}
|
||||
|
||||
if other.writes_all {
|
||||
return !self.has_any_read();
|
||||
if other.writes_all_components {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.reads_all {
|
||||
return !other.has_any_write();
|
||||
if self.reads_all_components {
|
||||
return other.reads_all_components;
|
||||
}
|
||||
|
||||
if other.reads_all {
|
||||
return !self.has_any_write();
|
||||
if other.reads_all_components {
|
||||
return self.component_writes.is_subset(&other.component_writes);
|
||||
}
|
||||
|
||||
self.writes.is_disjoint(&other.reads_and_writes)
|
||||
&& other.writes.is_disjoint(&self.reads_and_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<T>) -> bool {
|
||||
if self.writes_all_resources {
|
||||
return other.writes_all_resources;
|
||||
}
|
||||
|
||||
if other.writes_all_resources {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.reads_all_resources {
|
||||
return other.reads_all_resources;
|
||||
}
|
||||
|
||||
if other.reads_all_resources {
|
||||
return 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<T>) -> bool {
|
||||
if self.writes_all {
|
||||
return other.writes_all;
|
||||
}
|
||||
|
||||
if other.writes_all {
|
||||
return true;
|
||||
}
|
||||
|
||||
if self.reads_all {
|
||||
return other.reads_all;
|
||||
}
|
||||
|
||||
if other.reads_all {
|
||||
return self.writes.is_subset(&other.writes);
|
||||
}
|
||||
|
||||
self.reads_and_writes.is_subset(&other.reads_and_writes)
|
||||
&& self.writes.is_subset(&other.writes)
|
||||
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.
|
||||
pub fn get_conflicts(&self, other: &Access<T>) -> Vec<T> {
|
||||
let mut conflicts = FixedBitSet::default();
|
||||
if self.reads_all {
|
||||
if self.reads_all_components {
|
||||
// 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_components {
|
||||
// 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_components {
|
||||
conflicts.extend(other.component_read_and_writes.ones());
|
||||
}
|
||||
|
||||
if other.writes_all {
|
||||
conflicts.extend(self.reads_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.resource_writes.ones());
|
||||
}
|
||||
|
||||
conflicts.extend(self.writes.intersection(&other.reads_and_writes));
|
||||
conflicts.extend(self.reads_and_writes.intersection(&other.writes));
|
||||
if other.reads_all_resources {
|
||||
// QUESTION: How to handle `self.writes_all`.
|
||||
conflicts.extend(self.resource_writes.ones());
|
||||
}
|
||||
|
||||
if self.writes_all_resources {
|
||||
conflicts.extend(other.resource_read_and_writes.ones());
|
||||
}
|
||||
|
||||
if other.writes_all_resources {
|
||||
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
|
||||
.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<Item = T> + '_ {
|
||||
self.reads_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<Item = T> + '_ {
|
||||
self.reads_and_writes
|
||||
.difference(&self.writes)
|
||||
/// 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 elements this has exclusive access to.
|
||||
pub fn writes(&self) -> impl Iterator<Item = T> + '_ {
|
||||
self.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 elements that this has an archetypal access to.
|
||||
/// 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.
|
||||
///
|
||||
/// 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<T>`].
|
||||
@ -420,20 +668,30 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
|
||||
&mut self.access
|
||||
}
|
||||
|
||||
/// Adds access to the element given by `index`.
|
||||
pub fn add_read(&mut self, index: T) {
|
||||
self.access.add_read(index.clone());
|
||||
/// 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) {
|
||||
self.access.add_write(index.clone());
|
||||
/// 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());
|
||||
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());
|
||||
}
|
||||
@ -544,6 +802,16 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
|
||||
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<T>) -> bool {
|
||||
@ -711,17 +979,17 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
|
||||
self.filtered_accesses.push(filtered_access);
|
||||
}
|
||||
|
||||
/// Adds a read access without filters to the set.
|
||||
pub(crate) fn add_unfiltered_read(&mut self, index: T) {
|
||||
/// 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_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) {
|
||||
/// 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_write(index);
|
||||
filter.add_resource_write(index);
|
||||
self.add(filter);
|
||||
}
|
||||
|
||||
@ -769,9 +1037,9 @@ mod tests {
|
||||
fn create_sample_access() -> Access<usize> {
|
||||
let mut access = Access::<usize>::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();
|
||||
|
||||
@ -781,8 +1049,8 @@ mod tests {
|
||||
fn create_sample_filtered_access() -> FilteredAccess<usize> {
|
||||
let mut filtered_access = FilteredAccess::<usize>::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);
|
||||
|
||||
@ -801,8 +1069,8 @@ mod tests {
|
||||
fn create_sample_filtered_access_set() -> FilteredAccessSet<usize> {
|
||||
let mut filtered_access_set = FilteredAccessSet::<usize>::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
|
||||
@ -821,8 +1089,8 @@ mod tests {
|
||||
let original: Access<usize> = create_sample_access();
|
||||
let mut cloned = Access::<usize>::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();
|
||||
|
||||
@ -844,8 +1112,8 @@ mod tests {
|
||||
let original: FilteredAccess<usize> = create_sample_filtered_access();
|
||||
let mut cloned = FilteredAccess::<usize>::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);
|
||||
@ -887,8 +1155,8 @@ mod tests {
|
||||
let original: FilteredAccessSet<usize> = create_sample_filtered_access_set();
|
||||
let mut cloned = FilteredAccessSet::<usize>::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);
|
||||
@ -900,7 +1168,7 @@ mod tests {
|
||||
fn read_all_access_conflicts() {
|
||||
// read_all / single write
|
||||
let mut access_a = Access::<usize>::default();
|
||||
access_a.add_write(0);
|
||||
access_a.add_component_write(0);
|
||||
|
||||
let mut access_b = Access::<usize>::default();
|
||||
access_b.read_all();
|
||||
@ -920,24 +1188,24 @@ mod tests {
|
||||
#[test]
|
||||
fn access_get_conflicts() {
|
||||
let mut access_a = Access::<usize>::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::<usize>::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::<usize>::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::<usize>::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![]);
|
||||
@ -947,10 +1215,10 @@ mod tests {
|
||||
#[test]
|
||||
fn filtered_combined_access() {
|
||||
let mut access_a = FilteredAccessSet::<usize>::default();
|
||||
access_a.add_unfiltered_read(1);
|
||||
access_a.add_unfiltered_resource_read(1);
|
||||
|
||||
let mut filter_b = FilteredAccess::<usize>::default();
|
||||
filter_b.add_write(1);
|
||||
filter_b.add_resource_write(1);
|
||||
|
||||
let conflicts = access_a.get_conflicts_single(&filter_b);
|
||||
assert_eq!(
|
||||
@ -963,22 +1231,22 @@ mod tests {
|
||||
#[test]
|
||||
fn filtered_access_extend() {
|
||||
let mut access_a = FilteredAccess::<usize>::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::<usize>::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::<usize>::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));
|
||||
@ -988,8 +1256,8 @@ mod tests {
|
||||
fn filtered_access_extend_or() {
|
||||
let mut access_a = FilteredAccess::<usize>::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<C>`.
|
||||
let mut access_b = FilteredAccess::<usize>::default();
|
||||
@ -1010,8 +1278,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::<usize>::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<A>, With<B>, With<C>), (With<A>, With<B>, With<D>, Without<E>))>`.
|
||||
expected.filter_sets = vec![
|
||||
AccessFilters {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -480,10 +480,10 @@ unsafe impl<'a> WorldQuery for EntityRef<'a> {
|
||||
|
||||
fn update_component_access(_state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
||||
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();
|
||||
access.read_all_components();
|
||||
}
|
||||
|
||||
fn init_state(_world: &mut World) {}
|
||||
@ -556,10 +556,10 @@ unsafe impl<'a> WorldQuery for EntityMut<'a> {
|
||||
|
||||
fn update_component_access(_state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
||||
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();
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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;
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,11 +988,11 @@ unsafe impl<T: Component> WorldQuery for &T {
|
||||
access: &mut FilteredAccess<ComponentId>,
|
||||
) {
|
||||
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::<T>(),
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
access.add_read(component_id);
|
||||
access.add_component_read(component_id);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
@ -1183,11 +1183,11 @@ unsafe impl<'__w, T: Component> WorldQuery for Ref<'__w, T> {
|
||||
access: &mut FilteredAccess<ComponentId>,
|
||||
) {
|
||||
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::<T>(),
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
access.add_read(component_id);
|
||||
access.add_component_read(component_id);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
@ -1378,11 +1378,11 @@ unsafe impl<'__w, T: Component> WorldQuery for &'__w mut T {
|
||||
access: &mut FilteredAccess<ComponentId>,
|
||||
) {
|
||||
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::<T>(),
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
access.add_write(component_id);
|
||||
access.add_component_write(component_id);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
@ -1476,11 +1476,11 @@ 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::<T>(),
|
||||
std::any::type_name::<T>(),
|
||||
);
|
||||
access.add_write(component_id);
|
||||
access.add_component_write(component_id);
|
||||
}
|
||||
|
||||
// Forwarded to `&mut T`
|
||||
|
@ -688,10 +688,10 @@ unsafe impl<T: Component> WorldQuery for Added<T> {
|
||||
|
||||
#[inline]
|
||||
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
|
||||
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::<T>());
|
||||
}
|
||||
access.add_read(id);
|
||||
access.add_component_read(id);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
@ -899,10 +899,10 @@ unsafe impl<T: Component> WorldQuery for Changed<T> {
|
||||
|
||||
#[inline]
|
||||
fn update_component_access(&id: &ComponentId, access: &mut FilteredAccess<ComponentId>) {
|
||||
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::<T>());
|
||||
}
|
||||
access.add_read(id);
|
||||
access.add_component_read(id);
|
||||
}
|
||||
|
||||
fn init_state(world: &mut World) -> ComponentId {
|
||||
|
@ -495,16 +495,22 @@ impl<D: QueryData, F: QueryFilter> QueryState<D, F> {
|
||||
archetype: &Archetype,
|
||||
access: &mut Access<ArchetypeComponentId>,
|
||||
) {
|
||||
self.component_access.access.reads().for_each(|id| {
|
||||
if let Some(id) = archetype.get_archetype_component_id(id) {
|
||||
access.add_read(id);
|
||||
}
|
||||
});
|
||||
self.component_access.access.writes().for_each(|id| {
|
||||
if let Some(id) = archetype.get_archetype_component_id(id) {
|
||||
access.add_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`].
|
||||
|
@ -737,6 +737,9 @@ mod tests {
|
||||
#[derive(Event)]
|
||||
struct E;
|
||||
|
||||
#[derive(Resource, Component)]
|
||||
struct RC;
|
||||
|
||||
fn empty_system() {}
|
||||
fn res_system(_res: Res<R>) {}
|
||||
fn resmut_system(_res: ResMut<R>) {}
|
||||
@ -746,6 +749,8 @@ mod tests {
|
||||
fn write_component_system(_query: Query<&mut A>) {}
|
||||
fn with_filtered_component_system(_query: Query<&mut A, With<B>>) {}
|
||||
fn without_filtered_component_system(_query: Query<&mut A, Without<B>>) {}
|
||||
fn entity_ref_system(_query: Query<EntityRef>) {}
|
||||
fn entity_mut_system(_query: Query<EntityMut>) {}
|
||||
fn event_reader_system(_reader: EventReader<E>) {}
|
||||
fn event_writer_system(_writer: EventWriter<E>) {}
|
||||
fn event_resource_system(_events: ResMut<Events<E>>) {}
|
||||
@ -788,6 +793,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 +900,73 @@ 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<RC>| {}, |_: 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();
|
||||
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();
|
||||
|
@ -1495,7 +1495,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
system
|
||||
.archetype_component_access()
|
||||
.reads()
|
||||
.component_reads()
|
||||
.collect::<HashSet<_>>(),
|
||||
expected_ids
|
||||
);
|
||||
@ -1525,7 +1525,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
system
|
||||
.archetype_component_access()
|
||||
.reads()
|
||||
.component_reads()
|
||||
.collect::<HashSet<_>>(),
|
||||
expected_ids
|
||||
);
|
||||
@ -1543,7 +1543,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
system
|
||||
.archetype_component_access()
|
||||
.reads()
|
||||
.component_reads()
|
||||
.collect::<HashSet<_>>(),
|
||||
expected_ids
|
||||
);
|
||||
|
@ -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_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::<T>(),
|
||||
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_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_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::<T>(), system_meta.name);
|
||||
} else if combined_access.has_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::<T>(), 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_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_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::<T>(),
|
||||
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_read(archetype_component_id);
|
||||
.add_resource_read(archetype_component_id);
|
||||
|
||||
component_id
|
||||
}
|
||||
@ -1246,25 +1246,25 @@ 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::<T>(), 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::<T>(), 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_non_send_archetype_component_id(component_id)
|
||||
.unwrap();
|
||||
system_meta
|
||||
.archetype_component_access
|
||||
.add_write(archetype_component_id);
|
||||
.add_resource_write(archetype_component_id);
|
||||
|
||||
component_id
|
||||
}
|
||||
|
@ -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<Item = ComponentId> + '_ {
|
||||
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<T: Component>(&self) -> Option<&'w T> {
|
||||
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
|
||||
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<T: Component>(&self) -> Option<Ref<'w, T>> {
|
||||
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
|
||||
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<T: Component>(&self) -> Option<ComponentTicks> {
|
||||
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
|
||||
self.access
|
||||
.has_read(id)
|
||||
.has_component_read(id)
|
||||
// SAFETY: We have read access
|
||||
.then(|| unsafe { self.entity.get_change_ticks::<T>() })
|
||||
.flatten()
|
||||
@ -1953,7 +1953,7 @@ impl<'w> FilteredEntityRef<'w> {
|
||||
#[inline]
|
||||
pub fn get_change_ticks_by_id(&self, component_id: ComponentId) -> Option<ComponentTicks> {
|
||||
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<Ptr<'w>> {
|
||||
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<Item = ComponentId> + '_ {
|
||||
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<T: Component>(&mut self) -> Option<Mut<'_, T>> {
|
||||
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
|
||||
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<T: Component>(self) -> Option<Mut<'w, T>> {
|
||||
let id = self.entity.world().components().get_id(TypeId::of::<T>())?;
|
||||
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<MutUntyped<'_>> {
|
||||
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()
|
||||
|
@ -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;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user