@@ -3,15 +3,19 @@ use bevy_utils::HashSet;
3
3
use fixedbitset:: FixedBitSet ;
4
4
use std:: marker:: PhantomData ;
5
5
6
- /// `Access` keeps track of read and write accesses to values within a collection.
6
+ /// Tracks read and write access to specific elements in a collection.
7
7
///
8
- /// This is used for ensuring systems are executed soundly.
9
- #[ derive( Debug , Eq , PartialEq , Clone ) ]
8
+ /// Used internally to ensure soundness during system initialization and execution.
9
+ /// See the [`is_compatible`](Access::is_compatible) and [`get_conflicts`](Access::get_conflicts) functions.
10
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
10
11
pub struct Access < T : SparseSetIndex > {
11
- reads_all : bool ,
12
- /// A combined set of T read and write accesses.
12
+ /// All accessed elements.
13
13
reads_and_writes : FixedBitSet ,
14
+ /// The exclusively-accessed elements.
14
15
writes : FixedBitSet ,
16
+ /// Is `true` if this has access to all elements in the collection?
17
+ /// This field is a performance optimization for `&World` (also harder to mess up for soundness).
18
+ reads_all : bool ,
15
19
marker : PhantomData < T > ,
16
20
}
17
21
@@ -27,26 +31,29 @@ impl<T: SparseSetIndex> Default for Access<T> {
27
31
}
28
32
29
33
impl < T : SparseSetIndex > Access < T > {
30
- pub fn grow ( & mut self , bits : usize ) {
31
- self . reads_and_writes . grow ( bits) ;
32
- self . writes . grow ( bits) ;
34
+ /// Increases the set capacity to the specified amount.
35
+ ///
36
+ /// Does nothing if `capacity` is less than or equal to the current value.
37
+ pub fn grow ( & mut self , capacity : usize ) {
38
+ self . reads_and_writes . grow ( capacity) ;
39
+ self . writes . grow ( capacity) ;
33
40
}
34
41
35
- /// Adds a read access for the given index.
42
+ /// Adds access to the element given by ` index` .
36
43
pub fn add_read ( & mut self , index : T ) {
37
44
self . reads_and_writes . grow ( index. sparse_set_index ( ) + 1 ) ;
38
45
self . reads_and_writes . insert ( index. sparse_set_index ( ) ) ;
39
46
}
40
47
41
- /// Adds a write access for the given index.
48
+ /// Adds exclusive access to the element given by ` index` .
42
49
pub fn add_write ( & mut self , index : T ) {
43
50
self . reads_and_writes . grow ( index. sparse_set_index ( ) + 1 ) ;
44
- self . writes . grow ( index. sparse_set_index ( ) + 1 ) ;
45
51
self . reads_and_writes . insert ( index. sparse_set_index ( ) ) ;
52
+ self . writes . grow ( index. sparse_set_index ( ) + 1 ) ;
46
53
self . writes . insert ( index. sparse_set_index ( ) ) ;
47
54
}
48
55
49
- /// Returns true if this `Access` contains a read access for the given index.
56
+ /// Returns ` true` if this can access the element given by ` index` .
50
57
pub fn has_read ( & self , index : T ) -> bool {
51
58
if self . reads_all {
52
59
true
@@ -55,51 +62,54 @@ impl<T: SparseSetIndex> Access<T> {
55
62
}
56
63
}
57
64
58
- /// Returns true if this `Access` contains a write access for the given index.
65
+ /// Returns ` true` if this can exclusively access the element given by ` index` .
59
66
pub fn has_write ( & self , index : T ) -> bool {
60
67
self . writes . contains ( index. sparse_set_index ( ) )
61
68
}
62
69
63
- /// Sets this `Access` to having read access for all indices .
70
+ /// Sets this as having access to all indexed elements (i.e. `&World`) .
64
71
pub fn read_all ( & mut self ) {
65
72
self . reads_all = true ;
66
73
}
67
74
68
- /// Returns true if this `Access` has read access to all indices .
69
- pub fn reads_all ( & self ) -> bool {
75
+ /// Returns ` true` if this has access to all indexed elements (i.e. `&World`) .
76
+ pub fn has_read_all ( & self ) -> bool {
70
77
self . reads_all
71
78
}
72
79
73
- /// Clears all recorded accesses.
80
+ /// Removes all accesses.
74
81
pub fn clear ( & mut self ) {
75
82
self . reads_all = false ;
76
83
self . reads_and_writes . clear ( ) ;
77
84
self . writes . clear ( ) ;
78
85
}
79
86
80
- /// Extends this `Access` with another, copying all accesses of `other` into this .
87
+ /// Adds all access from `other`.
81
88
pub fn extend ( & mut self , other : & Access < T > ) {
82
89
self . reads_all = self . reads_all || other. reads_all ;
83
90
self . reads_and_writes . union_with ( & other. reads_and_writes ) ;
84
91
self . writes . union_with ( & other. writes ) ;
85
92
}
86
93
87
- /// Returns true if this `Access` is compatible with `other` .
94
+ /// Returns ` true` if the access and `other` can be active at the same time .
88
95
///
89
- /// Two `Access` instances are incompatible with each other if one `Access` has a write for
90
- /// which the other also has a write or a read .
96
+ /// `Access` instances are incompatible if one can write
97
+ /// an element that the other can read or write .
91
98
pub fn is_compatible ( & self , other : & Access < T > ) -> bool {
99
+ // Only systems that do not write data are compatible with systems that operate on `&World`.
92
100
if self . reads_all {
93
- 0 == other. writes . count_ones ( ..)
94
- } else if other. reads_all {
95
- 0 == self . writes . count_ones ( ..)
96
- } else {
97
- self . writes . is_disjoint ( & other. reads_and_writes )
98
- && self . reads_and_writes . is_disjoint ( & other. writes )
101
+ return other. writes . count_ones ( ..) == 0 ;
102
+ }
103
+
104
+ if other. reads_all {
105
+ return self . writes . count_ones ( ..) == 0 ;
99
106
}
107
+
108
+ self . writes . is_disjoint ( & other. reads_and_writes )
109
+ && self . reads_and_writes . is_disjoint ( & other. writes )
100
110
}
101
111
102
- /// Calculates conflicting accesses between this `Access` and `other`.
112
+ /// Returns a vector of elements that the access and `other` cannot access at the same time .
103
113
pub fn get_conflicts ( & self , other : & Access < T > ) -> Vec < T > {
104
114
let mut conflicts = FixedBitSet :: default ( ) ;
105
115
if self . reads_all {
@@ -117,20 +127,28 @@ impl<T: SparseSetIndex> Access<T> {
117
127
. collect ( )
118
128
}
119
129
120
- /// Returns all read accesses.
130
+ /// Returns the indices of the elements this has access to.
131
+ pub fn reads_and_writes ( & self ) -> impl Iterator < Item = T > + ' _ {
132
+ self . reads_and_writes . ones ( ) . map ( T :: get_sparse_set_index)
133
+ }
134
+
135
+ /// Returns the indices of the elements this has non-exclusive access to.
121
136
pub fn reads ( & self ) -> impl Iterator < Item = T > + ' _ {
122
137
self . reads_and_writes
123
138
. difference ( & self . writes )
124
139
. map ( T :: get_sparse_set_index)
125
140
}
126
141
127
- /// Returns all write accesses .
142
+ /// Returns the indices of the elements this has exclusive access to .
128
143
pub fn writes ( & self ) -> impl Iterator < Item = T > + ' _ {
129
144
self . writes . ones ( ) . map ( T :: get_sparse_set_index)
130
145
}
131
146
}
132
147
133
- #[ derive( Clone , Eq , PartialEq , Debug ) ]
148
+ /// An [`Access`] that has been filtered to include and exclude certain combinations of elements.
149
+ ///
150
+ /// Used internally to statically check if queries are disjoint.
151
+ #[ derive( Debug , Clone , Eq , PartialEq ) ]
134
152
pub struct FilteredAccess < T : SparseSetIndex > {
135
153
access : Access < T > ,
136
154
with : FixedBitSet ,
@@ -156,31 +174,43 @@ impl<T: SparseSetIndex> From<FilteredAccess<T>> for FilteredAccessSet<T> {
156
174
}
157
175
158
176
impl < T : SparseSetIndex > FilteredAccess < T > {
177
+ /// Returns a reference to the underlying unfiltered access.
159
178
#[ inline]
160
179
pub fn access ( & self ) -> & Access < T > {
161
180
& self . access
162
181
}
163
182
183
+ /// Returns a mutable reference to the underlying unfiltered access.
184
+ #[ inline]
185
+ pub fn access_mut ( & mut self ) -> & mut Access < T > {
186
+ & mut self . access
187
+ }
188
+
189
+ /// Adds access to the element given by `index`.
164
190
pub fn add_read ( & mut self , index : T ) {
165
191
self . access . add_read ( index. clone ( ) ) ;
166
192
self . add_with ( index) ;
167
193
}
168
194
195
+ /// Adds exclusive access to the element given by `index`.
169
196
pub fn add_write ( & mut self , index : T ) {
170
197
self . access . add_write ( index. clone ( ) ) ;
171
198
self . add_with ( index) ;
172
199
}
173
200
201
+ /// Retains only combinations where the element given by `index` is also present.
174
202
pub fn add_with ( & mut self , index : T ) {
175
203
self . with . grow ( index. sparse_set_index ( ) + 1 ) ;
176
204
self . with . insert ( index. sparse_set_index ( ) ) ;
177
205
}
178
206
207
+ /// Retains only combinations where the element given by `index` is not present.
179
208
pub fn add_without ( & mut self , index : T ) {
180
209
self . without . grow ( index. sparse_set_index ( ) + 1 ) ;
181
210
self . without . insert ( index. sparse_set_index ( ) ) ;
182
211
}
183
212
213
+ /// Returns `true` if this and `other` can be active at the same time.
184
214
pub fn is_compatible ( & self , other : & FilteredAccess < T > ) -> bool {
185
215
if self . access . is_compatible ( & other. access ) {
186
216
true
@@ -190,56 +220,94 @@ impl<T: SparseSetIndex> FilteredAccess<T> {
190
220
}
191
221
}
192
222
223
+ /// Returns a vector of elements that this and `other` cannot access at the same time.
224
+ pub fn get_conflicts ( & self , other : & FilteredAccess < T > ) -> Vec < T > {
225
+ if !self . is_compatible ( other) {
226
+ // filters are disjoint, so we can just look at the unfiltered intersection
227
+ return self . access . get_conflicts ( & other. access ) ;
228
+ }
229
+ Vec :: new ( )
230
+ }
231
+
232
+ /// Adds all access and filters from `other`.
193
233
pub fn extend ( & mut self , access : & FilteredAccess < T > ) {
194
234
self . access . extend ( & access. access ) ;
195
235
self . with . union_with ( & access. with ) ;
196
236
self . without . union_with ( & access. without ) ;
197
237
}
198
238
239
+ /// Sets the underlying unfiltered access as having access to all indexed elements.
199
240
pub fn read_all ( & mut self ) {
200
241
self . access . read_all ( ) ;
201
242
}
202
243
}
203
- #[ derive( Clone , Debug ) ]
244
+
245
+ /// A collection of [`FilteredAccess`] instances.
246
+ ///
247
+ /// Used internally to statically check if systems have conflicting access.
248
+ #[ derive( Debug , Clone ) ]
204
249
pub struct FilteredAccessSet < T : SparseSetIndex > {
205
250
combined_access : Access < T > ,
206
251
filtered_accesses : Vec < FilteredAccess < T > > ,
207
252
}
208
253
209
254
impl < T : SparseSetIndex > FilteredAccessSet < T > {
255
+ /// Returns a reference to the unfiltered access of the entire set.
210
256
#[ inline]
211
257
pub fn combined_access ( & self ) -> & Access < T > {
212
258
& self . combined_access
213
259
}
214
260
261
+ /// Returns a mutable reference to the unfiltered access of the entire set.
215
262
#[ inline]
216
263
pub fn combined_access_mut ( & mut self ) -> & mut Access < T > {
217
264
& mut self . combined_access
218
265
}
219
266
220
- pub fn get_conflicts ( & self , filtered_access : & FilteredAccess < T > ) -> Vec < T > {
221
- // if combined unfiltered access is incompatible, check each filtered access for
222
- // compatibility
223
- let mut conflicts = HashSet :: < usize > :: default ( ) ;
224
- if !filtered_access. access . is_compatible ( & self . combined_access ) {
225
- for current_filtered_access in & self . filtered_accesses {
226
- if !current_filtered_access. is_compatible ( filtered_access) {
227
- conflicts. extend (
228
- current_filtered_access
229
- . access
230
- . get_conflicts ( & filtered_access. access )
231
- . iter ( )
232
- . map ( |ind| ind. sparse_set_index ( ) ) ,
233
- ) ;
267
+ /// Returns `true` if this and `other` can be active at the same time.
268
+ pub fn is_compatible ( & self , other : & FilteredAccessSet < T > ) -> bool {
269
+ if self . combined_access . is_compatible ( other. combined_access ( ) ) {
270
+ return true ;
271
+ } else {
272
+ for filtered in self . filtered_accesses . iter ( ) {
273
+ for other_filtered in other. filtered_accesses . iter ( ) {
274
+ if !filtered. is_compatible ( other_filtered) {
275
+ return false ;
276
+ }
234
277
}
235
278
}
236
279
}
237
- conflicts
238
- . iter ( )
239
- . map ( |ind| T :: get_sparse_set_index ( * ind) )
240
- . collect ( )
280
+
281
+ true
282
+ }
283
+
284
+ /// Returns a vector of elements that this set and `other` cannot access at the same time.
285
+ pub fn get_conflicts ( & self , other : & FilteredAccessSet < T > ) -> Vec < T > {
286
+ // if the unfiltered access is incompatible, must check each pair
287
+ let mut conflicts = HashSet :: new ( ) ;
288
+ if !self . combined_access . is_compatible ( other. combined_access ( ) ) {
289
+ for filtered in self . filtered_accesses . iter ( ) {
290
+ for other_filtered in other. filtered_accesses . iter ( ) {
291
+ conflicts. extend ( filtered. get_conflicts ( other_filtered) . into_iter ( ) ) ;
292
+ }
293
+ }
294
+ }
295
+ conflicts. into_iter ( ) . collect ( )
296
+ }
297
+
298
+ /// Returns a vector of elements that this set and `other` cannot access at the same time.
299
+ pub fn get_conflicts_single ( & self , filtered_access : & FilteredAccess < T > ) -> Vec < T > {
300
+ // if the unfiltered access is incompatible, must check each pair
301
+ let mut conflicts = HashSet :: new ( ) ;
302
+ if !self . combined_access . is_compatible ( filtered_access. access ( ) ) {
303
+ for filtered in self . filtered_accesses . iter ( ) {
304
+ conflicts. extend ( filtered. get_conflicts ( filtered_access) . into_iter ( ) ) ;
305
+ }
306
+ }
307
+ conflicts. into_iter ( ) . collect ( )
241
308
}
242
309
310
+ /// Adds the filtered access to the set.
243
311
pub fn add ( & mut self , filtered_access : FilteredAccess < T > ) {
244
312
self . combined_access . extend ( & filtered_access. access ) ;
245
313
self . filtered_accesses . push ( filtered_access) ;
0 commit comments