use core::any::TypeId;
use core::ptr::{self, NonNull};
use crate::alloc::alloc::{alloc, dealloc, Layout};
use crate::alloc::vec::Vec;
use crate::archetype::TypeInfo;
use crate::Entity;
use crate::World;
use crate::{align, DynamicBundle};
pub struct CommandBuffer {
entities: Vec<EntityIndex>,
storage: NonNull<u8>,
layout: Layout,
cursor: usize,
components: Vec<ComponentInfo>,
ids: Vec<TypeId>,
}
impl CommandBuffer {
pub fn new() -> Self {
Self::default()
}
unsafe fn grow(
min_size: usize,
cursor: usize,
align: usize,
storage: NonNull<u8>,
) -> (NonNull<u8>, Layout) {
let layout = Layout::from_size_align(min_size.next_power_of_two().max(64), align).unwrap();
let new_storage = NonNull::new_unchecked(alloc(layout));
ptr::copy_nonoverlapping(storage.as_ptr(), new_storage.as_ptr(), cursor);
(new_storage, layout)
}
unsafe fn add_inner(&mut self, ptr: *mut u8, ty: TypeInfo) {
let offset = align(self.cursor, ty.layout().align());
let end = offset + ty.layout().size();
if end > self.layout.size() || ty.layout().align() > self.layout.align() {
let new_align = self.layout.align().max(ty.layout().align());
let (new_storage, new_layout) = Self::grow(end, self.cursor, new_align, self.storage);
if self.layout.size() != 0 {
dealloc(self.storage.as_ptr(), self.layout);
}
self.storage = new_storage;
self.layout = new_layout;
}
let addr = self.storage.as_ptr().add(offset);
ptr::copy_nonoverlapping(ptr, addr, ty.layout().size());
self.components.push(ComponentInfo { ty, offset });
self.cursor = end;
}
pub fn insert(&mut self, entity: Entity, components: impl DynamicBundle) {
let first_component = self.components.len();
unsafe {
components.put(|ptr, ty| self.add_inner(ptr, ty));
}
self.entities.push(EntityIndex {
entity: Some(entity),
first_component,
});
}
pub fn spawn(&mut self, components: impl DynamicBundle) {
let first_component = self.components.len();
unsafe {
components.put(|ptr, ty| self.add_inner(ptr, ty));
}
self.entities.push(EntityIndex {
entity: None,
first_component,
});
}
pub fn run_on(&mut self, world: &mut World) {
let mut end = self.components.len();
for entity in self.entities.iter().rev() {
self.components[entity.first_component..end].sort_unstable_by_key(|z| z.ty);
end = entity.first_component;
}
for index in (0..self.entities.len()).rev() {
let (entity, components) = self.build(index);
match entity {
Some(entity) => {
let _ = world.insert(entity, components);
}
None => {
world.spawn(components);
}
}
}
self.clear();
}
fn build(&mut self, index: usize) -> (Option<Entity>, RecordedEntity<'_>) {
self.ids.clear();
self.ids.extend(
self.components[self.entities[index].first_component..]
.iter()
.map(|x| x.ty.id()),
);
let entity = self.entities[index].entity;
(entity, RecordedEntity { cmd: self, index })
}
pub fn clear(&mut self) {
self.ids.clear();
self.entities.clear();
self.cursor = 0;
unsafe {
for info in self.components.drain(..) {
info.ty.drop(self.storage.as_ptr().add(info.offset));
}
}
}
}
unsafe impl Send for CommandBuffer {}
unsafe impl Sync for CommandBuffer {}
impl Drop for CommandBuffer {
fn drop(&mut self) {
self.clear();
if self.layout.size() != 0 {
unsafe {
dealloc(self.storage.as_ptr(), self.layout);
}
}
}
}
impl Default for CommandBuffer {
fn default() -> Self {
Self {
entities: Vec::new(),
storage: NonNull::dangling(),
layout: Layout::from_size_align(0, 8).unwrap(),
cursor: 0,
components: Vec::new(),
ids: Vec::new(),
}
}
}
struct RecordedEntity<'a> {
cmd: &'a mut CommandBuffer,
index: usize,
}
unsafe impl DynamicBundle for RecordedEntity<'_> {
fn with_ids<T>(&self, f: impl FnOnce(&[TypeId]) -> T) -> T {
f(&self.cmd.ids)
}
fn type_info(&self) -> Vec<TypeInfo> {
self.cmd.components[self.cmd.entities[self.index].first_component..]
.iter()
.map(|x| x.ty)
.collect()
}
unsafe fn put(self, mut f: impl FnMut(*mut u8, TypeInfo)) {
for info in self
.cmd
.components
.drain(self.cmd.entities[self.index].first_component..)
{
let ptr = self.cmd.storage.as_ptr().add(info.offset);
f(ptr, info.ty);
}
}
}
struct ComponentInfo {
ty: TypeInfo,
offset: usize,
}
struct EntityIndex {
entity: Option<Entity>,
first_component: usize,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn populate_archetypes() {
let mut world = World::new();
let mut buffer = CommandBuffer::new();
let ent = world.reserve_entity();
let enta = world.reserve_entity();
let entb = world.reserve_entity();
let entc = world.reserve_entity();
buffer.insert(ent, (true, "a"));
buffer.insert(entc, (true, "a"));
buffer.insert(enta, (1, 1.0));
buffer.insert(entb, (1.0, "a"));
buffer.run_on(&mut world);
assert_eq!(world.archetypes().len(), 4);
}
}