Skip to content

Commit 9b91b56

Browse files
Expose more data from FilteredAccess (#23384)
# Objective Expose more data from `FilteredAccess` to allow third-party crates to inspect queries. In particular, expose `required` and `filter_sets`, which will allow third-party implementations of query observers (#20817) to determine what component observers they will need. ## Solution Add `pub` methods to expose `required` and `filter_sets`, make `AccessFilters` `pub`, and add methods to expose `with` and `without`. Add a `ComponentIdSet` type that wraps `FixedBitSet`. This allows us to expose the efficient set operations like `union_with` that would not be available if we returned a simple `Iterator<Item = ComponentId>`. And not exposing the `FixedBitSet` directly avoids forcing users to manually convert between `usize` and `ComponentId`, and will let us change the implementation of the set (as in #18955) or representation of `ComponentId` without changing the public API. It also simplifies the internals a bit! ## Showcase Here is how to calculate the component observers required for a query observer in an implementation of #20817 using these APIs: ```rust fn components_for_observers( access: &FilteredAccess, ) -> (&ComponentIdSet, ComponentIdSet, ComponentIdSet) { // We need `insert` observers for any component we read, // since the value has changed. // Unbounded access (`EntityRef`) is not supported, // since we cannot add observers to all possible components. let insert = access .access() .try_reads_and_writes() .expect("Query observers do not support unbounded access"); // We need `add` observers for any component with archetypal access, // since `Has` may have changed, let mut add = access.access().archetypal().clone(); // and any component with `With` filters, // since the query may start matching, for filter_set in access.filter_sets() { add.union_with(&filter_set.with()); } // but not for any component already covered by `insert` observers add.difference_with(insert); // We need `remove` observers for any component we read or write, // since `Option<&T>` may have changed, let mut remove = insert.clone(); // and for any component with archetypal access, // since `Has` may have changed, remove.union_with(access.access().archetypal()); // and any component with `Without` filters, // since the query may start matching, for filter_set in access.filter_sets() { add.union_with(&filter_set.without()); } // but not for any required component, // since it is guaranteed not to match after they are removed // (otherwise every `&T` would add a `remove` observer!) remove.difference_with(access.required()); (insert, add, remove) } ``` --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
1 parent 1aea391 commit 9b91b56

4 files changed

Lines changed: 462 additions & 195 deletions

File tree

0 commit comments

Comments
 (0)