1use crate::stabletypeid::StableTypeId;
10use core::{
11 mem,
12 ops::Range,
13 ptr::{self, NonNull},
14};
15
16use 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#[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 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 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 component_range_start: first_component,
113 component_range_end: self.components.len(),
114 }));
115 }
116
117 pub fn insert_one(&mut self, entity: Entity, component: impl Component) {
121 self.insert(entity, (component,));
122 }
123
124 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)] 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 pub fn remove_one<T: Component>(&mut self, ent: Entity) {
144 self.remove::<(T,)>(ent);
145 }
146
147 pub fn despawn(&mut self, entity: Entity) {
149 self.cmds.push(Cmd::Despawn(entity));
150 }
151
152 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 component_range_start: first_component,
167 component_range_end: self.components.len(),
168 }));
169 }
170
171 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 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 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 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 pub fn len(&mut self) -> usize {
225 self.cmds.len()
226 }
227
228 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 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
263struct 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 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 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#[repr(C)]
303#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
304struct ComponentInfo {
305 ty: TypeInfo,
306 offset: usize,
308}
309
310#[repr(C)]
312#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
313struct EntityIndex {
314 entity: ROption<Entity>,
315 component_range_start: usize,
321 component_range_end: usize,
322}
323
324#[repr(C)]
326#[cfg_attr(not(target_arch = "wasm32"), derive(StableAbi))]
327struct RemovedComps {
328 #[cfg(target_arch = "wasm32")] 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#[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 #[derive(Clone)]
370 struct A;
371
372 let mut world = World::new();
373
374 let a = world.spawn((A,));
376 let b = world.spawn((A,));
377
378 world.clear();
380
381 let mut cmd = CommandBuffer::new();
382 cmd.insert_one(a, A);
383 cmd.insert_one(b, A);
384
385 world.spawn_at(a, ());
387
388 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}