11use crate :: storage:: SparseSetIndex ;
22use bevy_utils:: HashSet ;
3+ use core:: fmt;
34use fixedbitset:: FixedBitSet ;
45use std:: marker:: PhantomData ;
56
7+ /// A wrapper struct to make Debug representations of [`FixedBitSet`] easier
8+ /// to read, when used to store [`SparseSetIndex`].
9+ ///
10+ /// Instead of the raw integer representation of the `FixedBitSet`, the list of
11+ /// `T` valid for [`SparseSetIndex`] is shown.
12+ ///
13+ /// Normal `FixedBitSet` `Debug` output:
14+ /// ```text
15+ /// read_and_writes: FixedBitSet { data: [ 160 ], length: 8 }
16+ /// ```
17+ ///
18+ /// Which, unless you are a computer, doesn't help much understand what's in
19+ /// the set. With `FormattedBitSet`, we convert the present set entries into
20+ /// what they stand for, it is much clearer what is going on:
21+ /// ```text
22+ /// read_and_writes: [ ComponentId(5), ComponentId(7) ]
23+ /// ```
24+ struct FormattedBitSet < ' a , T : SparseSetIndex > {
25+ bit_set : & ' a FixedBitSet ,
26+ _marker : PhantomData < T > ,
27+ }
28+ impl < ' a , T : SparseSetIndex > FormattedBitSet < ' a , T > {
29+ fn new ( bit_set : & ' a FixedBitSet ) -> Self {
30+ Self {
31+ bit_set,
32+ _marker : PhantomData ,
33+ }
34+ }
35+ }
36+ impl < ' a , T : SparseSetIndex + fmt:: Debug > fmt:: Debug for FormattedBitSet < ' a , T > {
37+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
38+ f. debug_list ( )
39+ . entries ( self . bit_set . ones ( ) . map ( T :: get_sparse_set_index) )
40+ . finish ( )
41+ }
42+ }
43+
644/// Tracks read and write access to specific elements in a collection.
745///
846/// Used internally to ensure soundness during system initialization and execution.
947/// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions.
10- #[ derive( Debug , Clone , Eq , PartialEq ) ]
48+ #[ derive( Clone , Eq , PartialEq ) ]
1149pub struct Access < T : SparseSetIndex > {
1250 /// All accessed elements.
1351 reads_and_writes : FixedBitSet ,
@@ -19,6 +57,18 @@ pub struct Access<T: SparseSetIndex> {
1957 marker : PhantomData < T > ,
2058}
2159
60+ impl < T : SparseSetIndex + fmt:: Debug > fmt:: Debug for Access < T > {
61+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
62+ f. debug_struct ( "Access" )
63+ . field (
64+ "read_and_writes" ,
65+ & FormattedBitSet :: < T > :: new ( & self . reads_and_writes ) ,
66+ )
67+ . field ( "writes" , & FormattedBitSet :: < T > :: new ( & self . writes ) )
68+ . field ( "reads_all" , & self . reads_all )
69+ . finish ( )
70+ }
71+ }
2272impl < T : SparseSetIndex > Default for Access < T > {
2373 fn default ( ) -> Self {
2474 Self {
@@ -55,11 +105,7 @@ impl<T: SparseSetIndex> Access<T> {
55105
56106 /// Returns `true` if this can access the element given by `index`.
57107 pub fn has_read ( & self , index : T ) -> bool {
58- if self . reads_all {
59- true
60- } else {
61- self . reads_and_writes . contains ( index. sparse_set_index ( ) )
62- }
108+ self . reads_all || self . reads_and_writes . contains ( index. sparse_set_index ( ) )
63109 }
64110
65111 /// Returns `true` if this can exclusively access the element given by `index`.
@@ -106,7 +152,7 @@ impl<T: SparseSetIndex> Access<T> {
106152 }
107153
108154 self . writes . is_disjoint ( & other. reads_and_writes )
109- && self . reads_and_writes . is_disjoint ( & other . writes )
155+ && other . writes . is_disjoint ( & self . reads_and_writes )
110156 }
111157
112158 /// Returns a vector of elements that the access and `other` cannot access at the same time.
@@ -153,7 +199,7 @@ impl<T: SparseSetIndex> Access<T> {
153199/// `with` access.
154200///
155201/// For example consider `Query<Option<&T>>` this only has a `read` of `T` as doing
156- /// otherwise would allow for queries to be considered disjoint that actually aren 't:
202+ /// otherwise would allow for queries to be considered disjoint when they shouldn 't:
157203/// - `Query<(&mut T, Option<&U>)>` read/write `T`, read `U`, with `U`
158204/// - `Query<&mut T, Without<U>>` read/write `T`, without `U`
159205/// from this we could reasonably conclude that the queries are disjoint but they aren't.
@@ -165,12 +211,21 @@ impl<T: SparseSetIndex> Access<T> {
165211/// - `Query<Option<&T>` accesses nothing
166212///
167213/// See comments the `WorldQuery` impls of `AnyOf`/`Option`/`Or` for more information.
168- #[ derive( Debug , Clone , Eq , PartialEq ) ]
214+ #[ derive( Clone , Eq , PartialEq ) ]
169215pub struct FilteredAccess < T : SparseSetIndex > {
170216 access : Access < T > ,
171217 with : FixedBitSet ,
172218 without : FixedBitSet ,
173219}
220+ impl < T : SparseSetIndex + fmt:: Debug > fmt:: Debug for FilteredAccess < T > {
221+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
222+ f. debug_struct ( "FilteredAccess" )
223+ . field ( "access" , & self . access )
224+ . field ( "with" , & FormattedBitSet :: < T > :: new ( & self . with ) )
225+ . field ( "without" , & FormattedBitSet :: < T > :: new ( & self . without ) )
226+ . finish ( )
227+ }
228+ }
174229
175230impl < T : SparseSetIndex > Default for FilteredAccess < T > {
176231 fn default ( ) -> Self {
@@ -238,12 +293,9 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
238293
239294 /// Returns `true` if this and `other` can be active at the same time.
240295 pub fn is_compatible ( & self , other : & FilteredAccess < T > ) -> bool {
241- if self . access . is_compatible ( & other. access ) {
242- true
243- } else {
244- self . with . intersection ( & other. without ) . next ( ) . is_some ( )
245- || self . without . intersection ( & other. with ) . next ( ) . is_some ( )
246- }
296+ self . access . is_compatible ( & other. access )
297+ || !self . with . is_disjoint ( & other. without )
298+ || !other. with . is_disjoint ( & self . without )
247299 }
248300
249301 /// Returns a vector of elements that this and `other` cannot access at the same time.
@@ -271,6 +323,10 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
271323/// A collection of [`FilteredAccess`] instances.
272324///
273325/// Used internally to statically check if systems have conflicting access.
326+ ///
327+ /// It stores multiple sets of accesses.
328+ /// - A "combined" set, which is the access of all filters in this set combined.
329+ /// - The set of access of each individual filters in this set.
274330#[ derive( Debug , Clone ) ]
275331pub struct FilteredAccessSet < T : SparseSetIndex > {
276332 combined_access : Access < T > ,
@@ -284,13 +340,18 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
284340 & self . combined_access
285341 }
286342
287- /// Returns a mutable reference to the unfiltered access of the entire set.
288- #[ inline]
289- pub fn combined_access_mut ( & mut self ) -> & mut Access < T > {
290- & mut self . combined_access
291- }
292-
293343 /// Returns `true` if this and `other` can be active at the same time.
344+ ///
345+ /// Access conflict resolution happen in two steps:
346+ /// 1. A "coarse" check, if there is no mutual unfiltered conflict between
347+ /// `self` and `other`, we already know that the two access sets are
348+ /// compatible.
349+ /// 2. A "fine grained" check, it kicks in when the "coarse" check fails.
350+ /// the two access sets might still be compatible if some of the accesses
351+ /// are restricted with the `With` or `Without` filters so that access is
352+ /// mutually exclusive. The fine grained phase iterates over all filters in
353+ /// the `self` set and compares it to all the filters in the `other` set,
354+ /// making sure they are all mutually compatible.
294355 pub fn is_compatible ( & self , other : & FilteredAccessSet < T > ) -> bool {
295356 if self . combined_access . is_compatible ( other. combined_access ( ) ) {
296357 return true ;
@@ -302,7 +363,6 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
302363 }
303364 }
304365 }
305-
306366 true
307367 }
308368
@@ -338,6 +398,20 @@ impl<T: SparseSetIndex> FilteredAccessSet<T> {
338398 self . filtered_accesses . push ( filtered_access) ;
339399 }
340400
401+ /// Adds a read access without filters to the set.
402+ pub ( crate ) fn add_unfiltered_read ( & mut self , index : T ) {
403+ let mut filter = FilteredAccess :: default ( ) ;
404+ filter. add_read ( index) ;
405+ self . add ( filter) ;
406+ }
407+
408+ /// Adds a write access without filters to the set.
409+ pub ( crate ) fn add_unfiltered_write ( & mut self , index : T ) {
410+ let mut filter = FilteredAccess :: default ( ) ;
411+ filter. add_write ( index) ;
412+ self . add ( filter) ;
413+ }
414+
341415 pub fn extend ( & mut self , filtered_access_set : FilteredAccessSet < T > ) {
342416 self . combined_access
343417 . extend ( & filtered_access_set. combined_access ) ;
@@ -362,7 +436,30 @@ impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
362436
363437#[ cfg( test) ]
364438mod tests {
365- use crate :: query:: { Access , FilteredAccess } ;
439+ use crate :: query:: { Access , FilteredAccess , FilteredAccessSet } ;
440+
441+ #[ test]
442+ fn read_all_access_conflicts ( ) {
443+ // read_all / single write
444+ let mut access_a = Access :: < usize > :: default ( ) ;
445+ access_a. grow ( 10 ) ;
446+ access_a. add_write ( 0 ) ;
447+
448+ let mut access_b = Access :: < usize > :: default ( ) ;
449+ access_b. read_all ( ) ;
450+
451+ assert ! ( !access_b. is_compatible( & access_a) ) ;
452+
453+ // read_all / read_all
454+ let mut access_a = Access :: < usize > :: default ( ) ;
455+ access_a. grow ( 10 ) ;
456+ access_a. read_all ( ) ;
457+
458+ let mut access_b = Access :: < usize > :: default ( ) ;
459+ access_b. read_all ( ) ;
460+
461+ assert ! ( access_b. is_compatible( & access_a) ) ;
462+ }
366463
367464 #[ test]
368465 fn access_get_conflicts ( ) {
@@ -391,6 +488,22 @@ mod tests {
391488 assert_eq ! ( access_d. get_conflicts( & access_c) , vec![ 0 ] ) ;
392489 }
393490
491+ #[ test]
492+ fn filtered_combined_access ( ) {
493+ let mut access_a = FilteredAccessSet :: < usize > :: default ( ) ;
494+ access_a. add_unfiltered_read ( 1 ) ;
495+
496+ let mut filter_b = FilteredAccess :: < usize > :: default ( ) ;
497+ filter_b. add_write ( 1 ) ;
498+
499+ let conflicts = access_a. get_conflicts_single ( & filter_b) ;
500+ assert_eq ! (
501+ & conflicts,
502+ & [ 1_usize ] ,
503+ "access_a: {access_a:?}, filter_b: {filter_b:?}"
504+ ) ;
505+ }
506+
394507 #[ test]
395508 fn filtered_access_extend ( ) {
396509 let mut access_a = FilteredAccess :: < usize > :: default ( ) ;
0 commit comments