Skip to main content

bevy_ecs/storage/table/
column.rs

1use crate::{
2    change_detection::{AtomicTick, CheckChangeTicks, ComponentTicks, MaybeLocation, Tick},
3    component::ComponentInfo,
4    storage::{blob_array::BlobArray, thin_array_ptr::ThinArrayPtr, TableRow},
5};
6use bevy_ptr::{OwningPtr, Ptr, UnsafeCellDeref};
7use core::{cell::UnsafeCell, mem::needs_drop, num::NonZeroUsize, panic::Location};
8
9/// A type-erased contiguous container for data of a homogeneous type.
10///
11/// Conceptually, a `Column` is very similar to a type-erased `Box<[T]>`.
12/// It also stores the change detection ticks for its components, kept in two separate
13/// contiguous buffers internally. An element shares its data across these buffers by using the
14/// same index (i.e. the entity at row 3 has it's data at index 3 and its change detection ticks at index 3).
15///
16/// Like many other low-level storage types, `Column` has a limited and highly unsafe
17/// interface. It's highly advised to use higher level types and their safe abstractions
18/// instead of working directly with `Column`.
19///
20/// For performance reasons, `Column` does not store its capacity and length.
21/// This type is used by [`Table`] and [`ComponentSparseSet`], where the corresponding capacity
22/// and length can be found.
23///
24/// [`Table`]: crate::storage::Table
25/// [`ComponentSparseSet`]: crate::storage::ComponentSparseSet
26#[derive(Debug)]
27pub struct Column {
28    data: BlobArray,
29    added_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
30    changed_ticks: ThinArrayPtr<UnsafeCell<Tick>>,
31    changed_by: MaybeLocation<ThinArrayPtr<UnsafeCell<&'static Location<'static>>>>,
32    summary_tick: Option<AtomicTick>,
33}
34
35impl Column {
36    /// Create a new [`Column`] with the given `capacity`.
37    pub fn with_capacity(component_info: &ComponentInfo, capacity: usize) -> Self {
38        Self {
39            // SAFETY:
40            // * The components stored in this columns will match the information in `component_info`
41            // * `ComponentInfo` ensures that `layout().size()` is a multiple of `layout().align()`
42            data: unsafe {
43                BlobArray::with_capacity(component_info.layout(), component_info.drop(), capacity)
44            },
45            added_ticks: ThinArrayPtr::with_capacity(capacity),
46            changed_ticks: ThinArrayPtr::with_capacity(capacity),
47            changed_by: MaybeLocation::new_with(|| ThinArrayPtr::with_capacity(capacity)),
48            summary_tick: if component_info.summary_tick() {
49                // Set this to zero for now; when we initialize the column by
50                // inserting a component it'll be updated with the correct
51                // value.
52                Some(AtomicTick::default())
53            } else {
54                None
55            },
56        }
57    }
58
59    /// Swap-remove and drop the removed element, but the component at `row` must not be the last element.
60    ///
61    /// # Safety
62    /// - `row.as_usize()` < `len`
63    /// - `last_element_index` = `len - 1`
64    /// - `last_element_index` != `row.as_usize()`
65    /// -   The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
66    pub(crate) unsafe fn swap_remove_and_drop_unchecked_nonoverlapping(
67        &mut self,
68        last_element_index: usize,
69        row: TableRow,
70    ) {
71        self.data
72            .swap_remove_and_drop_unchecked_nonoverlapping(row.index(), last_element_index);
73        self.added_ticks
74            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
75        self.changed_ticks
76            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
77        self.changed_by.as_mut().map(|changed_by| {
78            changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
79        });
80    }
81
82    /// Swap-remove the provided row.
83    ///
84    /// If `DROP` is `true`, the removed element will be dropped as needed.
85    ///
86    /// If `DROP` is `false`, the removed element will be forgotten.
87    ///
88    /// # Safety
89    /// - `last_element_index` must be the index of the last element.
90    /// - `row.index()` <= `last_element_index`
91    /// - The caller should update their saved length to reflect the change (decrement it by 1).
92    pub(crate) unsafe fn swap_remove_unchecked<const DROP: bool>(
93        &mut self,
94        last_element_index: usize,
95        row: TableRow,
96    ) {
97        if DROP {
98            self.data
99                .swap_remove_and_drop_unchecked(row.index(), last_element_index);
100        } else {
101            _ = self
102                .data
103                .swap_remove_unchecked(row.index(), last_element_index);
104        }
105        self.added_ticks
106            .swap_remove_unchecked(row.index(), last_element_index);
107        self.changed_ticks
108            .swap_remove_unchecked(row.index(), last_element_index);
109        self.changed_by.as_mut().map(|changed_by| {
110            changed_by.swap_remove_unchecked(row.index(), last_element_index);
111        });
112    }
113
114    /// Swap-remove and forgets the removed element, but the component at `row` must not be the last element.
115    ///
116    /// # Safety
117    /// - `row.as_usize()` < `len`
118    /// - `last_element_index` = `len - 1`
119    /// - `last_element_index` != `row.as_usize()`
120    /// -   The caller should update the `len` to `len - 1`, or immediately initialize another element in the `last_element_index`
121    pub(crate) unsafe fn swap_remove_and_forget_unchecked_nonoverlapping(
122        &mut self,
123        last_element_index: usize,
124        row: TableRow,
125    ) -> OwningPtr<'_> {
126        let data = self
127            .data
128            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
129        self.added_ticks
130            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
131        self.changed_ticks
132            .swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
133        self.changed_by.as_mut().map(|changed_by| {
134            changed_by.swap_remove_unchecked_nonoverlapping(row.index(), last_element_index);
135        });
136        data
137    }
138
139    /// Call [`realloc`](std::alloc::realloc) to expand / shrink the memory allocation for this [`Column`]
140    ///
141    /// # Panics
142    /// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
143    /// - Panics if the any of the reallocations causes an out-of-memory error.
144    ///
145    /// # Safety
146    /// - `current_capacity` must be the current capacity of this column (the capacity of `self.data`, `self.added_ticks`, `self.changed_tick`)
147    /// -   The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
148    pub(crate) unsafe fn realloc(
149        &mut self,
150        current_capacity: NonZeroUsize,
151        new_capacity: NonZeroUsize,
152    ) {
153        self.data.realloc(current_capacity, new_capacity);
154        self.added_ticks.realloc(current_capacity, new_capacity);
155        self.changed_ticks.realloc(current_capacity, new_capacity);
156        self.changed_by
157            .as_mut()
158            .map(|changed_by| changed_by.realloc(current_capacity, new_capacity));
159    }
160
161    /// Call [`alloc`](std::alloc::alloc) to allocate memory for this [`Column`]
162    /// The caller should make sure their saved `capacity` value is updated to `new_capacity` after this operation.
163    ///
164    /// # Panics
165    /// - Panics if the any of the new capacity overflows `isize::MAX` bytes.
166    /// - Panics if the any of the allocations causes an out-of-memory error.
167    pub(crate) fn alloc(&mut self, new_capacity: NonZeroUsize) {
168        self.data.alloc(new_capacity);
169        self.added_ticks.alloc(new_capacity);
170        self.changed_ticks.alloc(new_capacity);
171        self.changed_by
172            .as_mut()
173            .map(|changed_by| changed_by.alloc(new_capacity));
174    }
175
176    /// Writes component data to the column at the given row.
177    /// Assumes the slot is uninitialized, drop is not called.
178    /// To overwrite existing initialized value, use [`Self::replace`] instead.
179    ///
180    /// # Safety
181    /// - `row.as_usize()` must be in bounds.
182    /// - `data` holds a component that matches the `component_id`
183    #[inline]
184    pub(crate) unsafe fn initialize(
185        &mut self,
186        row: TableRow,
187        data: OwningPtr<'_>,
188        tick: Tick,
189        caller: MaybeLocation,
190    ) {
191        self.data.initialize_unchecked(row.index(), data);
192        self.added_ticks
193            .initialize_unchecked(row.index(), UnsafeCell::new(tick));
194        self.changed_ticks
195            .initialize_unchecked(row.index(), UnsafeCell::new(tick));
196        self.changed_by
197            .as_mut()
198            .zip(caller)
199            .map(|(changed_by, caller)| {
200                changed_by.initialize_unchecked(row.index(), UnsafeCell::new(caller));
201            });
202        if let Some(summary_tick) = &self.summary_tick {
203            Self::update_summary_tick(summary_tick, tick);
204        }
205    }
206
207    /// Overwrites component data to the column at given row. The previous value is dropped.
208    ///
209    /// # Safety
210    /// - There must be a valid initialized value stored at `row`.
211    /// - `row.as_usize()` must be in bounds.
212    /// - `data` holds a component that matches the `component_id`
213    #[inline]
214    pub(crate) unsafe fn replace(
215        &mut self,
216        row: TableRow,
217        data: OwningPtr<'_>,
218        change_tick: Tick,
219        caller: MaybeLocation,
220    ) {
221        self.data.replace_unchecked(row.index(), data);
222        *self.changed_ticks.get_unchecked_mut(row.index()).get_mut() = change_tick;
223        self.changed_by
224            .as_mut()
225            .map(|changed_by| changed_by.get_unchecked_mut(row.index()).get_mut())
226            .assign(caller);
227        if let Some(summary_tick) = &self.summary_tick {
228            Self::update_summary_tick(summary_tick, change_tick);
229        }
230    }
231
232    /// Removes the element from `src` at `src_row` and inserts it
233    /// into this column to initialize the values at `dst_row`.
234    /// Does not do any bounds checking.
235    ///
236    /// `change_tick` must be the change tick of the current system.
237    ///
238    /// # Safety
239    ///  - `src` must have the same data layout as `self`
240    ///  - `src_row` must be in bounds for `src`
241    ///  - `dst_row` must be in bounds for `self`
242    ///  - `src[src_row]` must be initialized to a valid value.
243    ///  - `self[dst_row]` must not be initialized yet.
244    ///  - `src_last_element_index` must be the last element in `src`
245    #[inline]
246    pub(crate) unsafe fn initialize_from_unchecked(
247        &mut self,
248        src: &mut Column,
249        src_last_element_index: usize,
250        src_row: TableRow,
251        dst_row: TableRow,
252        this_run: Tick,
253    ) {
254        debug_assert!(self.data.layout() == src.data.layout());
255
256        // Making this a cold path avoids most of the performance cost.
257        #[cold]
258        fn update_summary_tick_from_row(
259            changed_ticks: &ThinArrayPtr<UnsafeCell<Tick>>,
260            summary_tick: &AtomicTick,
261            dst_row: TableRow,
262            this_run: Tick,
263        ) {
264            // SAFETY:
265            // - Changed tick just got initialized at dst_row
266            // - There are no mutable references to the changed tick
267            let row_change_tick = unsafe { changed_ticks.get_unchecked(dst_row.index()).read() };
268            if row_change_tick.is_newer_than(summary_tick.get(), this_run) {
269                summary_tick.set(row_change_tick);
270            }
271        }
272
273        // SAFETY:
274        // In bounds, same layout & correct last element index as per preconditions
275        unsafe {
276            self.data.initialize_from_swap_remove_unchecked(
277                &mut src.data,
278                src_last_element_index,
279                src_row.index(),
280                dst_row.index(),
281            );
282            self.added_ticks.initialize_from_swap_remove_unchecked(
283                &mut src.added_ticks,
284                src_last_element_index,
285                src_row.index(),
286                dst_row.index(),
287            );
288            self.changed_ticks.initialize_from_swap_remove_unchecked(
289                &mut src.changed_ticks,
290                src_last_element_index,
291                src_row.index(),
292                dst_row.index(),
293            );
294            self.changed_by.as_mut().zip(src.changed_by.as_mut()).map(
295                |(self_changed_by, src_changed_by)| {
296                    self_changed_by.initialize_from_swap_remove_unchecked(
297                        src_changed_by,
298                        src_last_element_index,
299                        src_row.index(),
300                        dst_row.index(),
301                    );
302                },
303            );
304        }
305
306        if let Some(summary_tick) = &self.summary_tick {
307            update_summary_tick_from_row(&self.changed_ticks, summary_tick, dst_row, this_run);
308        }
309    }
310
311    /// Calls [`Tick::check_tick`] on all of the ticks stored in this column, as
312    /// well as the summary tick, if one is present.
313    ///
314    /// # Safety
315    /// `len` is the actual length of this column
316    #[inline]
317    pub(crate) unsafe fn check_change_ticks(&mut self, len: usize, check: CheckChangeTicks) {
318        for i in 0..len {
319            // SAFETY:
320            // - `i` < `len`
321            // we have a mutable reference to `self`
322            unsafe { self.added_ticks.get_unchecked_mut(i) }
323                .get_mut()
324                .check_tick(check);
325            // SAFETY:
326            // - `i` < `len`
327            // we have a mutable reference to `self`
328            unsafe { self.changed_ticks.get_unchecked_mut(i) }
329                .get_mut()
330                .check_tick(check);
331        }
332
333        // Update the summary tick, if one is present.
334        if let Some(summary_tick) = &self.summary_tick {
335            let mut summary_tick_value = summary_tick.get();
336            if summary_tick_value.check_tick(check) {
337                summary_tick.set(summary_tick_value);
338            }
339        }
340    }
341
342    /// Clear all the components from this column.
343    ///
344    /// # Safety
345    /// - `len` must match the actual length of the column
346    /// -   The caller must not use the elements this column's data until [`initializing`](Self::initialize) it again (set `len` to 0).
347    pub(crate) unsafe fn clear(&mut self, len: usize) {
348        self.added_ticks.clear_elements(len);
349        self.changed_ticks.clear_elements(len);
350        self.data.clear(len);
351        self.changed_by
352            .as_mut()
353            .map(|changed_by| changed_by.clear_elements(len));
354    }
355
356    /// Because this method needs parameters, it can't be the implementation of the `Drop` trait.
357    /// The owner of this [`Column`] must call this method with the correct information.
358    ///
359    /// # Safety
360    /// - `len` is indeed the length of the column
361    /// - `cap` is indeed the capacity of the column
362    /// - the data stored in `self` will never be used again
363    pub(crate) unsafe fn drop(&mut self, cap: usize, len: usize) {
364        self.added_ticks.drop(cap, len);
365        self.changed_ticks.drop(cap, len);
366        self.data.drop(cap, len);
367        self.changed_by
368            .as_mut()
369            .map(|changed_by| changed_by.drop(cap, len));
370    }
371
372    /// Drops the last component in this column.
373    ///
374    /// # Safety
375    /// - `last_element_index` is indeed the index of the last element
376    /// - the data stored in `last_element_index` will never be used unless properly initialized again.
377    pub(crate) unsafe fn drop_last_component(&mut self, last_element_index: usize) {
378        const {
379            assert!(!needs_drop::<UnsafeCell<Tick>>());
380            assert!(!needs_drop::<UnsafeCell<&'static Location<'static>>>());
381        }
382        self.data.drop_last_element(last_element_index);
383    }
384
385    /// Get a slice to the data stored in this [`Column`].
386    ///
387    /// # Safety
388    /// - `T` must match the type of data that's stored in this [`Column`]
389    /// - `len` must match the actual length of this column (number of elements stored)
390    pub unsafe fn get_data_slice<T>(&self, len: usize) -> &[UnsafeCell<T>] {
391        // SAFETY: Upheld by caller
392        unsafe { self.data.get_sub_slice(len) }
393    }
394
395    /// Get a slice to the added [`ticks`](Tick) in this [`Column`].
396    ///
397    /// # Safety
398    /// - `len` must match the actual length of this column (number of elements stored)
399    pub unsafe fn get_added_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
400        // SAFETY: Upheld by caller
401        unsafe { self.added_ticks.as_slice(len) }
402    }
403
404    /// Get a slice to the changed [`ticks`](Tick) in this [`Column`].
405    ///
406    /// # Safety
407    /// - `len` must match the actual length of this column (number of elements stored)
408    pub unsafe fn get_changed_ticks_slice(&self, len: usize) -> &[UnsafeCell<Tick>] {
409        // SAFETY: Upheld by caller
410        unsafe { self.changed_ticks.as_slice(len) }
411    }
412
413    /// Get a slice to the calling locations that last changed each value in this [`Column`]
414    ///
415    /// # Safety
416    /// - `len` must match the actual length of this column (number of elements stored)
417    pub unsafe fn get_changed_by_slice(
418        &self,
419        len: usize,
420    ) -> MaybeLocation<&[UnsafeCell<&'static Location<'static>>]> {
421        self.changed_by
422            .as_ref()
423            .map(|changed_by| changed_by.as_slice(len))
424    }
425
426    /// Fetches a read-only reference to the data at `row`. This does not
427    /// do any bounds checking.
428    ///
429    /// # Safety
430    /// - `row` must be within the range `[0, self.len())`.
431    /// - no other mutable reference to the data of the same row can exist at the same time
432    #[inline]
433    pub unsafe fn get_data_unchecked(&self, row: TableRow) -> Ptr<'_> {
434        self.data.get_unchecked(row.index())
435    }
436
437    /// Fetches the calling location that last changed the value at `row`.
438    ///
439    /// This function does not do any bounds checking.
440    ///
441    /// # Safety
442    /// `row` must be within the range `[0, self.len())`.
443    #[inline]
444    pub unsafe fn get_changed_by_unchecked(
445        &self,
446        row: TableRow,
447    ) -> MaybeLocation<&UnsafeCell<&'static Location<'static>>> {
448        self.changed_by
449            .as_ref()
450            .map(|changed_by| changed_by.get_unchecked(row.index()))
451    }
452
453    /// Fetches the "added" change detection tick for the value at `row`.
454    /// This function does not do any bounds checking.
455    ///
456    /// # Safety
457    /// `row` must be within the range `[0, self.len())`.
458    #[inline]
459    pub unsafe fn get_added_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
460        // SAFETY: Upheld by caller
461        unsafe { self.added_ticks.get_unchecked(row.index()) }
462    }
463
464    /// Fetches the "changed" change detection tick for the value at `row`
465    /// This function does not do any bounds checking.
466    ///
467    /// # Safety
468    /// `row` must be within the range `[0, self.len())`.
469    #[inline]
470    pub unsafe fn get_changed_tick_unchecked(&self, row: TableRow) -> &UnsafeCell<Tick> {
471        // SAFETY: Upheld by caller
472        unsafe { self.changed_ticks.get_unchecked(row.index()) }
473    }
474
475    /// Fetches the change detection ticks for the value at `row`.
476    /// This function does not do any bounds checking.
477    ///
478    /// # Safety
479    /// `row` must be within the range `[0, self.len())`.
480    #[inline]
481    pub unsafe fn get_ticks_unchecked(&self, row: TableRow) -> ComponentTicks {
482        ComponentTicks {
483            added: self.added_ticks.get_unchecked(row.index()).read(),
484            changed: self.changed_ticks.get_unchecked(row.index()).read(),
485        }
486    }
487
488    /// Returns the drop function for elements of the column,
489    /// or `None` if they don't need to be dropped.
490    #[inline]
491    pub fn get_drop(&self) -> Option<unsafe fn(OwningPtr<'_>)> {
492        self.data.get_drop()
493    }
494
495    /// Returns a reference to the summary tick for this column, if the
496    /// component that this column is associated with has a summary tick.
497    ///
498    /// The summary tick stores the most recent changed timestamp that was
499    /// written to any component instance of this column. "Most recent" here
500    /// refers to the wall clock.
501    ///
502    /// Be careful when using this value, as it's easy to misuse. Because
503    /// multiple systems with different ticks can be concurrently writing to a
504    /// single column, and because "most recent" is in reference to wall clock
505    /// time, *there may be ticks in the column that are logically later than
506    /// the summary tick.* It's therefore incorrect to assume that the summary
507    /// tick represents the latest tick stored in the column.
508    ///
509    /// Importantly, however, this situation can only occur when multiple
510    /// systems are *actually* concurrently writing to a column. This can only
511    /// happen when both systems are writing to a column with sparse queries.
512    /// Systems that iterate over components with dense iteration have immutable
513    /// or exclusive access to the columns corresponding to those components in
514    /// the tables that they iterate over (and this fact is what makes
515    /// contiguous iteration safe to begin with). So, *for a dense query*, we
516    /// can guarantee that if the `last_run` tick for that query is *t*, then if
517    /// the summary tick has a value earlier than *t*, then there have been no
518    /// changes to the column values. Again, this is *only* true for dense
519    /// iteration.
520    #[inline]
521    pub fn get_summary_tick(&self) -> Option<&AtomicTick> {
522        self.summary_tick.as_ref()
523    }
524
525    /// Separate method for the sake of using `#[cold]`,
526    /// since most components won't have summary ticks.
527    #[cold]
528    fn update_summary_tick(summary_tick: &AtomicTick, tick: Tick) {
529        summary_tick.set(tick);
530    }
531}