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