bevy/bevy_legion/src/command.rs
2020-01-20 20:10:40 -08:00

700 lines
25 KiB
Rust

use crate::{
borrow::AtomicRefCell,
cons::{ConsAppend, ConsFlatten},
entity::{Entity, EntityAllocator},
filter::{ChunksetFilterData, Filter},
storage::{Component, ComponentTypeId, Tag, TagTypeId},
world::{ComponentSource, ComponentTupleSet, IntoComponentSource, TagLayout, TagSet, World},
};
use derivative::Derivative;
use smallvec::SmallVec;
use std::{collections::VecDeque, iter::FromIterator, marker::PhantomData, sync::Arc};
/// This trait can be used to implement custom world writer types that can be directly
/// inserted into the command buffer, for more custom and complex world operations. This is analogous
/// to the `CommandBuffer::exec_mut` function type, but does not perform explicit any/any archetype
/// access.
pub trait WorldWritable {
/// Destructs the writer and performs the write operations on the world.
fn write(self: Arc<Self>, world: &mut World);
/// Returns the list of `ComponentTypeId` which are written by this command buffer. This is leveraged
/// to allow parralel command buffer flushing.
fn write_components(&self) -> Vec<ComponentTypeId>;
/// Returns the list of `TagTypeId` which are written by this command buffer. This is leveraged
/// to allow parralel command buffer flushing.
fn write_tags(&self) -> Vec<TagTypeId>;
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct InsertBufferedCommand<T, C> {
write_components: Vec<ComponentTypeId>,
write_tags: Vec<TagTypeId>,
#[derivative(Debug = "ignore")]
tags: T,
#[derivative(Debug = "ignore")]
components: C,
entities: Vec<Entity>,
}
impl<T, C> WorldWritable for InsertBufferedCommand<T, C>
where
T: TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
C: ComponentSource,
{
fn write(self: Arc<Self>, world: &mut World) {
let consumed = Arc::try_unwrap(self).unwrap();
world.insert_buffered(&consumed.entities, consumed.tags, consumed.components);
}
fn write_components(&self) -> Vec<ComponentTypeId> { self.write_components.clone() }
fn write_tags(&self) -> Vec<TagTypeId> { self.write_tags.clone() }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct InsertCommand<T, C> {
write_components: Vec<ComponentTypeId>,
write_tags: Vec<TagTypeId>,
#[derivative(Debug = "ignore")]
tags: T,
#[derivative(Debug = "ignore")]
components: C,
}
impl<T, C> WorldWritable for InsertCommand<T, C>
where
T: TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
C: IntoComponentSource,
{
fn write(self: Arc<Self>, world: &mut World) {
let consumed = Arc::try_unwrap(self).unwrap();
world.insert(consumed.tags, consumed.components);
}
fn write_components(&self) -> Vec<ComponentTypeId> { self.write_components.clone() }
fn write_tags(&self) -> Vec<TagTypeId> { self.write_tags.clone() }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct DeleteEntityCommand(Entity);
impl WorldWritable for DeleteEntityCommand {
fn write(self: Arc<Self>, world: &mut World) { world.delete(self.0); }
fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct AddTagCommand<T> {
entity: Entity,
#[derivative(Debug = "ignore")]
tag: T,
}
impl<T> WorldWritable for AddTagCommand<T>
where
T: Tag,
{
fn write(self: Arc<Self>, world: &mut World) {
let consumed = Arc::try_unwrap(self).unwrap();
world.add_tag(consumed.entity, consumed.tag)
}
fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
fn write_tags(&self) -> Vec<TagTypeId> { vec![TagTypeId::of::<T>()] }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct RemoveTagCommand<T> {
entity: Entity,
_marker: PhantomData<T>,
}
impl<T> WorldWritable for RemoveTagCommand<T>
where
T: Tag,
{
fn write(self: Arc<Self>, world: &mut World) { world.remove_tag::<T>(self.entity) }
fn write_components(&self) -> Vec<ComponentTypeId> { Vec::with_capacity(0) }
fn write_tags(&self) -> Vec<TagTypeId> { vec![TagTypeId::of::<T>()] }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct AddComponentCommand<C> {
#[derivative(Debug = "ignore")]
entity: Entity,
#[derivative(Debug = "ignore")]
component: C,
}
impl<C> WorldWritable for AddComponentCommand<C>
where
C: Component,
{
fn write(self: Arc<Self>, world: &mut World) {
let consumed = Arc::try_unwrap(self).unwrap();
world
.add_component::<C>(consumed.entity, consumed.component)
.unwrap();
}
fn write_components(&self) -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<C>()] }
fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
}
#[derive(Derivative)]
#[derivative(Debug(bound = ""))]
struct RemoveComponentCommand<C> {
entity: Entity,
_marker: PhantomData<C>,
}
impl<C> WorldWritable for RemoveComponentCommand<C>
where
C: Component,
{
fn write(self: Arc<Self>, world: &mut World) { world.remove_component::<C>(self.entity) }
fn write_components(&self) -> Vec<ComponentTypeId> { vec![ComponentTypeId::of::<C>()] }
fn write_tags(&self) -> Vec<TagTypeId> { Vec::with_capacity(0) }
}
#[allow(clippy::enum_variant_names)]
enum EntityCommand {
WriteWorld(Arc<dyn WorldWritable>),
ExecWorld(Arc<dyn Fn(&World)>),
ExecMutWorld(Arc<dyn Fn(&mut World)>),
}
/// A builder type which can be retrieved from the command buffer. This is the ideal use case for
/// inserted complex entities with multiple components and tags from a command buffer. Although
/// `add_component` will perform a new move operation on every addition, this allows the construction
/// of a single `insert` command for an entity, but without using the actual `insert` command
/// provided by the `CommandBuffer`
///
/// # Examples
///
/// Inserting an entity using the `EntityBuilder`:
///
/// ```
/// # use legion::prelude::*;
/// # #[derive(Copy, Clone, Debug, PartialEq)]
/// # struct Position(f32);
/// # #[derive(Copy, Clone, Debug, PartialEq)]
/// # struct Rotation(f32);
/// # let universe = Universe::new();
/// # let mut world = universe.create_world();
/// let mut command_buffer = CommandBuffer::from_world(&mut world);
/// command_buffer.build_entity().unwrap()
/// .with_component(Position(123.0))
/// .with_component(Rotation(456.0)).build(&mut command_buffer);
/// command_buffer.write(&mut world);
/// ```
pub struct EntityBuilder<TS = (), CS = ()> {
entity: Entity,
tags: TS,
components: CS,
}
impl<TS, CS> EntityBuilder<TS, CS>
where
TS: 'static + Send + ConsFlatten,
CS: 'static + Send + ConsFlatten,
{
/// Adds a component to this builder, returning a new builder type containing that component type
/// and its data.
pub fn with_component<C: Component>(
self,
component: C,
) -> EntityBuilder<TS, <CS as ConsAppend<C>>::Output>
where
CS: ConsAppend<C>,
<CS as ConsAppend<C>>::Output: ConsFlatten,
{
EntityBuilder {
components: ConsAppend::append(self.components, component),
entity: self.entity,
tags: self.tags,
}
}
/// Adds a tag to this builder, returning a new builder type containing that component type
/// and its data.
pub fn with_tag<T: Tag>(self, tag: T) -> EntityBuilder<<TS as ConsAppend<T>>::Output, CS>
where
TS: ConsAppend<T>,
<TS as ConsAppend<T>>::Output: ConsFlatten,
{
EntityBuilder {
tags: ConsAppend::append(self.tags, tag),
entity: self.entity,
components: self.components,
}
}
/// Finalizes this builder type and submits it to the `CommandBuffer` as a `WorldWritable` trait
/// object.
pub fn build(self, buffer: &mut CommandBuffer)
where
<TS as ConsFlatten>::Output: TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
ComponentTupleSet<
<CS as ConsFlatten>::Output,
std::iter::Once<<CS as ConsFlatten>::Output>,
>: ComponentSource,
{
buffer
.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(InsertBufferedCommand {
write_components: Vec::default(),
write_tags: Vec::default(),
tags: self.tags.flatten(),
components: IntoComponentSource::into(std::iter::once(self.components.flatten())),
entities: vec![self.entity],
})));
}
}
/// Errors returned by the `CommandBuffer`
#[derive(Debug)]
pub enum CommandError {
/// The command buffers entity cache has been exhausted. This is defaulted to 64 at `World::DEFAULT_COMMAND_BUFFER_SIZE`.
/// This upper limit can be changed via `SystemBuilder::with_command_buffer_size` for specific systems,
/// or globally via `World::set_command_buffer_size`.
EntityBlockFull,
}
impl std::fmt::Display for CommandError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "CommandError") }
}
impl std::error::Error for CommandError {
fn cause(&self) -> Option<&dyn std::error::Error> { None }
}
/// A command buffer used to queue mutable changes to the world from a system. This buffer is automatically
/// flushed and refreshed at the beginning of every frame by `Schedule`. If `Schedule` is not used,
/// then the user needs to manually flush it by performing `CommandBuffer::write`.
///
/// This buffer operates as follows:
/// - All commands are queued as trait object of type `WorldWritable`, to be executed when `CommandBuffer:write` is called.
/// - Entities are allocated at the time of `CommandBuffer:write` occuring, being directly allocated from the world
/// and cached internally in the system. This upper cache size can be changed via `SystemBuilder::with_command_buffer_size`
/// for specific systems, or globally via `World::set_command_buffer_size`. In the event the cached entity count is exceeded,
/// the cache will be refilled on demand from the world `EntityAllocator`.
///
/// This behavior exists because `EntityAllocator` is a shared lock within the world, so in order to reduce lock contention with many
/// systems running and adding entities, the `CommandBuffer` will cache the configured number of entities - reducing contention.
///
/// # Examples
///
/// Inserting an entity using the `CommandBuffer`:
///
/// ```
/// # use legion::prelude::*;
/// # #[derive(Copy, Clone, Debug, PartialEq)]
/// # struct Position(f32);
/// # #[derive(Copy, Clone, Debug, PartialEq)]
/// # struct Rotation(f32);
/// # let universe = Universe::new();
/// # let mut world = universe.create_world();
/// let mut command_buffer = CommandBuffer::from_world(&mut world);
/// let entity = command_buffer.create_entity().unwrap();
///
/// command_buffer.add_component(entity, Position(123.0));
/// command_buffer.delete(entity);
///
/// command_buffer.write(&mut world);
/// ```
#[derive(Default)]
pub struct CommandBuffer {
commands: AtomicRefCell<VecDeque<EntityCommand>>,
entity_allocator: Option<Arc<EntityAllocator>>,
pub(crate) custom_capacity: Option<usize>,
pub(crate) free_list: SmallVec<[Entity; 64]>,
pub(crate) used_list: SmallVec<[Entity; 64]>,
}
// This is safe because only 1 system in 1 execution is only ever accessing a command buffer
// and we garuntee the write operations of a command buffer occur in a safe manner
unsafe impl Send for CommandBuffer {}
unsafe impl Sync for CommandBuffer {}
impl CommandBuffer {
/// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame.
/// Allocating a command buffer in this manner will overwrite `World::set_command_buffer_size` and
/// this system will always allocate the custom provide capacity of entities every frame.
///
/// # Notes
/// This function does not perform any actual entity preallocation. `ComamandBuffer:resize` or `CommandBuffer:write`
/// must be called before using the command buffer for the first time to make entities available.
pub fn with_capacity(capacity: usize) -> Self {
// Pull free entities from the world.
Self {
custom_capacity: Some(capacity),
free_list: SmallVec::with_capacity(capacity),
commands: Default::default(),
used_list: SmallVec::with_capacity(capacity),
entity_allocator: None,
}
}
/// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame.
/// Allocating a command buffer in this manner will overwrite `World::set_command_buffer_size` and
/// this system will always allocate the custom provide capacity of entities every frame.
///
/// This constructor will preallocate the first round of entities needed from the world.
pub fn from_world_with_capacity(world: &mut World, capacity: usize) -> Self {
// Pull free entities from the world.
let free_list =
SmallVec::from_iter((0..capacity).map(|_| world.entity_allocator.create_entity()));
Self {
free_list,
custom_capacity: Some(capacity),
commands: Default::default(),
used_list: SmallVec::with_capacity(capacity),
entity_allocator: Some(world.entity_allocator.clone()),
}
}
/// Creates a `CommandBuffer` with a custom capacity of cached Entity's to be collected every frame.
/// Allocating a command buffer in this manner will use the default `World::set_command_buffer_size`
/// value.
///
/// This constructor will preallocate the first round of entities needed from the world.
pub fn from_world(world: &mut World) -> Self {
// Pull free entities from the world.
let free_list = SmallVec::from_iter(
(0..world.command_buffer_size()).map(|_| world.entity_allocator.create_entity()),
);
Self {
free_list,
custom_capacity: None,
commands: Default::default(),
used_list: SmallVec::with_capacity(world.command_buffer_size()),
entity_allocator: Some(world.entity_allocator.clone()),
}
}
/// Changes the cached capacity of this `CommandBuffer` to the specified capacity. This includes shrinking
/// and growing the allocated entities, and possibly returning them to the entity allocator in the
/// case of a shrink.
///
/// This function does *NOT* set the `CommandBuffer::custom_capacity` override.
#[allow(clippy::comparison_chain)]
pub fn resize(&mut self, capacity: usize) {
let allocator = &self.entity_allocator;
let free_list = &mut self.free_list;
if let Some(allocator) = allocator.as_ref() {
if free_list.len() < capacity {
(free_list.len()..capacity).for_each(|_| free_list.push(allocator.create_entity()));
} else if free_list.len() > capacity {
// Free the entities
(free_list.len() - capacity..capacity).for_each(|_| {
allocator.delete_entity(free_list.pop().unwrap());
});
}
} else {
panic!("Entity allocator not assigned to command buffer")
}
}
/// Flushes this command buffer, draining all stored commands and writing them to the world.
///
/// Command flushes are performed in a FIFO manner, allowing for reliable, linear commands being
/// executed in the order they were provided.
///
/// This function also calls `CommandBuffer:resize`, performing any appropriate entity preallocation,
/// refilling the entity cache of any consumed entities.
pub fn write(&mut self, world: &mut World) {
tracing::trace!("Draining command buffer");
if self.entity_allocator.is_none() {
self.entity_allocator = Some(world.entity_allocator.clone());
}
let empty = Vec::from_iter((0..self.used_list.len()).map(|_| ()));
world.insert_buffered(
self.used_list.as_slice(),
(),
IntoComponentSource::into(empty),
);
self.used_list.clear();
while let Some(command) = self.commands.get_mut().pop_back() {
match command {
EntityCommand::WriteWorld(ptr) => ptr.write(world),
EntityCommand::ExecMutWorld(closure) => closure(world),
EntityCommand::ExecWorld(closure) => closure(world),
}
}
// Refill our entity buffer from the world
if let Some(custom_capacity) = self.custom_capacity {
self.resize(custom_capacity);
} else {
self.resize(world.command_buffer_size());
}
}
/// Consumed an internally cached entity, returning an `EntityBuilder` using that entity.
pub fn build_entity(&mut self) -> Result<EntityBuilder<(), ()>, CommandError> {
let entity = self.create_entity()?;
Ok(EntityBuilder {
entity,
tags: (),
components: (),
})
}
/// Consumed an internally cached entity, or returns `CommandError`
pub fn create_entity(&mut self) -> Result<Entity, CommandError> {
if self.free_list.is_empty() {
self.resize(
self.custom_capacity
.unwrap_or(World::DEFAULT_COMMAND_BUFFER_SIZE),
);
}
let entity = self.free_list.pop().ok_or(CommandError::EntityBlockFull)?;
self.used_list.push(entity);
Ok(entity)
}
/// Executes an arbitrary closure against the mutable world, allowing for queued exclusive
/// access to the world.
pub fn exec_mut<F>(&self, f: F)
where
F: 'static + Fn(&mut World),
{
self.commands
.get_mut()
.push_front(EntityCommand::ExecMutWorld(Arc::new(f)));
}
/// Inserts an arbitrary implementor of the `WorldWritable` trait into the command queue.
/// This can be leveraged for creating custom `WorldWritable` trait implementors, and is used
/// internally for the default writers.
pub fn insert_writer<W>(&self, writer: W)
where
W: 'static + WorldWritable,
{
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(writer)));
}
/// Queues an *unbuffered* insertion into the world. This command follows the same syntax as
/// the normal `World::insert`, except for one caviate - entities are NOT returned by this
/// function, meaning that the internal entity cache and limits of this `CommandBuffer` are not
/// applicable to this function.
///
/// This function can be considered a "fire and forget" entity creation method which is not bound
/// by the standard command buffer size limits of the other entity insertion functions. This allows
/// for mass insertion of entities, exceeding the command buffer sizes, to occur in scenarios that
/// the entities do not need to be retrieved.
pub fn insert_unbuffered<T, C>(&mut self, tags: T, components: C)
where
T: 'static + TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
C: 'static + IntoComponentSource,
{
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(InsertCommand {
write_components: Vec::default(),
write_tags: Vec::default(),
tags,
components,
})));
}
/// Queues an insertion into the world. This command follows the same syntax as
/// the normal `World::insert`, returning the entities created for this command.
pub fn insert<T, C>(&mut self, tags: T, components: C) -> Result<Vec<Entity>, CommandError>
where
T: 'static + TagSet + TagLayout + for<'a> Filter<ChunksetFilterData<'a>>,
C: 'static + IntoComponentSource,
{
let components = components.into();
if components.len() > self.free_list.len() {
return Err(CommandError::EntityBlockFull);
}
let mut entities = Vec::with_capacity(components.len());
for _ in 0..components.len() {
entities.push(self.free_list.pop().ok_or(CommandError::EntityBlockFull)?);
}
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(InsertBufferedCommand {
write_components: Vec::default(),
write_tags: Vec::default(),
tags,
components,
entities: entities.clone(),
})));
Ok(entities)
}
/// Queues the deletion of an entity in the command buffer. This writer calls `World::delete`
pub fn delete(&self, entity: Entity) {
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(DeleteEntityCommand(
entity,
))));
}
/// Queues the addition of a component from an entity in the command buffer.
/// This writer calls `World::add_component`
pub fn add_component<C: Component>(&self, entity: Entity, component: C) {
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(AddComponentCommand {
entity,
component,
})));
}
/// Queues the removal of a component from an entity in the command buffer.
/// This writer calls `World::remove_component`
pub fn remove_component<C: Component>(&self, entity: Entity) {
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(
RemoveComponentCommand {
entity,
_marker: PhantomData::<C>::default(),
},
)));
}
/// Queues the addition of a tag from an entity in the command buffer.
/// This writer calls `World::add_tag`
pub fn add_tag<T: Tag>(&self, entity: Entity, tag: T) {
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(AddTagCommand {
entity,
tag,
})));
}
/// Queues the removal of a tag from an entity in the command buffer.
/// This writer calls `World::remove_tag`
pub fn remove_tag<T: Tag>(&self, entity: Entity) {
self.commands
.get_mut()
.push_front(EntityCommand::WriteWorld(Arc::new(RemoveTagCommand {
entity,
_marker: PhantomData::<T>::default(),
})));
}
/// Returns the current number of commands already queued in this `CommandBuffer` instance.
#[inline]
pub fn len(&self) -> usize { self.commands.get().len() }
/// Returns true if this `CommandBuffer` is currently empty and contains no writers.
#[inline]
pub fn is_empty(&self) -> bool { self.commands.get().len() == 0 }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
#[derive(Clone, Copy, Debug, PartialEq)]
struct Pos(f32, f32, f32);
#[derive(Clone, Copy, Debug, PartialEq)]
struct Vel(f32, f32, f32);
#[derive(Default)]
struct TestResource(pub i32);
#[test]
fn create_entity_test() -> Result<(), CommandError> {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let components_len = components.len();
//world.entity_allocator.get_block()
let mut command = CommandBuffer::from_world(&mut world);
let entity1 = command.create_entity()?;
let entity2 = command.create_entity()?;
command.add_component(entity1, Pos(1., 2., 3.));
command.add_component(entity2, Pos(4., 5., 6.));
command.write(&mut world);
let query = Read::<Pos>::query();
let mut count = 0;
for _ in query.iter_entities(&mut world) {
count += 1;
}
assert_eq!(components_len, count);
Ok(())
}
#[test]
fn simple_write_test() -> Result<(), CommandError> {
let _ = tracing_subscriber::fmt::try_init();
let universe = Universe::new();
let mut world = universe.create_world();
let components = vec![
(Pos(1., 2., 3.), Vel(0.1, 0.2, 0.3)),
(Pos(4., 5., 6.), Vel(0.4, 0.5, 0.6)),
];
let components_len = components.len();
//world.entity_allocator.get_block()
let mut command = CommandBuffer::from_world(&mut world);
let _ = command.insert((), components)?;
// Assert writing checks
// TODO:
//assert_eq!(
// vec![ComponentTypeId::of::<Pos>(), ComponentTypeId::of::<Vel>()],
// command.write_components()
//);
command.write(&mut world);
let query = Read::<Pos>::query();
let mut count = 0;
for _ in query.iter_entities_mut(&mut world) {
count += 1;
}
assert_eq!(components_len, count);
Ok(())
}
}