bevy_trait_query/one/impls/
one.rs

1use bevy_ecs::change_detection::{Mut, Ref};
2use bevy_ecs::entity::Entity;
3use bevy_ecs::prelude::World;
4use bevy_ecs::ptr::UnsafeCellDeref;
5use bevy_ecs::{
6    component::{ComponentId, Components, Tick},
7    query::{QueryData, QueryItem, ReadOnlyQueryData, WorldQuery},
8    storage::TableRow,
9    world::unsafe_world_cell::UnsafeWorldCell,
10};
11
12use crate::TraitImplMeta;
13use crate::{
14    debug_unreachable, one::FetchStorage, zip_exact, OneTraitFetch, TraitQuery, TraitQueryState,
15};
16
17/// [`WorldQuery`] adapter that fetches entities with exactly one component implementing a trait.
18///
19/// Depending on whether you requested shared or exclusive access to the trait objects, iterating
20/// over these queries yields types with different capacities
21///
22/// - `Query<One<&dyn Trait>>` yields a [`Ref`] object
23/// - `Query<One<&mut dyn Trait>>` yields a [`Mut`] object
24pub struct One<T>(pub T);
25
26unsafe impl<Trait: ?Sized + TraitQuery> QueryData for One<&Trait> {
27    type ReadOnly = Self;
28
29    const IS_READ_ONLY: bool = true;
30
31    type Item<'w> = Ref<'w, Trait>;
32
33    #[inline]
34    fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> {
35        item
36    }
37
38    #[inline]
39    unsafe fn fetch<'w>(
40        fetch: &mut Self::Fetch<'w>,
41        entity: Entity,
42        table_row: TableRow,
43    ) -> Self::Item<'w> {
44        unsafe {
45            let table_row = table_row.as_usize();
46            let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
47                // SAFETY: This function must have been called after `set_archetype`,
48                // so we know that `self.storage` has been initialized.
49                FetchStorage::Uninit => debug_unreachable(),
50                FetchStorage::Table {
51                    column,
52                    added_ticks,
53                    changed_ticks,
54                    location,
55                    meta,
56                } => {
57                    let ptr = column.byte_add(table_row * meta.size_bytes);
58                    (
59                        meta.dyn_ctor,
60                        ptr,
61                        // SAFETY: We have read access to the component, so by extension
62                        // we have access to the corresponding `ComponentTicks`.
63                        added_ticks.get(table_row).deref(),
64                        changed_ticks.get(table_row).deref(),
65                        location,
66                    )
67                }
68                FetchStorage::SparseSet { components, meta } => {
69                    let (ptr, ticks, location) = components
70                        .get_with_ticks(entity)
71                        .unwrap_or_else(|| debug_unreachable());
72                    (
73                        meta.dyn_ctor,
74                        ptr,
75                        // SAFETY: We have read access to the component, so by extension
76                        // we have access to the corresponding `ComponentTicks`.
77                        ticks.added.deref(),
78                        ticks.changed.deref(),
79                        location,
80                    )
81                }
82            };
83
84            Ref::new(
85                dyn_ctor.cast(ptr),
86                added,
87                changed,
88                fetch.last_run,
89                fetch.this_run,
90                location.map(|loc| loc.deref()),
91            )
92        }
93    }
94}
95
96unsafe impl<Trait: ?Sized + TraitQuery> ReadOnlyQueryData for One<&Trait> {}
97
98// SAFETY: We only access the components registered in TraitQueryState.
99// This same set of components is used to match archetypes, and used to register world access.
100unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&Trait> {
101    type Fetch<'w> = OneTraitFetch<'w, Trait>;
102    type State = TraitQueryState<Trait>;
103
104    #[inline]
105    unsafe fn init_fetch<'w>(
106        world: UnsafeWorldCell<'w>,
107        _state: &Self::State,
108        _last_run: Tick,
109        _this_run: Tick,
110    ) -> OneTraitFetch<'w, Trait> {
111        unsafe {
112            OneTraitFetch {
113                storage: FetchStorage::Uninit,
114                last_run: Tick::new(0),
115                sparse_sets: &world.storages().sparse_sets,
116                this_run: Tick::new(0),
117            }
118        }
119    }
120
121    const IS_DENSE: bool = false;
122    // const IS_ARCHETYPAL: bool = false;
123
124    #[inline]
125    unsafe fn set_archetype<'w>(
126        fetch: &mut OneTraitFetch<'w, Trait>,
127        state: &Self::State,
128        _archetype: &'w bevy_ecs::archetype::Archetype,
129        table: &'w bevy_ecs::storage::Table,
130    ) {
131        unsafe {
132            // Search for a registered trait impl that is present in the archetype.
133            // We check the table components first since it is faster to retrieve data of this type.
134            //
135            // without loss of generality we use the zero-th row since we only care about whether the
136            // component exists in the table
137            let row = TableRow::from_usize(0);
138            for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
139                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
140                    fetch.storage = table_storage;
141                    return;
142                }
143            }
144            for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
145                if let Some(sparse_set) = fetch.sparse_sets.get(component) {
146                    fetch.storage = FetchStorage::SparseSet {
147                        components: sparse_set,
148                        meta,
149                    };
150                    return;
151                }
152            }
153            // At least one of the components must be present in the table/sparse set.
154            debug_unreachable()
155        }
156    }
157
158    #[inline]
159    unsafe fn set_table<'w>(
160        fetch: &mut OneTraitFetch<'w, Trait>,
161        state: &Self::State,
162        table: &'w bevy_ecs::storage::Table,
163    ) {
164        unsafe {
165            // Search for a registered trait impl that is present in the table.
166            //
167            // without loss of generality we use the zero-th row since we only care about whether the
168            // component exists in the table
169            let row = TableRow::from_usize(0);
170            for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
171                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
172                    fetch.storage = table_storage;
173                    return;
174                }
175            }
176            // At least one of the components must be present in the table.
177            debug_unreachable()
178        }
179    }
180
181    #[inline]
182    fn update_component_access(
183        state: &Self::State,
184        access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
185    ) {
186        let mut new_access = access.clone();
187        let mut not_first = false;
188        for &component in &*state.components {
189            assert!(
190                !access.access().has_component_write(component),
191                "&{} conflicts with a previous access in this query. Shared access cannot coincide with exclusive access.",
192                std::any::type_name::<Trait>(),
193            );
194            if not_first {
195                let mut intermediate = access.clone();
196                intermediate.add_component_read(component);
197                new_access.append_or(&intermediate);
198                new_access.extend_access(&intermediate);
199            } else {
200                new_access.and_with(component);
201                new_access.access_mut().add_component_read(component);
202                not_first = true;
203            }
204        }
205        *access = new_access;
206    }
207
208    #[inline]
209    fn init_state(world: &mut World) -> Self::State {
210        TraitQueryState::init(world)
211    }
212
213    #[inline]
214    fn get_state(_: &Components) -> Option<Self::State> {
215        // TODO: fix this https://github.com/bevyengine/bevy/issues/13798
216        panic!("transmuting and any other operations concerning the state of a query are currently broken and shouldn't be used. See https://github.com/JoJoJet/bevy-trait-query/issues/59");
217    }
218
219    #[inline]
220    fn matches_component_set(
221        state: &Self::State,
222        set_contains_id: &impl Fn(ComponentId) -> bool,
223    ) -> bool {
224        state.matches_component_set_one(set_contains_id)
225    }
226
227    #[inline]
228    fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
229        fetch
230    }
231}
232
233unsafe impl<'a, Trait: ?Sized + TraitQuery> QueryData for One<&'a mut Trait> {
234    type ReadOnly = One<&'a Trait>;
235
236    const IS_READ_ONLY: bool = false;
237
238    type Item<'w> = Mut<'w, Trait>;
239
240    #[inline]
241    fn shrink<'wlong: 'wshort, 'wshort>(item: QueryItem<'wlong, Self>) -> QueryItem<'wshort, Self> {
242        item
243    }
244
245    #[inline]
246    unsafe fn fetch<'w>(
247        fetch: &mut Self::Fetch<'w>,
248        entity: Entity,
249        table_row: TableRow,
250    ) -> Mut<'w, Trait> {
251        unsafe {
252            let table_row = table_row.as_usize();
253            let (dyn_ctor, ptr, added, changed, location) = match fetch.storage {
254                // SAFETY: This function must have been called after `set_archetype`,
255                // so we know that `self.storage` has been initialized.
256                FetchStorage::Uninit => debug_unreachable(),
257                FetchStorage::Table {
258                    column,
259                    added_ticks,
260                    changed_ticks,
261                    location,
262                    meta,
263                } => {
264                    let ptr = column.byte_add(table_row * meta.size_bytes);
265                    (
266                        meta.dyn_ctor,
267                        // SAFETY: `column` allows for shared mutable access.
268                        // So long as the caller does not invoke this function twice with the same archetype_index,
269                        // this pointer will never be aliased.
270                        ptr.assert_unique(),
271                        // SAFETY: We have exclusive access to the component, so by extension
272                        // we have exclusive access to the corresponding `ComponentTicks`.
273                        added_ticks.get(table_row).deref_mut(),
274                        changed_ticks.get(table_row).deref_mut(),
275                        location,
276                    )
277                }
278                FetchStorage::SparseSet { components, meta } => {
279                    let (ptr, ticks, location) = components
280                        .get_with_ticks(entity)
281                        .unwrap_or_else(|| debug_unreachable());
282                    (
283                        meta.dyn_ctor,
284                        // SAFETY: We have exclusive access to the sparse set `components`.
285                        // So long as the caller does not invoke this function twice with the same archetype_index,
286                        // this pointer will never be aliased.
287                        ptr.assert_unique(),
288                        // SAFETY: We have exclusive access to the component, so by extension
289                        // we have exclusive access to the corresponding `ComponentTicks`.
290                        ticks.added.deref_mut(),
291                        ticks.changed.deref_mut(),
292                        location,
293                    )
294                }
295            };
296
297            Mut::new(
298                dyn_ctor.cast_mut(ptr),
299                added,
300                changed,
301                fetch.last_run,
302                fetch.this_run,
303                location.map(|loc| loc.deref_mut()),
304            )
305        }
306    }
307}
308
309// SAFETY: We only access the components registered in TraitQueryState.
310// This same set of components is used to match archetypes, and used to register world access.
311unsafe impl<Trait: ?Sized + TraitQuery> WorldQuery for One<&mut Trait> {
312    type Fetch<'w> = OneTraitFetch<'w, Trait>;
313    type State = TraitQueryState<Trait>;
314
315    #[inline]
316    unsafe fn init_fetch<'w>(
317        world: UnsafeWorldCell<'w>,
318        _state: &Self::State,
319        last_run: Tick,
320        this_run: Tick,
321    ) -> OneTraitFetch<'w, Trait> {
322        unsafe {
323            OneTraitFetch {
324                storage: FetchStorage::Uninit,
325                sparse_sets: &world.storages().sparse_sets,
326                last_run,
327                this_run,
328            }
329        }
330    }
331
332    const IS_DENSE: bool = false;
333
334    #[inline]
335    unsafe fn set_archetype<'w>(
336        fetch: &mut OneTraitFetch<'w, Trait>,
337        state: &Self::State,
338        _archetype: &'w bevy_ecs::archetype::Archetype,
339        table: &'w bevy_ecs::storage::Table,
340    ) {
341        unsafe {
342            // Search for a registered trait impl that is present in the archetype.
343            //
344            // without loss of generality we use the zero-th row since we only care about whether the
345            // component exists in the table
346            let row = TableRow::from_usize(0);
347            for (&component_id, &meta) in zip_exact(&*state.components, &*state.meta) {
348                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
349                    fetch.storage = table_storage;
350                    return;
351                }
352            }
353            for (&component, &meta) in zip_exact(&*state.components, &*state.meta) {
354                if let Some(sparse_set) = fetch.sparse_sets.get(component) {
355                    fetch.storage = FetchStorage::SparseSet {
356                        components: sparse_set,
357                        meta,
358                    };
359                    return;
360                }
361            }
362            // At least one of the components must be present in the table/sparse set.
363            debug_unreachable()
364        }
365    }
366
367    #[inline]
368    unsafe fn set_table<'w>(
369        fetch: &mut OneTraitFetch<'w, Trait>,
370        state: &Self::State,
371        table: &'w bevy_ecs::storage::Table,
372    ) {
373        unsafe {
374            // Search for a registered trait impl that is present in the table.
375            //
376            // without loss of generality we use the zero-th row since we only care about whether the
377            // component exists in the table
378            let row = TableRow::from_usize(0);
379            for (&component_id, &meta) in std::iter::zip(&*state.components, &*state.meta) {
380                if let Some(table_storage) = get_table_fetch_data(table, component_id, row, meta) {
381                    fetch.storage = table_storage;
382                    return;
383                }
384            }
385            // At least one of the components must be present in the table.
386            debug_unreachable()
387        }
388    }
389
390    #[inline]
391    fn update_component_access(
392        state: &Self::State,
393        access: &mut bevy_ecs::query::FilteredAccess<ComponentId>,
394    ) {
395        let mut new_access = access.clone();
396        let mut not_first = false;
397        for &component in &*state.components {
398            assert!(
399                !access.access().has_component_write(component),
400                "&mut {} conflicts with a previous access in this query. Mutable component access must be unique.",
401                std::any::type_name::<Trait>(),
402            );
403            if not_first {
404                let mut intermediate = access.clone();
405                intermediate.add_component_write(component);
406                new_access.append_or(&intermediate);
407                new_access.extend_access(&intermediate);
408            } else {
409                new_access.and_with(component);
410                new_access.access_mut().add_component_write(component);
411                not_first = true;
412            }
413        }
414        *access = new_access;
415    }
416
417    #[inline]
418    fn init_state(world: &mut World) -> Self::State {
419        TraitQueryState::init(world)
420    }
421
422    #[inline]
423    fn get_state(_: &Components) -> Option<Self::State> {
424        // TODO: fix this https://github.com/bevyengine/bevy/issues/13798
425        panic!("transmuting and any other operations concerning the state of a query are currently broken and shouldn't be used. See https://github.com/JoJoJet/bevy-trait-query/issues/59");
426    }
427
428    #[inline]
429    fn matches_component_set(
430        state: &Self::State,
431        set_contains_id: &impl Fn(ComponentId) -> bool,
432    ) -> bool {
433        state.matches_component_set_one(set_contains_id)
434    }
435
436    #[inline]
437    fn shrink_fetch<'wlong: 'wshort, 'wshort>(fetch: Self::Fetch<'wlong>) -> Self::Fetch<'wshort> {
438        fetch
439    }
440}
441
442// gets all the relevant data repeatingly used for the table storage
443#[inline]
444unsafe fn get_table_fetch_data<Trait: ?Sized + TraitQuery>(
445    table: &bevy_ecs::storage::Table,
446    component_id: ComponentId,
447    row: TableRow,
448    meta: TraitImplMeta<Trait>,
449) -> Option<FetchStorage<Trait>> {
450    unsafe {
451        let ptr = table.get_component(component_id, row)?;
452        let location = table.get_changed_by(component_id, row).transpose()?;
453        let added = table.get_added_ticks_slice_for(component_id)?;
454        let changed = table.get_changed_ticks_slice_for(component_id)?;
455        Some(FetchStorage::Table {
456            column: ptr,
457            added_ticks: added.into(),
458            changed_ticks: changed.into(),
459            location,
460            meta,
461        })
462    }
463}