Skip to content

Commit bea0a0a

Browse files
authored
Let FilteredEntity(Ref|Mut) receive access when nested. (#18236)
# Objective Let `FilteredEntityRef` and `FilteredEntityMut` receive access when nested inside tuples or `#[derive(QueryData)]` types. Make sure to exclude any access that would conflict with other subqueries! Fixes #14349 ## Solution Replace `WorldQuery::set_access(state, access)` with a new method, `QueryData::provide_extra_access(state, access, available_access)`, that passes both the total available access and the currently used access. This is called after `WorldQuery::update_component_access()`, so any access used by ordinary subqueries will be known. `FilteredEntityRef` and `FilteredEntityMut` can use the combination to determine how much access they can safely take, while tuples can safely pass those parameters directly to their subqueries. This requires a new `Access::remove_conflicting_access()` method that can be used to remove any access that would conflict with existing access. Implementing this method was easier by first factoring some common set manipulation code out of `Access::extend`. I can extract that refactoring to a separate PR if desired. Have `FilteredEntity(Ref|Mut)` store `Access` instead of `FilteredAccess` because they do not need to keep track of the filter. This was necessary in an early draft but no longer is. I left it in because it's small and I'm touching that code anyway, but I can extract it to a separate PR if desired.
1 parent 02d569d commit bea0a0a

File tree

8 files changed

+407
-142
lines changed

8 files changed

+407
-142
lines changed

crates/bevy_ecs/macros/src/query_data.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,14 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
268268
}
269269
}
270270

271+
fn provide_extra_access(
272+
state: &mut Self::State,
273+
access: &mut #path::query::Access<#path::component::ComponentId>,
274+
available_access: &#path::query::Access<#path::component::ComponentId>,
275+
) {
276+
#(<#field_types>::provide_extra_access(&mut state.#named_field_idents, access, available_access);)*
277+
}
278+
271279
/// SAFETY: we call `fetch` for each member that implements `Fetch`.
272280
#[inline(always)]
273281
unsafe fn fetch<'__w>(
@@ -305,6 +313,14 @@ pub fn derive_query_data_impl(input: TokenStream) -> TokenStream {
305313
}
306314
}
307315

316+
fn provide_extra_access(
317+
state: &mut Self::State,
318+
access: &mut #path::query::Access<#path::component::ComponentId>,
319+
available_access: &#path::query::Access<#path::component::ComponentId>,
320+
) {
321+
#(<#field_types>::provide_extra_access(&mut state.#named_field_idents, access, available_access);)*
322+
}
323+
308324
/// SAFETY: we call `fetch` for each member that implements `Fetch`.
309325
#[inline(always)]
310326
unsafe fn fetch<'__w>(

crates/bevy_ecs/src/query/access.rs

