Skip to main content

rs_ecs/
archetype.rs

1use std::alloc::{alloc, dealloc, handle_alloc_error, Layout};
2use std::any::TypeId;
3use std::cmp::Reverse;
4use std::mem::{align_of, needs_drop, transmute};
5use std::ptr::{copy_nonoverlapping, drop_in_place, slice_from_raw_parts_mut};
6
7use crate::{resources::TypeIdMap, world::Entity};
8
9pub struct Archetype {
10    types: Box<[TypeMetadata]>,
11    layout: Layout,
12    len: u32,
13    cap: u32,
14    ptr: *mut u8,
15}
16
17unsafe impl Send for Archetype {}
18
19impl Archetype {
20    pub fn new(types: TypeMetadataSet) -> Self {
21        assert!(types.0.len() <= u16::MAX as usize);
22
23        let max_align = types
24            .0
25            .iter()
26            .map(|ty| ty.layout.align())
27            .max()
28            .unwrap_or(1);
29
30        let layout = Layout::from_size_align(0, max_align).unwrap();
31        let ptr = invalid_ptr(max_align);
32
33        Self {
34            types: types.0.into(),
35            layout,
36            len: 0,
37            cap: 0,
38            ptr,
39        }
40    }
41}
42
43impl Archetype {
44    pub fn len(&self) -> u32 {
45        self.len
46    }
47
48    pub unsafe fn alloc(&mut self) -> u32 {
49        if self.len == self.cap {
50            self.grow();
51        }
52
53        let idx = self.len;
54        self.len += 1;
55        idx
56    }
57
58    #[must_use]
59    pub unsafe fn free<const DROP: bool>(&mut self, idx: u32) -> bool {
60        self.len -= 1;
61
62        let _copy_on_drop;
63
64        let swapped = if idx != self.len {
65            struct CopyOnDrop<'a> {
66                types: &'a [TypeMetadata],
67                len: u32,
68                idx: u32,
69            }
70
71            impl Drop for CopyOnDrop<'_> {
72                fn drop(&mut self) {
73                    for ty in self.types {
74                        let size = ty.layout.size();
75
76                        unsafe {
77                            let src_ptr = ty.base_pointer.add(size * self.len as usize);
78                            let dst_ptr = ty.base_pointer.add(size * self.idx as usize);
79
80                            copy_nonoverlapping(src_ptr, dst_ptr, size);
81                        }
82                    }
83                }
84            }
85
86            _copy_on_drop = CopyOnDrop {
87                types: &self.types,
88                len: self.len,
89                idx,
90            };
91
92            true
93        } else {
94            false
95        };
96
97        if DROP {
98            for ty in &*self.types {
99                if let Some(drop) = ty.drop {
100                    let ptr = ty.base_pointer.add(ty.layout.size() * idx as usize);
101
102                    drop(ptr, 1);
103                }
104            }
105        }
106
107        swapped
108    }
109
110    pub fn clear(&mut self) {
111        let len = self.len;
112        self.len = 0;
113
114        for ty in &*self.types {
115            if let Some(drop) = ty.drop {
116                unsafe {
117                    drop(ty.base_pointer, len as usize);
118                }
119            }
120        }
121    }
122
123    #[cold]
124    #[inline(never)]
125    fn grow(&mut self) {
126        let new_cap = self.cap.checked_add(self.cap.max(8)).unwrap() as usize;
127
128        struct SortOnDrop<'a>(&'a mut [TypeMetadata]);
129
130        impl Drop for SortOnDrop<'_> {
131            fn drop(&mut self) {
132                self.0.sort_unstable_by_key(|ty| ty.id);
133            }
134        }
135
136        let types = SortOnDrop(&mut self.types);
137
138        types
139            .0
140            .sort_unstable_by_key(|ty| Reverse(ty.layout.align()));
141
142        let mut new_offsets = Vec::<usize>::with_capacity(types.0.len());
143        let mut new_size = 0;
144
145        for ty in types.0.iter() {
146            new_offsets.push(new_size);
147
148            new_size = new_size
149                .checked_add(ty.layout.size().checked_mul(new_cap).unwrap())
150                .unwrap();
151        }
152
153        let new_layout = Layout::from_size_align(new_size, self.layout.align()).unwrap();
154
155        let new_ptr = alloc_ptr(new_layout);
156
157        unsafe {
158            for (ty, new_offset) in types.0.iter_mut().zip(&new_offsets) {
159                let new_base_pointer = new_ptr.add(*new_offset);
160
161                copy_nonoverlapping(
162                    ty.base_pointer,
163                    new_base_pointer,
164                    ty.layout.size() * self.cap as usize,
165                );
166
167                ty.base_pointer = new_base_pointer;
168            }
169
170            if self.layout.size() != 0 {
171                dealloc(self.ptr, self.layout);
172            }
173        }
174
175        self.layout = new_layout;
176        self.ptr = new_ptr;
177
178        self.cap = new_cap as u32;
179    }
180}
181
182impl Drop for Archetype {
183    fn drop(&mut self) {
184        self.clear();
185
186        unsafe {
187            if self.layout.size() != 0 {
188                dealloc(self.ptr, self.layout);
189            }
190        }
191    }
192}
193
194impl Archetype {
195    pub fn types(&self) -> TypeMetadataSet {
196        TypeMetadataSet(self.types.to_vec())
197    }
198
199    pub fn match_(&self, types: &TypeMetadataSet) -> bool {
200        let lhs = self.types.iter().map(|ty| ty.id);
201        let rhs = types.0.iter().map(|ty| ty.id);
202
203        lhs.eq(rhs)
204    }
205
206    pub fn find<C>(&self) -> Option<u16>
207    where
208        C: 'static,
209    {
210        self.find_impl(&TypeId::of::<C>())
211    }
212
213    fn find_impl(&self, key: &TypeId) -> Option<u16> {
214        self.types
215            .binary_search_by_key(key, |ty| ty.id)
216            .map(|idx| idx as u16)
217            .ok()
218    }
219}
220
221impl Archetype {
222    #[allow(clippy::needless_pass_by_ref_mut)]
223    pub unsafe fn move_(src: &mut Self, dst: &mut Self, src_idx: u32, dst_idx: u32) {
224        debug_assert!(src_idx < src.len);
225        debug_assert!(dst_idx < dst.len);
226
227        let mut dst_types = &*dst.types;
228
229        for src_ty in &*src.types {
230            let dst_ty = match dst_types.iter().position(|ty| ty.id == src_ty.id) {
231                Some(dst_ty) => {
232                    dst_types = &dst_types[dst_ty..];
233                    &dst_types[0]
234                }
235                None => continue,
236            };
237
238            let size = src_ty.layout.size();
239
240            let src_ptr = src_ty.base_pointer.add(size * src_idx as usize);
241            let dst_ptr = dst_ty.base_pointer.add(size * dst_idx as usize);
242
243            copy_nonoverlapping(src_ptr, dst_ptr, size);
244        }
245    }
246}
247
248impl Archetype {
249    #[cfg(debug_assertions)]
250    fn type_metadata<C>(&self, ty: u16) -> &TypeMetadata
251    where
252        C: 'static,
253    {
254        let ty = &self.types[ty as usize];
255        assert_eq!(ty.id, TypeId::of::<C>());
256        ty
257    }
258
259    #[cfg(not(debug_assertions))]
260    unsafe fn type_metadata_unchecked(&self, ty: u16) -> &TypeMetadata {
261        self.types.get_unchecked(ty as usize)
262    }
263
264    pub unsafe fn base_pointer<C>(&self, ty: u16) -> *mut C
265    where
266        C: 'static,
267    {
268        #[cfg(debug_assertions)]
269        let ty = self.type_metadata::<C>(ty);
270        #[cfg(not(debug_assertions))]
271        let ty = self.type_metadata_unchecked(ty);
272
273        ty.base_pointer.cast::<C>()
274    }
275
276    pub unsafe fn pointer<C>(&self, ty: u16, idx: u32) -> *mut C
277    where
278        C: 'static,
279    {
280        let ptr = self.base_pointer::<C>(ty);
281
282        debug_assert!(idx < self.len);
283        ptr.add(idx as usize)
284    }
285}
286
287impl Archetype {
288    pub unsafe fn get<C>(&self, idx: u32) -> *mut C
289    where
290        C: 'static,
291    {
292        let ty = self.find::<C>().unwrap();
293        self.pointer::<C>(ty, idx)
294    }
295
296    pub unsafe fn drop<C>(&mut self, idx: u32)
297    where
298        C: 'static,
299    {
300        if needs_drop::<C>() {
301            if let Some(ty) = self.find::<C>() {
302                let ty = self.types.get_unchecked(ty as usize);
303
304                let ptr = ty.base_pointer.add(ty.layout.size() * idx as usize);
305
306                (ty.drop.unwrap())(ptr, 1);
307            }
308        }
309    }
310}
311
312impl Archetype {
313    pub fn clone(&mut self, cloner: &Cloner) -> Self {
314        let ptr = alloc_ptr(self.layout);
315
316        let mut types = self.types.clone();
317
318        unsafe {
319            for ty in &mut *types {
320                let clone = cloner
321                    .clone
322                    .get(&ty.id)
323                    .expect("Component type missing from cloner");
324
325                let offset = ty.base_pointer.offset_from(self.ptr);
326                let new_base_pointer = ptr.offset(offset);
327
328                clone(ty.base_pointer, new_base_pointer, self.len as usize);
329
330                ty.base_pointer = new_base_pointer;
331            }
332        }
333
334        Self {
335            types,
336            layout: self.layout,
337            len: self.len,
338            cap: self.cap,
339            ptr,
340        }
341    }
342}
343
344#[derive(Clone, Copy)]
345struct TypeMetadata {
346    id: TypeId,
347    layout: Layout,
348    drop: Option<unsafe fn(*mut u8, usize)>,
349    base_pointer: *mut u8,
350}
351
352impl TypeMetadata {
353    fn new<C>() -> Self
354    where
355        C: 'static,
356    {
357        unsafe fn drop<C>(ptr: *mut u8, len: usize) {
358            drop_in_place(slice_from_raw_parts_mut(ptr.cast::<C>(), len))
359        }
360
361        Self {
362            id: TypeId::of::<C>(),
363            layout: Layout::new::<C>(),
364            drop: if needs_drop::<C>() {
365                Some(drop::<C>)
366            } else {
367                None
368            },
369            base_pointer: invalid_ptr(align_of::<C>()),
370        }
371    }
372}
373
374#[derive(Default)]
375pub struct TypeMetadataSet(Vec<TypeMetadata>);
376
377impl TypeMetadataSet {
378    pub fn ids(&self) -> impl ExactSizeIterator<Item = TypeId> + '_ {
379        self.0.iter().map(|ty| ty.id)
380    }
381
382    pub fn insert<C>(&mut self)
383    where
384        C: 'static,
385    {
386        if let Err(ty) = self.0.binary_search_by_key(&TypeId::of::<C>(), |ty| ty.id) {
387            self.0.insert(ty, TypeMetadata::new::<C>());
388        }
389    }
390
391    #[must_use]
392    pub fn remove<C>(&mut self) -> Option<()>
393    where
394        C: 'static,
395    {
396        let ty = self
397            .0
398            .binary_search_by_key(&TypeId::of::<C>(), |ty| ty.id)
399            .ok()?;
400
401        self.0.remove(ty);
402
403        Some(())
404    }
405}
406
407fn alloc_ptr(layout: Layout) -> *mut u8 {
408    if layout.size() == 0 {
409        return invalid_ptr(layout.align());
410    }
411
412    let ptr = unsafe { alloc(layout) };
413    if ptr.is_null() {
414        handle_alloc_error(layout);
415    }
416    ptr
417}
418
419fn invalid_ptr(addr: usize) -> *mut u8 {
420    unsafe {
421        #[allow(integer_to_ptr_transmutes)]
422        transmute(addr)
423    }
424}
425
426/// Collects component types which can be cloned or copied
427///
428/// Populating such an object with all used component types is required to use [`World::clone`][crate::World::clone].
429pub struct Cloner {
430    clone: TypeIdMap<unsafe fn(*const u8, *mut u8, usize)>,
431}
432
433impl Cloner {
434    /// Creates a new cloner which contains only the [`Entity`] component type
435    pub fn new() -> Self {
436        let mut _self = Self {
437            clone: Default::default(),
438        };
439
440        _self.add_copyable::<Entity>();
441
442        _self
443    }
444}
445
446impl Default for Cloner {
447    fn default() -> Self {
448        Self::new()
449    }
450}
451
452impl Cloner {
453    /// Adds a component type which is cloneable
454    ///
455    /// If `C` is actually copyable, using [`add_copyable`][Self::add_copyable] is more efficient.
456    pub fn add_cloneable<C>(&mut self)
457    where
458        C: Clone + 'static,
459    {
460        unsafe fn clone<C>(src: *const u8, dst: *mut u8, len: usize)
461        where
462            C: Clone,
463        {
464            let src = src.cast::<C>();
465            let dst = dst.cast::<C>();
466
467            for idx in 0..len {
468                let val = (*src.add(idx)).clone();
469                dst.add(idx).write(val);
470            }
471        }
472
473        self.clone.insert(TypeId::of::<C>(), clone::<C>);
474    }
475
476    /// Adds a component type which is copyable
477    pub fn add_copyable<C>(&mut self)
478    where
479        C: Copy + 'static,
480    {
481        unsafe fn clone<C>(src: *const u8, dst: *mut u8, len: usize)
482        where
483            C: Copy,
484        {
485            let src = src.cast::<C>();
486            let dst = dst.cast::<C>();
487
488            copy_nonoverlapping(src, dst, len);
489        }
490
491        self.clone.insert(TypeId::of::<C>(), clone::<C>);
492    }
493}
494
495#[cfg(test)]
496mod tests {
497    use super::*;
498
499    use std::cell::Cell;
500    use std::panic::{catch_unwind, AssertUnwindSafe};
501    use std::rc::Rc;
502    use std::thread::panicking;
503
504    #[test]
505    fn reverse_sorting_by_alignment_avoids_padding() {
506        let mut types = TypeMetadataSet::default();
507        types.insert::<u64>();
508        types.insert::<u32>();
509        types.insert::<u16>();
510        types.insert::<u8>();
511
512        let mut archetype = Archetype::new(types);
513        let _ent = unsafe { archetype.alloc() };
514
515        assert_eq!(archetype.layout.size(), 8 * (8 + 4 + 2 + 1));
516        assert_eq!(archetype.layout.align(), 8);
517
518        assert_eq!(archetype.len, 1);
519        assert_eq!(archetype.cap, 8);
520
521        assert_eq!(archetype.types.len(), 4);
522
523        unsafe {
524            let ty = archetype.find::<u64>().unwrap();
525            let base_pointer = archetype.base_pointer::<u64>(ty);
526            assert_eq!(base_pointer, archetype.ptr.add(0).cast());
527
528            let ty = archetype.find::<u32>().unwrap();
529            let base_pointer = archetype.base_pointer::<u32>(ty);
530            assert_eq!(base_pointer, archetype.ptr.add(8 * 8).cast());
531
532            let ty = archetype.find::<u16>().unwrap();
533            let base_pointer = archetype.base_pointer::<u16>(ty);
534            assert_eq!(base_pointer, archetype.ptr.add(8 * (8 + 4)).cast());
535
536            let ty = archetype.find::<u8>().unwrap();
537            let base_pointer = archetype.base_pointer::<u8>(ty);
538            assert_eq!(base_pointer, archetype.ptr.add(8 * (8 + 4 + 2)).cast());
539        }
540    }
541
542    #[test]
543    fn drops_all_component_values() {
544        struct CountDrops(Rc<Cell<usize>>);
545
546        impl Drop for CountDrops {
547            fn drop(&mut self) {
548                let drops = &self.0;
549
550                drops.set(drops.get() + 1);
551            }
552        }
553
554        let drops = Rc::new(Cell::new(0));
555
556        let mut types = TypeMetadataSet::default();
557        types.insert::<CountDrops>();
558
559        let mut archetype = Archetype::new(types);
560
561        unsafe {
562            for _ in 0..32 {
563                let ent = archetype.alloc();
564
565                archetype
566                    .get::<CountDrops>(ent)
567                    .write(CountDrops(drops.clone()));
568            }
569        }
570
571        drop(archetype);
572
573        assert_eq!(drops.get(), 32);
574    }
575
576    struct MaybePanicOnDrop(bool);
577
578    impl Drop for MaybePanicOnDrop {
579        fn drop(&mut self) {
580            if self.0 && !panicking() {
581                panic!();
582            }
583        }
584    }
585
586    #[test]
587    fn free_does_not_double_drop_on_panic() {
588        let mut types = TypeMetadataSet::default();
589        types.insert::<MaybePanicOnDrop>();
590
591        let mut archetype = Archetype::new(types);
592
593        unsafe {
594            for idx in 0..4 {
595                let ent = archetype.alloc();
596
597                archetype
598                    .get::<MaybePanicOnDrop>(ent)
599                    .write(MaybePanicOnDrop(idx == 2));
600            }
601        }
602
603        let res = catch_unwind(AssertUnwindSafe(|| unsafe { archetype.free::<true>(2) }));
604        assert!(res.is_err());
605
606        assert_eq!(archetype.len(), 3);
607    }
608
609    #[test]
610    fn clear_does_not_double_drop_on_panic() {
611        let mut types = TypeMetadataSet::default();
612        types.insert::<MaybePanicOnDrop>();
613
614        let mut archetype = Archetype::new(types);
615
616        unsafe {
617            for idx in 0..4 {
618                let ent = archetype.alloc();
619
620                archetype
621                    .get::<MaybePanicOnDrop>(ent)
622                    .write(MaybePanicOnDrop(idx == 2));
623            }
624        }
625
626        let res = catch_unwind(AssertUnwindSafe(|| archetype.clear()));
627        assert!(res.is_err());
628
629        assert_eq!(archetype.len(), 0);
630    }
631}