@@ -25,6 +25,7 @@ struct FormattedBitSet<'a, T: SparseSetIndex> {
25
25
bit_set : & ' a FixedBitSet ,
26
26
_marker : PhantomData < T > ,
27
27
}
28
+
28
29
impl < ' a , T : SparseSetIndex > FormattedBitSet < ' a , T > {
29
30
fn new ( bit_set : & ' a FixedBitSet ) -> Self {
30
31
Self {
@@ -33,6 +34,7 @@ impl<'a, T: SparseSetIndex> FormattedBitSet<'a, T> {
33
34
}
34
35
}
35
36
}
37
+
36
38
impl < ' a , T : SparseSetIndex + fmt:: Debug > fmt:: Debug for FormattedBitSet < ' a , T > {
37
39
fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
38
40
f. debug_list ( )
@@ -69,6 +71,7 @@ impl<T: SparseSetIndex + fmt::Debug> fmt::Debug for Access<T> {
69
71
. finish ( )
70
72
}
71
73
}
74
+
72
75
impl < T : SparseSetIndex > Default for Access < T > {
73
76
fn default ( ) -> Self {
74
77
Self :: new ( )
@@ -213,31 +216,22 @@ impl<T: SparseSetIndex> Access<T> {
213
216
/// is read/write `T`, read `U`. It must still have a read `U` access otherwise the following
214
217
/// queries would be incorrectly considered disjoint:
215
218
/// - `Query<&mut T>` read/write `T`
216
- /// - `Query<Option<&T>` accesses nothing
219
+ /// - `Query<Option<&T>> ` accesses nothing
217
220
///
218
221
/// See comments the `WorldQuery` impls of `AnyOf`/`Option`/`Or` for more information.
219
- #[ derive( Clone , Eq , PartialEq ) ]
222
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
220
223
pub struct FilteredAccess < T : SparseSetIndex > {
221
224
access : Access < T > ,
222
- with : FixedBitSet ,
223
- without : FixedBitSet ,
224
- }
225
- impl < T : SparseSetIndex + fmt:: Debug > fmt:: Debug for FilteredAccess < T > {
226
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
227
- f. debug_struct ( "FilteredAccess" )
228
- . field ( "access" , & self . access )
229
- . field ( "with" , & FormattedBitSet :: < T > :: new ( & self . with ) )
230
- . field ( "without" , & FormattedBitSet :: < T > :: new ( & self . without ) )
231
- . finish ( )
232
- }
225
+ // An array of filter sets to express `With` or `Without` clauses in disjunctive normal form, for example: `Or<(With<A>, With<B>)>`.
226
+ // Filters like `(With<A>, Or<(With<B>, Without<C>)>` are expanded into `Or<((With<A>, With<B>), (With<A>, Without<C>))>`.
227
+ filter_sets : Vec < AccessFilters < T > > ,
233
228
}
234
229
235
230
impl < T : SparseSetIndex > Default for FilteredAccess < T > {
236
231
fn default ( ) -> Self {
237
232
Self {
238
233
access : Access :: default ( ) ,
239
- with : Default :: default ( ) ,
240
- without : Default :: default ( ) ,
234
+ filter_sets : vec ! [ AccessFilters :: default ( ) ] ,
241
235
}
242
236
}
243
237
}
@@ -266,30 +260,46 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
266
260
/// Adds access to the element given by `index`.
267
261
pub fn add_read ( & mut self , index : T ) {
268
262
self . access . add_read ( index. clone ( ) ) ;
269
- self . add_with ( index) ;
263
+ self . and_with ( index) ;
270
264
}
271
265
272
266
/// Adds exclusive access to the element given by `index`.
273
267
pub fn add_write ( & mut self , index : T ) {
274
268
self . access . add_write ( index. clone ( ) ) ;
275
- self . add_with ( index) ;
269
+ self . and_with ( index) ;
276
270
}
277
271
278
- /// Retains only combinations where the element given by `index` is also present.
279
- pub fn add_with ( & mut self , index : T ) {
280
- self . with . grow ( index. sparse_set_index ( ) + 1 ) ;
281
- self . with . insert ( index. sparse_set_index ( ) ) ;
272
+ /// Adds a `With` filter: corresponds to a conjunction (AND) operation.
273
+ ///
274
+ /// Suppose we begin with `Or<(With<A>, With<B>)>`, which is represented by an array of two `AccessFilter` instances.
275
+ /// Adding `AND With<C>` via this method transforms it into the equivalent of `Or<((With<A>, With<C>), (With<B>, With<C>))>`.
276
+ pub fn and_with ( & mut self , index : T ) {
277
+ let index = index. sparse_set_index ( ) ;
278
+ for filter in & mut self . filter_sets {
279
+ filter. with . grow ( index + 1 ) ;
280
+ filter. with . insert ( index) ;
281
+ }
282
282
}
283
283
284
- /// Retains only combinations where the element given by `index` is not present.
285
- pub fn add_without ( & mut self , index : T ) {
286
- self . without . grow ( index. sparse_set_index ( ) + 1 ) ;
287
- self . without . insert ( index. sparse_set_index ( ) ) ;
284
+ /// Adds a `Without` filter: corresponds to a conjunction (AND) operation.
285
+ ///
286
+ /// Suppose we begin with `Or<(With<A>, With<B>)>`, which is represented by an array of two `AccessFilter` instances.
287
+ /// Adding `AND Without<C>` via this method transforms it into the equivalent of `Or<((With<A>, Without<C>), (With<B>, Without<C>))>`.
288
+ pub fn and_without ( & mut self , index : T ) {
289
+ let index = index. sparse_set_index ( ) ;
290
+ for filter in & mut self . filter_sets {
291
+ filter. without . grow ( index + 1 ) ;
292
+ filter. without . insert ( index) ;
293
+ }
288
294
}
289
295
290
- pub fn extend_intersect_filter ( & mut self , other : & FilteredAccess < T > ) {
291
- self . without . intersect_with ( & other. without ) ;
292
- self . with . intersect_with ( & other. with ) ;
296
+ /// Appends an array of filters: corresponds to a disjunction (OR) operation.
297
+ ///
298
+ /// As the underlying array of filters represents a disjunction,
299
+ /// where each element (`AccessFilters`) represents a conjunction,
300
+ /// we can simply append to the array.
301
+ pub fn append_or ( & mut self , other : & FilteredAccess < T > ) {
302
+ self . filter_sets . append ( & mut other. filter_sets . clone ( ) ) ;
293
303
}
294
304
295
305
pub fn extend_access ( & mut self , other : & FilteredAccess < T > ) {
@@ -298,9 +308,23 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
298
308
299
309
/// Returns `true` if this and `other` can be active at the same time.
300
310
pub fn is_compatible ( & self , other : & FilteredAccess < T > ) -> bool {
301
- self . access . is_compatible ( & other. access )
302
- || !self . with . is_disjoint ( & other. without )
303
- || !other. with . is_disjoint ( & self . without )
311
+ if self . access . is_compatible ( & other. access ) {
312
+ return true ;
313
+ }
314
+
315
+ // If the access instances are incompatible, we want to check that whether filters can
316
+ // guarantee that queries are disjoint.
317
+ // Since the `filter_sets` array represents a Disjunctive Normal Form formula ("ORs of ANDs"),
318
+ // we need to make sure that each filter set (ANDs) rule out every filter set from the `other` instance.
319
+ //
320
+ // For example, `Query<&mut C, Or<(With<A>, Without<B>)>>` is compatible `Query<&mut C, (With<B>, Without<A>)>`,
321
+ // but `Query<&mut C, Or<(Without<A>, Without<B>)>>` isn't compatible with `Query<&mut C, Or<(With<A>, With<B>)>>`.
322
+ self . filter_sets . iter ( ) . all ( |filter| {
323
+ other
324
+ . filter_sets
325
+ . iter ( )
326
+ . all ( |other_filter| filter. is_ruled_out_by ( other_filter) )
327
+ } )
304
328
}
305
329
306
330
/// Returns a vector of elements that this and `other` cannot access at the same time.
@@ -313,10 +337,34 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
313
337
}
314
338
315
339
/// Adds all access and filters from `other`.
316
- pub fn extend ( & mut self , access : & FilteredAccess < T > ) {
317
- self . access . extend ( & access. access ) ;
318
- self . with . union_with ( & access. with ) ;
319
- self . without . union_with ( & access. without ) ;
340
+ ///
341
+ /// Corresponds to a conjunction operation (AND) for filters.
342
+ ///
343
+ /// Extending `Or<(With<A>, Without<B>)>` with `Or<(With<C>, Without<D>)>` will result in
344
+ /// `Or<((With<A>, With<C>), (With<A>, Without<D>), (Without<B>, With<C>), (Without<B>, Without<D>))>`.
345
+ pub fn extend ( & mut self , other : & FilteredAccess < T > ) {
346
+ self . access . extend ( & other. access ) ;
347
+
348
+ // We can avoid allocating a new array of bitsets if `other` contains just a single set of filters:
349
+ // in this case we can short-circuit by performing an in-place union for each bitset.
350
+ if other. filter_sets . len ( ) == 1 {
351
+ for filter in & mut self . filter_sets {
352
+ filter. with . union_with ( & other. filter_sets [ 0 ] . with ) ;
353
+ filter. without . union_with ( & other. filter_sets [ 0 ] . without ) ;
354
+ }
355
+ return ;
356
+ }
357
+
358
+ let mut new_filters = Vec :: with_capacity ( self . filter_sets . len ( ) * other. filter_sets . len ( ) ) ;
359
+ for filter in & self . filter_sets {
360
+ for other_filter in & other. filter_sets {
361
+ let mut new_filter = filter. clone ( ) ;
362
+ new_filter. with . union_with ( & other_filter. with ) ;
363
+ new_filter. without . union_with ( & other_filter. without ) ;
364
+ new_filters. push ( new_filter) ;
365
+ }
366
+ }
367
+ self . filter_sets = new_filters;
320
368
}
321
369
322
370
/// Sets the underlying unfiltered access as having access to all indexed elements.
@@ -325,6 +373,43 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
325
373
}
326
374
}
327
375
376
+ #[ derive( Clone , Eq , PartialEq ) ]
377
+ struct AccessFilters < T > {
378
+ with : FixedBitSet ,
379
+ without : FixedBitSet ,
380
+ _index_type : PhantomData < T > ,
381
+ }
382
+
383
+ impl < T : SparseSetIndex + fmt:: Debug > fmt:: Debug for AccessFilters < T > {
384
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
385
+ f. debug_struct ( "AccessFilters" )
386
+ . field ( "with" , & FormattedBitSet :: < T > :: new ( & self . with ) )
387
+ . field ( "without" , & FormattedBitSet :: < T > :: new ( & self . without ) )
388
+ . finish ( )
389
+ }
390
+ }
391
+
392
+ impl < T : SparseSetIndex > Default for AccessFilters < T > {
393
+ fn default ( ) -> Self {
394
+ Self {
395
+ with : FixedBitSet :: default ( ) ,
396
+ without : FixedBitSet :: default ( ) ,
397
+ _index_type : PhantomData ,
398
+ }
399
+ }
400
+ }
401
+
402
+ impl < T : SparseSetIndex > AccessFilters < T > {
403
+ fn is_ruled_out_by ( & self , other : & Self ) -> bool {
404
+ // Although not technically complete, we don't consider the case when `AccessFilters`'s
405
+ // `without` bitset contradicts its own `with` bitset (e.g. `(With<A>, Without<A>)`).
406
+ // Such query would be considered compatible with any other query, but as it's almost
407
+ // always an error, we ignore this case instead of treating such query as compatible
408
+ // with others.
409
+ !self . with . is_disjoint ( & other. without ) || !self . without . is_disjoint ( & other. with )
410
+ }
411
+ }
412
+
328
413
/// A collection of [`FilteredAccess`] instances.
329
414
///
330
415
/// Used internally to statically check if systems have conflicting access.
@@ -441,7 +526,10 @@ impl<T: SparseSetIndex> Default for FilteredAccessSet<T> {
441
526
442
527
#[ cfg( test) ]
443
528
mod tests {
529
+ use crate :: query:: access:: AccessFilters ;
444
530
use crate :: query:: { Access , FilteredAccess , FilteredAccessSet } ;
531
+ use fixedbitset:: FixedBitSet ;
532
+ use std:: marker:: PhantomData ;
445
533
446
534
#[ test]
447
535
fn read_all_access_conflicts ( ) {
@@ -514,22 +602,67 @@ mod tests {
514
602
let mut access_a = FilteredAccess :: < usize > :: default ( ) ;
515
603
access_a. add_read ( 0 ) ;
516
604
access_a. add_read ( 1 ) ;
517
- access_a. add_with ( 2 ) ;
605
+ access_a. and_with ( 2 ) ;
518
606
519
607
let mut access_b = FilteredAccess :: < usize > :: default ( ) ;
520
608
access_b. add_read ( 0 ) ;
521
609
access_b. add_write ( 3 ) ;
522
- access_b. add_without ( 4 ) ;
610
+ access_b. and_without ( 4 ) ;
523
611
524
612
access_a. extend ( & access_b) ;
525
613
526
614
let mut expected = FilteredAccess :: < usize > :: default ( ) ;
527
615
expected. add_read ( 0 ) ;
528
616
expected. add_read ( 1 ) ;
529
- expected. add_with ( 2 ) ;
617
+ expected. and_with ( 2 ) ;
530
618
expected. add_write ( 3 ) ;
531
- expected. add_without ( 4 ) ;
619
+ expected. and_without ( 4 ) ;
532
620
533
621
assert ! ( access_a. eq( & expected) ) ;
534
622
}
623
+
624
+ #[ test]
625
+ fn filtered_access_extend_or ( ) {
626
+ let mut access_a = FilteredAccess :: < usize > :: default ( ) ;
627
+ // Exclusive access to `(&mut A, &mut B)`.
628
+ access_a. add_write ( 0 ) ;
629
+ access_a. add_write ( 1 ) ;
630
+
631
+ // Filter by `With<C>`.
632
+ let mut access_b = FilteredAccess :: < usize > :: default ( ) ;
633
+ access_b. and_with ( 2 ) ;
634
+
635
+ // Filter by `(With<D>, Without<E>)`.
636
+ let mut access_c = FilteredAccess :: < usize > :: default ( ) ;
637
+ access_c. and_with ( 3 ) ;
638
+ access_c. and_without ( 4 ) ;
639
+
640
+ // Turns `access_b` into `Or<(With<C>, (With<D>, Without<D>))>`.
641
+ access_b. append_or ( & access_c) ;
642
+ // Applies the filters to the initial query, which corresponds to the FilteredAccess'
643
+ // representation of `Query<(&mut A, &mut B), Or<(With<C>, (With<D>, Without<E>))>>`.
644
+ access_a. extend ( & access_b) ;
645
+
646
+ // Construct the expected `FilteredAccess` struct.
647
+ // The intention here is to test that exclusive access implied by `add_write`
648
+ // forms correct normalized access structs when extended with `Or` filters.
649
+ let mut expected = FilteredAccess :: < usize > :: default ( ) ;
650
+ expected. add_write ( 0 ) ;
651
+ expected. add_write ( 1 ) ;
652
+ // The resulted access is expected to represent `Or<((With<A>, With<B>, With<C>), (With<A>, With<B>, With<D>, Without<E>))>`.
653
+ expected. filter_sets = vec ! [
654
+ AccessFilters {
655
+ with: FixedBitSet :: with_capacity_and_blocks( 3 , [ 0b111 ] ) ,
656
+ without: FixedBitSet :: default ( ) ,
657
+ _index_type: PhantomData ,
658
+ } ,
659
+ AccessFilters {
660
+ with: FixedBitSet :: with_capacity_and_blocks( 4 , [ 0b1011 ] ) ,
661
+ without: FixedBitSet :: with_capacity_and_blocks( 5 , [ 0b10000 ] ) ,
662
+ _index_type: PhantomData ,
663
+ } ,
664
+ ] ;
665
+
666
+ assert_eq ! ( access_a, expected) ;
667
+ }
535
668
}
0 commit comments