Lines changed: 186 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -431,78 +431,58 @@ impl<T: SparseSetIndex> Access<T> {
431431

432432
/// Adds all access from `other`.
433433
pub fn extend(&mut self, other: &Access<T>) {
434-
let component_read_and_writes_inverted =
435-
self.component_read_and_writes_inverted || other.component_read_and_writes_inverted;
436-
let component_writes_inverted =
437-
self.component_writes_inverted || other.component_writes_inverted;
438-
439-
match (
440-
self.component_read_and_writes_inverted,
434+
invertible_union_with(
435+
&mut self.component_read_and_writes,
436+
&mut self.component_read_and_writes_inverted,
437+
&other.component_read_and_writes,
441438
other.component_read_and_writes_inverted,
442-
) {
443-
(true, true) => {
444-
self.component_read_and_writes
445-
.intersect_with(&other.component_read_and_writes);
446-
}
447-
(true, false) => {
448-
self.component_read_and_writes
449-
.difference_with(&other.component_read_and_writes);
450-
}
451-
(false, true) => {
452-
// We have to grow here because the new bits are going to get flipped to 1.
453-
self.component_read_and_writes.grow(
454-
self.component_read_and_writes
455-
.len()
456-
.max(other.component_read_and_writes.len()),
457-
);
458-
self.component_read_and_writes.toggle_range(..);
459-
self.component_read_and_writes
460-
.intersect_with(&other.component_read_and_writes);
461-
}
462-
(false, false) => {
463-
self.component_read_and_writes
464-
.union_with(&other.component_read_and_writes);
465-
}
466-
}
467-
468-
match (
469-
self.component_writes_inverted,
439+
);
440+
invertible_union_with(
441+
&mut self.component_writes,
442+
&mut self.component_writes_inverted,
443+
&other.component_writes,
470444
other.component_writes_inverted,
471-
) {
472-
(true, true) => {
473-
self.component_writes
474-
.intersect_with(&other.component_writes);
475-
}
476-
(true, false) => {
477-
self.component_writes
478-
.difference_with(&other.component_writes);
479-
}
480-
(false, true) => {
481-
// We have to grow here because the new bits are going to get flipped to 1.
482-
self.component_writes.grow(
483-
self.component_writes
484-
.len()
485-
.max(other.component_writes.len()),
486-
);
487-
self.component_writes.toggle_range(..);
488-
self.component_writes
489-
.intersect_with(&other.component_writes);
490-
}
491-
(false, false) => {
492-
self.component_writes.union_with(&other.component_writes);
493-
}
494-
}
445+
);
495446

496447
self.reads_all_resources = self.reads_all_resources || other.reads_all_resources;
497448
self.writes_all_resources = self.writes_all_resources || other.writes_all_resources;
498-
self.component_read_and_writes_inverted = component_read_and_writes_inverted;
499-
self.component_writes_inverted = component_writes_inverted;
500449
self.resource_read_and_writes
501450
.union_with(&other.resource_read_and_writes);
502451
self.resource_writes.union_with(&other.resource_writes);
503452
self.archetypal.union_with(&other.archetypal);
504453
}
505454

455+
/// Removes any access from `self` that would conflict with `other`.
456+
/// This removes any reads and writes for any component written by `other`,
457+
/// and removes any writes for any component read by `other`.
458+
pub fn remove_conflicting_access(&mut self, other: &Access<T>) {
459+
invertible_difference_with(
460+
&mut self.component_read_and_writes,
461+
&mut self.component_read_and_writes_inverted,
462+
&other.component_writes,
463+
other.component_writes_inverted,
464+
);
465+
invertible_difference_with(
466+
&mut self.component_writes,
467+
&mut self.component_writes_inverted,
468+
&other.component_read_and_writes,
469+
other.component_read_and_writes_inverted,
470+
);
471+
472+
if other.reads_all_resources {
473+
self.writes_all_resources = false;
474+
self.resource_writes.clear();
475+
}
476+
if other.writes_all_resources {
477+
self.reads_all_resources = false;
478+
self.resource_read_and_writes.clear();
479+
}
480+
self.resource_read_and_writes
481+
.difference_with(&other.resource_writes);
482+
self.resource_writes
483+
.difference_with(&other.resource_read_and_writes);
484+
}
485+
506486
/// Returns `true` if the access and `other` can be active at the same time,
507487
/// only looking at their component access.
508488
///
@@ -840,6 +820,55 @@ impl<T: SparseSetIndex> Access<T> {
840820
}
841821
}
842822

823+
/// Performs an in-place union of `other` into `self`, where either set may be inverted.
824+
///
825+
/// Each set corresponds to a `FixedBitSet` if `inverted` is `false`,
826+
/// or to the infinite (co-finite) complement of the `FixedBitSet` if `inverted` is `true`.
827+
///
828+
/// This updates the `self` set to include any elements in the `other` set.
829+
/// Note that this may change `self_inverted` to `true` if we add an infinite
830+
/// set to a finite one, resulting in a new infinite set.
831+
fn invertible_union_with(
832+
self_set: &mut FixedBitSet,
833+
self_inverted: &mut bool,
834+
other_set: &FixedBitSet,
835+
other_inverted: bool,
836+
) {
837+
match (*self_inverted, other_inverted) {
838+
(true, true) => self_set.intersect_with(other_set),
839+
(true, false) => self_set.difference_with(other_set),
840+
(false, true) => {
841+
*self_inverted = true;
842+
// We have to grow here because the new bits are going to get flipped to 1.
843+
self_set.grow(other_set.len());
844+
self_set.toggle_range(..);
845+
self_set.intersect_with(other_set);
846+
}
847+
(false, false) => self_set.union_with(other_set),
848+
}
849+
}
850+
851+
/// Performs an in-place set difference of `other` from `self`, where either set may be inverted.
852+
///
853+
/// Each set corresponds to a `FixedBitSet` if `inverted` is `false`,
854+
/// or to the infinite (co-finite) complement of the `FixedBitSet` if `inverted` is `true`.
855+
///
856+
/// This updates the `self` set to remove any elements in the `other` set.
857+
/// Note that this may change `self_inverted` to `false` if we remove an
858+
/// infinite set from another infinite one, resulting in a finite difference.
859+
fn invertible_difference_with(
860+
self_set: &mut FixedBitSet,
861+
self_inverted: &mut bool,
862+
other_set: &FixedBitSet,
863+
other_inverted: bool,
864+
) {
865+
// We can share the implementation of `invertible_union_with` with some algebra:
866+
// A - B = A & !B = !(!A | B)
867+
*self_inverted = !*self_inverted;
868+
invertible_union_with(self_set, self_inverted, other_set, other_inverted);
869+
*self_inverted = !*self_inverted;
870+
}
871+
843872
/// Error returned when attempting to iterate over items included in an [`Access`]
844873
/// if the access excludes items rather than including them.
845874
#[derive(Clone, Copy, PartialEq, Eq, Debug, Error)]
@@ -1428,6 +1457,7 @@ impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
14281457

