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}