Add FilteredAccess::empty and simplify the implementatin of update_component_access for AnyOf/Or (#14352)
# Objective - The implementation of `update_component_access` for `AnyOf`/`Or` is kinda weird due to special casing the first filter, let's simplify it; - Fundamentally we want to fold/reduce the various filters using an OR operation, however in order to do a proper fold we need a neutral element for the initial accumulator, which for OR is FALSE. However we didn't have a way to create a `FilteredAccess` value corresponding to FALSE and thus the only option was reducing, which special cases the first element as being the initial accumulator. This is an alternative to https://github.com/bevyengine/bevy/pull/14026 ## Solution - Introduce `FilteredAccess::empty` as a way to create a `FilteredAccess` corresponding to the logical proposition FALSE; - Use it as the initial accumulator for the above operations, allowing to handle all the elements to fold in the same way. --- ## Migration Guide - The behaviour of `AnyOf<()>` and `Or<()>` has been changed to match no archetypes rather than all archetypes to naturally match the corresponding logical operation. Consider replacing them with `()` instead.
This commit is contained in:
parent
71c5f1e3e4
commit
7de271f992
@ -336,11 +336,7 @@ pub struct FilteredAccess<T: SparseSetIndex> {
|
|||||||
|
|
||||||
impl<T: SparseSetIndex> Default for FilteredAccess<T> {
|
impl<T: SparseSetIndex> Default for FilteredAccess<T> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::matches_everything()
|
||||||
access: Access::default(),
|
|
||||||
required: FixedBitSet::default(),
|
|
||||||
filter_sets: vec![AccessFilters::default()],
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,6 +349,26 @@ impl<T: SparseSetIndex> From<FilteredAccess<T>> for FilteredAccessSet<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SparseSetIndex> FilteredAccess<T> {
|
impl<T: SparseSetIndex> FilteredAccess<T> {
|
||||||
|
/// Returns a `FilteredAccess` which has no access and matches everything.
|
||||||
|
/// This is the equivalent of a `TRUE` logic atom.
|
||||||
|
pub fn matches_everything() -> Self {
|
||||||
|
Self {
|
||||||
|
access: Access::default(),
|
||||||
|
required: FixedBitSet::default(),
|
||||||
|
filter_sets: vec![AccessFilters::default()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a `FilteredAccess` which has no access and matches nothing.
|
||||||
|
/// This is the equivalent of a `FALSE` logic atom.
|
||||||
|
pub fn matches_nothing() -> Self {
|
||||||
|
Self {
|
||||||
|
access: Access::default(),
|
||||||
|
required: FixedBitSet::default(),
|
||||||
|
filter_sets: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a reference to the underlying unfiltered access.
|
/// Returns a reference to the underlying unfiltered access.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn access(&self) -> &Access<T> {
|
pub fn access(&self) -> &Access<T> {
|
||||||
|
|||||||
@ -1861,30 +1861,30 @@ macro_rules! impl_anytuple_fetch {
|
|||||||
)*)
|
)*)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_component_access(state: &Self::State, _access: &mut FilteredAccess<ComponentId>) {
|
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
||||||
let mut _new_access = _access.clone();
|
|
||||||
|
|
||||||
// update the filters (Or<(With<$name>,)>)
|
// update the filters (Or<(With<$name>,)>)
|
||||||
let ($($name,)*) = state;
|
let ($($name,)*) = state;
|
||||||
let mut _not_first = false;
|
|
||||||
|
let mut _new_access = FilteredAccess::matches_nothing();
|
||||||
|
|
||||||
$(
|
$(
|
||||||
if _not_first {
|
// Create an intermediate because `access`'s value needs to be preserved
|
||||||
// we use an intermediate access because we only want to update the filter_sets, not the access
|
// for the next query data, and `_new_access` has to be modified only by `append_or` to it,
|
||||||
let mut intermediate = _access.clone();
|
// which only updates the `filter_sets`, not the `access`.
|
||||||
$name::update_component_access($name, &mut intermediate);
|
let mut intermediate = access.clone();
|
||||||
_new_access.append_or(&intermediate);
|
$name::update_component_access($name, &mut intermediate);
|
||||||
} else {
|
_new_access.append_or(&intermediate);
|
||||||
$name::update_component_access($name, &mut _new_access);
|
|
||||||
_new_access.required = _access.required.clone();
|
|
||||||
_not_first = true;
|
|
||||||
}
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
_access.filter_sets = _new_access.filter_sets;
|
// Of the accumulated `_new_access` we only care about the filter sets, not the access.
|
||||||
|
access.filter_sets = _new_access.filter_sets;
|
||||||
|
|
||||||
// update the access (add the read/writes)
|
// For the access we instead delegate to a tuple of `Option`s.
|
||||||
// Option<T> updates the access but not the filter_sets
|
// This has essentially the same semantics of `AnyOf`, except that it doesn't
|
||||||
<($(Option<$name>,)*)>::update_component_access(state, _access);
|
// require at least one of them to be `Some`.
|
||||||
|
// We however solve this by setting explicitly the `filter_sets` above.
|
||||||
|
// Also note that Option<T> updates the `access` but not the `filter_sets`.
|
||||||
|
<($(Option<$name>,)*)>::update_component_access(state, access);
|
||||||
|
|
||||||
}
|
}
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
|
|||||||
@ -443,21 +443,22 @@ macro_rules! impl_or_query_filter {
|
|||||||
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
fn update_component_access(state: &Self::State, access: &mut FilteredAccess<ComponentId>) {
|
||||||
let ($($filter,)*) = state;
|
let ($($filter,)*) = state;
|
||||||
|
|
||||||
let mut _new_access = access.clone();
|
let mut _new_access = FilteredAccess::matches_nothing();
|
||||||
let mut _not_first = false;
|
|
||||||
$(
|
$(
|
||||||
if _not_first {
|
// Create an intermediate because `access`'s value needs to be preserved
|
||||||
let mut intermediate = access.clone();
|
// for the next filter, and `_new_access` has to be modified only by `append_or` to it.
|
||||||
$filter::update_component_access($filter, &mut intermediate);
|
let mut intermediate = access.clone();
|
||||||
_new_access.append_or(&intermediate);
|
$filter::update_component_access($filter, &mut intermediate);
|
||||||
_new_access.extend_access(&intermediate);
|
_new_access.append_or(&intermediate);
|
||||||
} else {
|
// Also extend the accesses required to compute the filter. This is required because
|
||||||
$filter::update_component_access($filter, &mut _new_access);
|
// otherwise a `Query<(), Or<(Changed<Foo>,)>` won't conflict with `Query<&mut Foo>`.
|
||||||
_new_access.required = access.required.clone();
|
_new_access.extend_access(&intermediate);
|
||||||
_not_first = true;
|
|
||||||
}
|
|
||||||
)*
|
)*
|
||||||
|
|
||||||
|
// The required components remain the same as the original `access`.
|
||||||
|
_new_access.required = std::mem::take(&mut access.required);
|
||||||
|
|
||||||
*access = _new_access;
|
*access = _new_access;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -808,6 +808,15 @@ mod tests {
|
|||||||
run_system(&mut world, sys);
|
run_system(&mut world, sys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic]
|
||||||
|
fn changed_trackers_or_conflict() {
|
||||||
|
fn sys(_: Query<&mut A>, _: Query<(), Or<(Changed<A>,)>>) {}
|
||||||
|
|
||||||
|
let mut world = World::default();
|
||||||
|
run_system(&mut world, sys);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn query_set_system() {
|
fn query_set_system() {
|
||||||
fn sys(mut _set: ParamSet<(Query<&mut A>, Query<&A>)>) {}
|
fn sys(mut _set: ParamSet<(Query<&mut A>, Query<&A>)>) {}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user