14291458
#[cfg(test)]
14301459
mod tests {
1460+
use super::{invertible_difference_with, invertible_union_with};
14311461
use crate::query::{
14321462
access::AccessFilters, Access, AccessConflicts, ComponentAccessKind, FilteredAccess,
14331463
FilteredAccessSet, UnboundedAccessError,
@@ -1770,4 +1800,99 @@ mod tests {
17701800
}),
17711801
);
17721802
}
1803+
1804+
/// Create a `FixedBitSet` with a given number of total bits and a given list of bits to set.
1805+
/// Setting the number of bits is important in tests since the `PartialEq` impl checks that the length matches.
1806+
fn bit_set(bits: usize, iter: impl IntoIterator<Item = usize>) -> FixedBitSet {
1807+
let mut result = FixedBitSet::with_capacity(bits);
1808+
result.extend(iter);
1809+
result
1810+
}
1811+
1812+
#[test]
1813+
fn invertible_union_with_tests() {
1814+
let invertible_union = |mut self_inverted: bool, other_inverted: bool| {
1815+
// Check all four possible bit states: In both sets, the first, the second, or neither
1816+
let mut self_set = bit_set(4, [0, 1]);
1817+
let other_set = bit_set(4, [0, 2]);
1818+
invertible_union_with(
1819+
&mut self_set,
1820+
&mut self_inverted,
1821+
&other_set,
1822+
other_inverted,
1823+
);
1824+
(self_set, self_inverted)
1825+
};
1826+
1827+
// Check each combination of `inverted` flags
1828+
let (s, i) = invertible_union(false, false);
1829+
// [0, 1] | [0, 2] = [0, 1, 2]
1830+
assert_eq!((s, i), (bit_set(4, [0, 1, 2]), false));
1831+
1832+
let (s, i) = invertible_union(false, true);
1833+
// [0, 1] | [1, 3, ...] = [0, 1, 3, ...]
1834+
assert_eq!((s, i), (bit_set(4, [2]), true));
1835+
1836+
let (s, i) = invertible_union(true, false);
1837+
// [2, 3, ...] | [0, 2] = [0, 2, 3, ...]
1838+
assert_eq!((s, i), (bit_set(4, [1]), true));
1839+
1840+
let (s, i) = invertible_union(true, true);
1841+
// [2, 3, ...] | [1, 3, ...] = [1, 2, 3, ...]
1842+
assert_eq!((s, i), (bit_set(4, [0]), true));
1843+
}
1844+
1845+
#[test]
1846+
fn invertible_union_with_different_lengths() {
1847+
// When adding a large inverted set to a small normal set,
1848+
// make sure we invert the bits beyond the original length.
1849+
// Failing to call `grow` before `toggle_range` would cause bit 1 to be zero,
1850+
// which would incorrectly treat it as included in the output set.
1851+
let mut self_set = bit_set(1, [0]);
1852+
let mut self_inverted = false;
1853+
let other_set = bit_set(3, [0, 1]);
1854+
let other_inverted = true;
1855+
invertible_union_with(
1856+
&mut self_set,
1857+
&mut self_inverted,
1858+
&other_set,
1859+
other_inverted,
1860+
);
1861+
1862+
// [0] | [2, ...] = [0, 2, ...]
1863+
assert_eq!((self_set, self_inverted), (bit_set(3, [1]), true));
1864+
}
1865+
1866+
#[test]
1867+
fn invertible_difference_with_tests() {
1868+
let invertible_difference = |mut self_inverted: bool, other_inverted: bool| {
1869+
// Check all four possible bit states: In both sets, the first, the second, or neither
1870+
let mut self_set = bit_set(4, [0, 1]);
1871+
let other_set = bit_set(4, [0, 2]);
1872+
invertible_difference_with(
1873+
&mut self_set,
1874+
&mut self_inverted,
1875+
&other_set,
1876+
other_inverted,
1877+
);
1878+
(self_set, self_inverted)
1879+
};
1880+
1881+
// Check each combination of `inverted` flags
1882+
let (s, i) = invertible_difference(false, false);
1883+
// [0, 1] - [0, 2] = [1]
1884+
assert_eq!((s, i), (bit_set(4, [1]), false));
1885+
1886+
let (s, i) = invertible_difference(false, true);
1887+
// [0, 1] - [1, 3, ...] = [0]
1888+
assert_eq!((s, i), (bit_set(4, [0]), false));
1889+
1890+
let (s, i) = invertible_difference(true, false);
1891+
// [2, 3, ...] - [0, 2] = [3, ...]
1892+
assert_eq!((s, i), (bit_set(4, [0, 1, 2]), true));
1893+
1894+
let (s, i) = invertible_difference(true, true);
1895+
// [2, 3, ...] - [1, 3, ...] = [2]
1896+
assert_eq!((s, i), (bit_set(4, [2]), false));
1897+
}
17731898
}

0 commit comments

Comments
 (0)