Skip to main content

bevy_ecs/query/
access_iter.rs

1use core::fmt::Display;
2
3use crate::{
4    component::{ComponentId, Components},
5    query::{Access, QueryData},
6};
7use bevy_utils::BloomFilter;
8
9// found by benchmarking
10// too low, and smaller queries do unnecessary work
11// maintaining the bloom filter for a handful of checks
12// too high, and the benefit of a simpler loop
13// is outweighed by the n^2 check
14const USE_FILTER_THRESHOLD: usize = 4;
15
16/// Check `Q` for internal conflicts, panicking if there are any.
17///
18/// Returns an error if not all components are registered.
19#[inline(never)]
20pub fn has_conflicts<Q: QueryData>(components: &Components) -> Result<(), QueryAccessError> {
21    let Some(state) = Q::get_state(components) else {
22        return Err(QueryAccessError::ComponentNotRegistered);
23    };
24
25    let result = if let Some(size) = Q::iter_access(&state).size_hint().1
26        && size <= USE_FILTER_THRESHOLD
27    {
28        has_conflicts_small::<Q>(&state)
29    } else {
30        has_conflicts_large::<Q>(&state)
31    };
32    if let Err(e) = result {
33        panic!("{e}");
34    }
35
36    Ok(())
37}
38
39/// Check if `Q` has any internal conflicts by checking all pairs of accesses.
40///
41/// This is intended for queries with fewer components than [`USE_FILTER_THRESHOLD`].
42/// Split from [`has_conflicts`] for easier testing.
43fn has_conflicts_small<'a, Q: QueryData>(
44    state: &'a Q::State,
45) -> Result<(), AccessConflictError<'a>> {
46    // we can optimize small sizes by caching the iteration result in an array on the stack
47    let mut inner_access = [EcsAccessType::Empty; USE_FILTER_THRESHOLD];
48    for (i, access) in Q::iter_access(state).enumerate() {
49        for access_other in inner_access.iter().take(i) {
50            if access.is_compatible(*access_other).is_err() {
51                return Err(AccessConflictError(access, *access_other));
52            }
53        }
54        inner_access[i] = access;
55    }
56
57    Ok(())
58}
59
60/// Check if `Q` has any internal conflicts using a bloom filter for efficiency.
61///
62/// This is intended for queries with more components than [`USE_FILTER_THRESHOLD`].
63/// Split from [`has_conflicts`] for easier testing.
64fn has_conflicts_large<'a, Q: QueryData>(
65    state: &'a Q::State,
66) -> Result<(), AccessConflictError<'a>> {
67    // use a bloom filter as a linear time check if we need to run the longer, exact check
68    let mut filter = BloomFilter::<8, 2>::new();
69    for (i, access) in Q::iter_access(state).enumerate() {
70        let needs_check = match access {
71            EcsAccessType::Component(EcsAccessLevel::Read(component_id))
72            | EcsAccessType::Component(EcsAccessLevel::Write(component_id)) => {
73                filter.check_insert(&component_id.index())
74            }
75            EcsAccessType::Component(EcsAccessLevel::ReadAll)
76            | EcsAccessType::Component(EcsAccessLevel::WriteAll) => true,
77            EcsAccessType::Access(access) => {
78                if let Ok(component_iter) = access.try_iter_access() {
79                    let mut needs_check = false;
80                    for kind in component_iter {
81                        let index = match kind {
82                            crate::query::ComponentAccessKind::Shared(id)
83                            | crate::query::ComponentAccessKind::Exclusive(id)
84                            | crate::query::ComponentAccessKind::Archetypal(id) => id.index(),
85                        };
86                        if filter.check_insert(&index) {
87                            needs_check = true;
88                        }
89                    }
90                    needs_check
91                } else {
92                    true
93                }
94            }
95            EcsAccessType::Empty => continue,
96        };
97        if needs_check {
98            // we MIGHT have a conflict, fallback to slow check
99            for (j, access_other) in Q::iter_access(state).enumerate() {
100                if i == j {
101                    continue;
102                }
103                if access.is_compatible(access_other).is_err() {
104                    return Err(AccessConflictError(access, access_other));
105                }
106            }
107        }
108    }
109    Ok(())
110}
111
112/// The data storage type that is being accessed.
113#[derive(Copy, Clone, Debug, PartialEq)]
114pub enum EcsAccessType<'a> {
115    /// Accesses [`Component`](crate::prelude::Component) data
116    Component(EcsAccessLevel),
117    /// borrowed access from [`WorldQuery::State`](crate::query::WorldQuery)
118    Access(&'a Access),
119    /// Does not access any data that can conflict.
120    Empty,
121}
122
123impl<'a> EcsAccessType<'a> {
124    /// Returns `Ok(())` if `self` and `other` are compatible. Returns a [`AccessConflictError`] otherwise.
125    #[inline(never)]
126    pub fn is_compatible(&self, other: Self) -> Result<(), AccessConflictError<'_>> {
127        use EcsAccessLevel::*;
128        use EcsAccessType::*;
129
130        match (*self, other) {
131            (Component(ReadAll), Component(Write(_)))
132            | (Component(Write(_)), Component(ReadAll))
133            | (Component(_), Component(WriteAll))
134            | (Component(WriteAll), Component(_)) => Err(AccessConflictError(*self, other)),
135
136            (Empty, _)
137            | (_, Empty)
138            // read only access doesn't conflict
139            | (Component(Read(_)), Component(Read(_)))
140            | (Component(ReadAll), Component(Read(_)))
141            | (Component(Read(_)), Component(ReadAll))
142            | (Component(ReadAll), Component(ReadAll))
143             => {
144                Ok(())
145            }
146
147            (Component(Read(id)), Component(Write(id_other)))
148            | (Component(Write(id)), Component(Read(id_other)))
149            | (Component(Write(id)), Component(Write(id_other)))
150 => if id == id_other {
151                Err(AccessConflictError(*self, other))
152            } else {
153                Ok(())
154            },
155
156            // Borrowed Access
157            (Component(Read(component_id)), Access(access))
158            | (Access(access), Component(Read(component_id))) => if access.has_write(component_id) {
159                Err(AccessConflictError(*self, other))
160            } else {
161                Ok(())
162            },
163
164            (Component(Write(component_id)), Access(access))
165            | (Access(access), Component(Write(component_id))) => if access.has_read(component_id) {
166                Err(AccessConflictError(*self, other))
167            } else {
168                Ok(())
169            },
170
171            (Component(ReadAll), Access(access))
172            | (Access(access), Component(ReadAll)) => if access.has_any_write() {
173                Err(AccessConflictError(*self, other))
174            } else {
175                Ok(())
176            },
177
178            (Component(WriteAll), Access(access))
179            | (Access(access), Component(WriteAll))=> if access.has_any_read() {
180                Err(AccessConflictError(*self, other))
181            } else {
182                Ok(())
183            },
184
185            (Access(access), Access(other_access)) => if access.is_compatible(other_access) {
186                Ok(())
187            } else {
188                Err(AccessConflictError(*self, other))
189            },
190        }
191    }
192}
193
194/// The way the data will be accessed and whether we take access on all the components on
195/// an entity or just one component.
196#[derive(Clone, Copy, Debug, PartialEq, Hash)]
197pub enum EcsAccessLevel {
198    /// Reads [`Component`](crate::prelude::Component) with [`ComponentId`]
199    Read(ComponentId),
200    /// Writes [`Component`](crate::prelude::Component) with [`ComponentId`]
201    Write(ComponentId),
202    /// Potentially reads all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
203    ReadAll,
204    /// Potentially writes all [`Component`](crate::prelude::Component)'s in the [`World`](crate::prelude::World)
205    WriteAll,
206}
207
208/// Error returned from [`EcsAccessType::is_compatible`]
209pub struct AccessConflictError<'a>(EcsAccessType<'a>, EcsAccessType<'a>);
210
211impl Display for AccessConflictError<'_> {
212    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
213        use EcsAccessLevel::*;
214        use EcsAccessType::*;
215
216        let AccessConflictError(a, b) = self;
217        match (a, b) {
218            // ReadAll/WriteAll + Component conflicts
219            (Component(ReadAll), Component(Write(id)))
220            | (Component(Write(id)), Component(ReadAll)) => {
221                write!(
222                    f,
223                    "Component read all access conflicts with component {id:?} write."
224                )
225            }
226            (Component(WriteAll), Component(Write(id)))
227            | (Component(Write(id)), Component(WriteAll)) => {
228                write!(
229                    f,
230                    "Component write all access conflicts with component {id:?} write."
231                )
232            }
233            (Component(WriteAll), Component(Read(id)))
234            | (Component(Read(id)), Component(WriteAll)) => {
235                write!(
236                    f,
237                    "Component write all access conflicts with component {id:?} read."
238                )
239            }
240            (Component(WriteAll), Component(ReadAll))
241            | (Component(ReadAll), Component(WriteAll)) => {
242                write!(f, "Component write all conflicts with component read all.")
243            }
244            (Component(WriteAll), Component(WriteAll)) => {
245                write!(f, "Component write all conflicts with component write all.")
246            }
247
248            // Component + Component conflicts
249            (Component(Read(id)), Component(Write(id_other)))
250            | (Component(Write(id_other)), Component(Read(id))) => write!(
251                f,
252                "Component {id:?} read conflicts with component {id_other:?} write."
253            ),
254            (Component(Write(id)), Component(Write(id_other))) => write!(
255                f,
256                "Component {id:?} write conflicts with component {id_other:?} write."
257            ),
258
259            // Borrowed Access conflicts
260            (Access(_), Component(Read(id))) | (Component(Read(id)), Access(_)) => write!(
261                f,
262                "Access has a write that conflicts with component {id:?} read."
263            ),
264            (Access(_), Component(Write(id))) | (Component(Write(id)), Access(_)) => write!(
265                f,
266                "Access has a read that conflicts with component {id:?} write."
267            ),
268            (Access(_), Component(ReadAll)) | (Component(ReadAll), Access(_)) => write!(
269                f,
270                "Access has a write that conflicts with component read all"
271            ),
272            (Access(_), Component(WriteAll)) | (Component(WriteAll), Access(_)) => write!(
273                f,
274                "Access has a read that conflicts with component write all"
275            ),
276            (Access(_), Access(_)) => write!(f, "Access conflicts with other Access"),
277
278            _ => {
279                unreachable!("Other accesses should be compatible");
280            }
281        }
282    }
283}
284
285/// Error indicating the entity does not have all requested component ids.
286#[derive(Clone, Copy, Debug, PartialEq)]
287pub enum QueryAccessError {
288    /// Component was not registered on world
289    ComponentNotRegistered,
290    /// Entity did not have the requested components
291    EntityDoesNotMatch,
292}
293
294impl core::error::Error for QueryAccessError {}
295
296impl Display for QueryAccessError {
297    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
298        match *self {
299            QueryAccessError::ComponentNotRegistered => {
300                write!(
301                    f,
302                    "At least one component in Q was not registered in world.
303                    Consider calling `World::register_component`"
304                )
305            }
306            QueryAccessError::EntityDoesNotMatch => {
307                write!(f, "Entity does not match Q")
308            }
309        }
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::*;
316    use crate::{
317        prelude::Component,
318        query::WorldQuery,
319        world::{EntityMut, EntityMutExcept, EntityRef, EntityRefExcept, World},
320    };
321
322    #[derive(Component)]
323    struct C1;
324
325    #[derive(Component)]
326    struct C2;
327
328    fn setup_world() -> World {
329        let world = World::new();
330        let mut world = world;
331        world.register_component::<C1>();
332        world.register_component::<C2>();
333        world
334    }
335
336    fn assert_compatible<Q: WorldQuery + QueryData>() {
337        let world = setup_world();
338        let c = world.components();
339
340        let state = <Q>::get_state(c).unwrap();
341        assert!(has_conflicts_small::<Q>(&state).is_ok());
342        assert!(has_conflicts_large::<Q>(&state).is_ok());
343        assert!(has_conflicts::<Q>(c).is_ok());
344    }
345
346    fn assert_conflicted<Q: WorldQuery + QueryData>() {
347        let world = setup_world();
348        let c = world.components();
349
350        let state = <Q>::get_state(c).unwrap();
351        assert!(has_conflicts_small::<Q>(&state).is_err());
352        assert!(has_conflicts_large::<Q>(&state).is_err());
353        let _ = has_conflicts::<Q>(c);
354    }
355
356    #[test]
357    fn simple_compatible() {
358        assert_compatible::<&mut C1>();
359        assert_compatible::<&C1>();
360        assert_compatible::<(&C1, &C1)>();
361    }
362
363    #[test]
364    #[should_panic(expected = "conflicts")]
365    fn conflict_component_read_conflicts_write() {
366        assert_conflicted::<(&C1, &mut C1)>();
367    }
368
369    #[test]
370    #[should_panic(expected = "conflicts")]
371    fn conflict_component_write_conflicts_read() {
372        assert_conflicted::<(&mut C1, &C1)>();
373    }
374
375    #[test]
376    #[should_panic(expected = "conflicts")]
377    fn conflict_component_write_conflicts_write() {
378        assert_conflicted::<(&mut C1, &mut C1)>();
379    }
380
381    #[test]
382    fn entity_ref_compatible() {
383        assert_compatible::<(EntityRef, &C1)>();
384        assert_compatible::<(&C1, EntityRef)>();
385        assert_compatible::<(EntityRef, EntityRef)>();
386    }
387
388    #[test]
389    #[should_panic(expected = "conflicts")]
390    fn entity_ref_conflicts_component_write() {
391        assert_conflicted::<(EntityRef, &mut C1)>();
392    }
393
394    #[test]
395    #[should_panic(expected = "conflicts")]
396    fn component_write_conflicts_entity_ref() {
397        assert_conflicted::<(&mut C1, EntityRef)>();
398    }
399
400    #[test]
401    #[should_panic(expected = "conflicts")]
402    fn entity_mut_conflicts_component_read() {
403        assert_conflicted::<(EntityMut, &C1)>();
404    }
405
406    #[test]
407    #[should_panic(expected = "conflicts")]
408    fn component_read_conflicts_entity_mut() {
409        assert_conflicted::<(&C1, EntityMut)>();
410    }
411
412    #[test]
413    #[should_panic(expected = "conflicts")]
414    fn entity_mut_conflicts_component_write() {
415        assert_conflicted::<(EntityMut, &mut C1)>();
416    }
417
418    #[test]
419    #[should_panic(expected = "conflicts")]
420    fn component_write_conflicts_entity_mut() {
421        assert_conflicted::<(&mut C1, EntityMut)>();
422    }
423
424    #[test]
425    #[should_panic(expected = "conflicts")]
426    fn entity_mut_conflicts_entity_ref() {
427        assert_conflicted::<(EntityMut, EntityRef)>();
428    }
429
430    #[test]
431    #[should_panic(expected = "conflicts")]
432    fn entity_ref_conflicts_entity_mut() {
433        assert_conflicted::<(EntityRef, EntityMut)>();
434    }
435
436    #[test]
437    fn entity_ref_except_compatible() {
438        assert_compatible::<(EntityRefExcept<C1>, &mut C1)>();
439        assert_compatible::<(&mut C1, EntityRefExcept<C1>)>();
440        assert_compatible::<(&C2, EntityRefExcept<C1>)>();
441        assert_compatible::<(&mut C1, EntityRefExcept<(C1, C2)>)>();
442        assert_compatible::<(EntityRefExcept<(C1, C2)>, &mut C1)>();
443        assert_compatible::<(&mut C1, &mut C2, EntityRefExcept<(C1, C2)>)>();
444        assert_compatible::<(&mut C1, EntityRefExcept<(C1, C2)>, &mut C2)>();
445        assert_compatible::<(EntityRefExcept<(C1, C2)>, &mut C1, &mut C2)>();
446    }
447
448    #[test]
449    #[should_panic(expected = "conflicts")]
450    fn entity_ref_except_conflicts_component_write() {
451        assert_conflicted::<(EntityRefExcept<C1>, &mut C2)>();
452    }
453
454    #[test]
455    #[should_panic(expected = "conflicts")]
456    fn component_write_conflicts_entity_ref_except() {
457        assert_conflicted::<(&mut C2, EntityRefExcept<C1>)>();
458    }
459
460    #[test]
461    fn entity_mut_except_compatible() {
462        assert_compatible::<(EntityMutExcept<C1>, &mut C1)>();
463        assert_compatible::<(&mut C1, EntityMutExcept<C1>)>();
464        assert_compatible::<(&mut C1, EntityMutExcept<(C1, C2)>)>();
465        assert_compatible::<(EntityMutExcept<(C1, C2)>, &mut C1)>();
466        assert_compatible::<(&mut C1, &mut C2, EntityMutExcept<(C1, C2)>)>();
467        assert_compatible::<(&mut C1, EntityMutExcept<(C1, C2)>, &mut C2)>();
468        assert_compatible::<(EntityMutExcept<(C1, C2)>, &mut C1, &mut C2)>();
469    }
470
471    #[test]
472    #[should_panic(expected = "conflicts")]
473    fn entity_mut_except_conflicts_component_read() {
474        assert_conflicted::<(EntityMutExcept<C1>, &C2)>();
475    }
476
477    #[test]
478    #[should_panic(expected = "conflicts")]
479    fn component_read_conflicts_entity_mut_except() {
480        assert_conflicted::<(&C2, EntityMutExcept<C1>)>();
481    }
482
483    #[test]
484    #[should_panic(expected = "conflicts")]
485    fn entity_mut_except_conflicts_component_write() {
486        assert_conflicted::<(EntityMutExcept<C1>, &mut C2)>();
487    }
488
489    #[test]
490    #[should_panic(expected = "conflicts")]
491    fn component_write_conflicts_entity_mut_except() {
492        assert_conflicted::<(&mut C2, EntityMutExcept<C1>)>();
493    }
494}