gloss_hecs/
command_buffer.rs

1// Copyright 2019 Google LLC
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8// use core::any::StableTypeId;
9use crate::stabletypeid::StableTypeId;
10use core::{
11    mem,
12    ops::Range,
13    ptr::{self, NonNull},
14};
15
16// use crate::alloc::alloc::{alloc, dealloc, Layout};
17use crate::{
18    align,
19    alloc::{
20        alloc::{alloc, dealloc},
21        vec::Vec,
22    },
23    archetype::TypeInfo,
24    layout::Layout,
25    Bundle, Component, DynamicBundle, Entity, World,
26};
27
28use gloss_utils::abi_stable_aliases::std_types::{ROption, ROption::RNone, ROption::RSome, RVec};
29
30#[cfg(not(target_arch = "wasm32"))]
31use abi_stable::sabi_extern_fn;
32#[cfg(not(target_arch = "wasm32"))]
33use gloss_utils::abi_stable_aliases::StableAbi;
34
35/// Records operations for future application to a [`World`]
36///
37/// Useful when operations cannot be applied directly due to ordering concerns
38/// or borrow checking.
39///
40/// ```
41/// # use gloss_hecs::*;
42/// let mut world = World::new();
43/// let entity = world.reserve_entity();
44/// let mut cmd = CommandBuffer::new();
45/// cmd.insert(entity, (true, 42));
46/// cmd.run_on(&mut world); // cmd can now be reused
47/// assert_eq!(*world.get::<&i32>(entity).unwrap(), 42);
48/// ```
49#[repr(C)]
50#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
51pub struct CommandBuffer {
52    cmds: RVec<Cmd>,
53    storage: NonNull<u8>,
54    layout: Layout,
55    cursor: usize,
56    components: RVec<ComponentInfo>,
57    ids: RVec<StableTypeId>,
58}
59
60impl CommandBuffer {
61    /// Create an empty command buffer
62    pub fn new() -> Self {
63        Self::default()
64    }
65
66    unsafe fn grow(min_size: usize, cursor: usize, align: usize, storage: NonNull<u8>) -> (NonNull<u8>, Layout) {
67        let layout = Layout::from_size_align(min_size.next_power_of_two().max(64), align).unwrap();
68        let core_layout = core::alloc::Layout::from_size_align(layout.size(), layout.align()).unwrap();
69        let new_storage = NonNull::new_unchecked(alloc(core_layout));
70        ptr::copy_nonoverlapping(storage.as_ptr(), new_storage.as_ptr(), cursor);
71        (new_storage, layout)
72    }
73
74    unsafe fn add_inner(&mut self, ptr: *mut u8, ty: TypeInfo) {
75        let offset = align(self.cursor, ty.layout().align());
76        let end = offset + ty.layout().size();
77
78        let core_layout = core::alloc::Layout::from_size_align(self.layout.size(), self.layout.align()).unwrap();
79
80        if end > self.layout.size() || ty.layout().align() > self.layout.align() {
81            let new_align = self.layout.align().max(ty.layout().align());
82            let (new_storage, new_layout) = Self::grow(end, self.cursor, new_align, self.storage);
83            if self.layout.size() != 0 {
84                dealloc(self.storage.as_ptr(), core_layout);
85            }
86            self.storage = new_storage;
87            self.layout = new_layout;
88        }
89
90        let addr = self.storage.as_ptr().add(offset);
91        ptr::copy_nonoverlapping(ptr, addr, ty.layout().size());
92        self.components.push(ComponentInfo { ty, offset });
93        self.cursor = end;
94    }
95
96    /// Add components from `bundle` to `entity`, if it exists
97    ///
98    /// Pairs well with [`World::reserve_entity`] to spawn entities with a known
99    /// handle.
100    ///
101    /// When inserting a single component, see [`insert_one`](Self::insert_one)
102    /// for convenience.
103    pub fn insert(&mut self, entity: Entity, components: impl DynamicBundle) {
104        let first_component = self.components.len();
105        unsafe {
106            components.put(|ptr, ty| self.add_inner(ptr, ty));
107        }
108        self.components[first_component..].sort_unstable_by_key(|c| c.ty);
109        self.cmds.push(Cmd::SpawnOrInsert(EntityIndex {
110            entity: RSome(entity),
111            // components: first_component..self.components.len(),
112            component_range_start: first_component,
113            component_range_end: self.components.len(),
114        }));
115    }
116
117    /// Add `component` to `entity`, if the entity exists
118    ///
119    /// See [`insert`](Self::insert).
120    pub fn insert_one(&mut self, entity: Entity, component: impl Component) {
121        self.insert(entity, (component,));
122    }
123
124    /// Remove components from `entity` if they exist
125    ///
126    /// When removing a single component, see [`remove_one`](Self::remove_one)
127    /// for convenience.
128    pub fn remove<T: Bundle + 'static>(&mut self, ent: Entity) {
129        #[allow(clippy::semicolon_if_nothing_returned)]
130        #[cfg_attr(not(target_arch = "wasm32"), sabi_extern_fn)] //adds extern "C" to it if it's not compiled on wasm
131        fn remove_bundle_and_ignore_result<T: Bundle + 'static>(world: &mut World, ents: Entity) {
132            let _ = world.remove::<T>(ents);
133        }
134        self.cmds.push(Cmd::Remove(RemovedComps {
135            remove: remove_bundle_and_ignore_result::<T>,
136            entity: ent,
137        }));
138    }
139
140    /// Remove a component from `entity` if it exists
141    ///
142    /// See [`remove`](Self::remove).
143    pub fn remove_one<T: Component>(&mut self, ent: Entity) {
144        self.remove::<(T,)>(ent);
145    }
146
147    /// Despawn `entity` from World
148    pub fn despawn(&mut self, entity: Entity) {
149        self.cmds.push(Cmd::Despawn(entity));
150    }
151
152    /// Spawn a new entity with `components`
153    ///
154    /// If the [`Entity`] is needed immediately, consider combining
155    /// [`World::reserve_entity`] with [`insert`](CommandBuffer::insert)
156    /// instead.
157    pub fn spawn(&mut self, components: impl DynamicBundle) {
158        let first_component = self.components.len();
159        unsafe {
160            components.put(|ptr, ty| self.add_inner(ptr, ty));
161        }
162        self.components[first_component..].sort_unstable_by_key(|c| c.ty);
163        self.cmds.push(Cmd::SpawnOrInsert(EntityIndex {
164            entity: RNone,
165            // components: first_component..self.components.len(),
166            component_range_start: first_component,
167            component_range_end: self.components.len(),
168        }));
169    }
170
171    /// Run recorded commands on `world`, clearing the command buffer
172    pub fn run_on(&mut self, world: &mut World) {
173        for i in 0..self.cmds.len() {
174            match mem::replace(&mut self.cmds[i], Cmd::Despawn(Entity::DANGLING)) {
175                Cmd::SpawnOrInsert(entity) => {
176                    let components_range = std::ops::Range {
177                        start: entity.component_range_start,
178                        end: entity.component_range_end,
179                    };
180                    let components = self.build(components_range);
181                    match entity.entity {
182                        RSome(entity) => {
183                            // If `entity` no longer exists, quietly drop the components.
184                            let _ = world.insert(entity, components);
185                        }
186                        RNone => {
187                            world.spawn(components);
188                        }
189                    }
190                }
191                Cmd::Remove(remove) => {
192                    (remove.remove)(world, remove.entity);
193                }
194                Cmd::Despawn(entity) => {
195                    let _ = world.despawn(entity);
196                }
197            }
198        }
199        // Wipe out component references so `clear` doesn't try to double-free
200        self.components.clear();
201
202        self.clear();
203    }
204
205    fn build(&mut self, components: Range<usize>) -> RecordedEntity<'_> {
206        self.ids.clear();
207        self.ids.extend(self.components[components.clone()].iter().map(|x| x.ty.id()));
208        RecordedEntity { cmd: self, components }
209    }
210
211    /// Drop all recorded commands
212    pub fn clear(&mut self) {
213        self.ids.clear();
214        self.cursor = 0;
215        for info in self.components.drain(..) {
216            unsafe {
217                info.ty.drop(self.storage.as_ptr().add(info.offset));
218            }
219        }
220        self.cmds.clear();
221    }
222
223    /// Number of commands
224    pub fn len(&mut self) -> usize {
225        self.cmds.len()
226    }
227
228    /// check if there were any commands recorded
229    pub fn is_empty(&mut self) -> bool {
230        self.cmds.is_empty()
231    }
232}
233
234unsafe impl Send for CommandBuffer {}
235unsafe impl Sync for CommandBuffer {}
236
237impl Drop for CommandBuffer {
238    fn drop(&mut self) {
239        let core_layout = core::alloc::Layout::from_size_align(self.layout.size(), self.layout.align()).unwrap();
240        self.clear();
241        if self.layout.size() != 0 {
242            unsafe {
243                dealloc(self.storage.as_ptr(), core_layout);
244            }
245        }
246    }
247}
248
249impl Default for CommandBuffer {
250    /// Create an empty buffer
251    fn default() -> Self {
252        Self {
253            cmds: RVec::new(),
254            storage: NonNull::dangling(),
255            layout: Layout::from_size_align(0, 8).unwrap(),
256            cursor: 0,
257            components: RVec::new(),
258            ids: RVec::new(),
259        }
260    }
261}
262
263/// The output of an `[CommandBuffer]` suitable for passing to
264/// [`World::spawn_into`](crate::World::spawn_into)
265struct RecordedEntity<'a> {
266    cmd: &'a mut CommandBuffer,
267    components: Range<usize>,
268}
269
270unsafe impl DynamicBundle for RecordedEntity<'_> {
271    fn with_ids<T>(&self, f: impl FnOnce(&[StableTypeId]) -> T) -> T {
272        f(&self.cmd.ids)
273    }
274
275    fn type_info(&self) -> Vec<TypeInfo> {
276        self.cmd.components[self.components.clone()].iter().map(|x| x.ty).collect()
277    }
278
279    unsafe fn put(mut self, mut f: impl FnMut(*mut u8, TypeInfo)) {
280        // Zero out the components slice so `drop` won't double-free
281        let components = mem::replace(&mut self.components, 0..0);
282        for info in &self.cmd.components[components] {
283            let ptr = self.cmd.storage.as_ptr().add(info.offset);
284            f(ptr, info.ty);
285        }
286    }
287}
288
289impl Drop for RecordedEntity<'_> {
290    fn drop(&mut self) {
291        // If `put` was never called, we still need to drop this entity's components and
292        // discard their info.
293        unsafe {
294            for info in &self.cmd.components[self.components.clone()] {
295                info.ty.drop(self.cmd.storage.as_ptr().add(info.offset));
296            }
297        }
298    }
299}
300
301/// Data required to store components and their offset  
302#[repr(C)]
303#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
304struct ComponentInfo {
305    ty: TypeInfo,
306    // Position in 'storage'
307    offset: usize,
308}
309
310/// Data of buffered 'entity' and its relative position in component data
311#[repr(C)]
312#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
313struct EntityIndex {
314    entity: ROption<Entity>,
315    // Position of this entity's components in `CommandBuffer::info`
316    //
317    // We could store a single start point for the first initialized entity, rather than one for
318    // each, but this would be more error prone for marginal space savings.
319    // components: Range<usize>,
320    component_range_start: usize,
321    component_range_end: usize,
322}
323
324/// Data required to remove components from 'entity'
325#[repr(C)]
326#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
327struct RemovedComps {
328    #[cfg(target_arch = "wasm32")] //if it's wasn we don't require StableABI so we don't need extern C which causes issues when compiling for wasm
329    remove: fn(&mut World, Entity),
330    #[cfg(not(target_arch = "wasm32"))]
331    remove: extern "C" fn(&mut World, Entity),
332    entity: Entity,
333}
334
335/// A buffered command
336#[repr(C)]
337#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
338enum Cmd {
339    SpawnOrInsert(EntityIndex),
340    Remove(RemovedComps),
341    Despawn(Entity),
342}
343
344#[cfg(test)]
345mod tests {
346    use super::*;
347
348    #[test]
349    #[allow(clippy::similar_names)]
350    fn populate_archetypes() {
351        let mut world = World::new();
352        let mut buffer = CommandBuffer::new();
353        let ent = world.reserve_entity();
354        let enta = world.reserve_entity();
355        let entb = world.reserve_entity();
356        let entc = world.reserve_entity();
357        buffer.insert(ent, (true, "a"));
358        buffer.insert(entc, (true, "a"));
359        buffer.insert(enta, (1, 1.0));
360        buffer.insert(entb, (1.0, "a"));
361        buffer.run_on(&mut world);
362        assert_eq!(world.archetypes().len(), 4);
363    }
364
365    #[test]
366    fn failed_insert_regression() {
367        // Verify that failing to insert components doesn't lead to concatenating
368        // components together
369        #[derive(Clone)]
370        struct A;
371
372        let mut world = World::new();
373
374        // Get two IDs
375        let a = world.spawn((A,));
376        let b = world.spawn((A,));
377
378        // Invalidate them both
379        world.clear();
380
381        let mut cmd = CommandBuffer::new();
382        cmd.insert_one(a, A);
383        cmd.insert_one(b, A);
384
385        // Make `a` valid again
386        world.spawn_at(a, ());
387
388        // The insert to `a` should succeed
389        cmd.run_on(&mut world);
390
391        assert!(world.satisfies::<&A>(a).unwrap());
392    }
393
394    #[test]
395    fn insert_then_remove() {
396        let mut world = World::new();
397        let a = world.spawn(());
398        let mut cmd = CommandBuffer::new();
399        cmd.insert_one(a, 42i32);
400        cmd.remove_one::<i32>(a);
401        cmd.run_on(&mut world);
402        assert!(!world.satisfies::<&i32>(a).unwrap());
403    }
404
405    #[test]
406    fn remove_then_insert() {
407        let mut world = World::new();
408        let a = world.spawn((17i32,));
409        let mut cmd = CommandBuffer::new();
410        cmd.remove_one::<i32>(a);
411        cmd.insert_one(a, 42i32);
412        cmd.run_on(&mut world);
413        assert_eq!(*world.get::<&i32>(a).unwrap(), 42);
414    }
415}