Add stable legion entity ids (GuidEntityAllocator)

This commit is contained in:
Carter Anderson 2020-05-20 10:39:52 -07:00
parent fb140ce4b0
commit 64ce5b42c0
6 changed files with 71 additions and 12 deletions

View File

@ -28,6 +28,7 @@ crossbeam-channel = "0.4.2"
derivative = "2.1"
smallvec = "1.4"
tracing = "0.1"
rand = "0.7.2"
metrics = { version = "0.12", optional = true }
serde = { version = "1", optional = true }
fxhash = "0.2"

View File

@ -1,7 +1,7 @@
use crate::{
borrow::AtomicRefCell,
cons::{ConsAppend, ConsFlatten},
entity::{Entity, EntityAllocator},
entity::{Entity, GuidEntityAllocator},
filter::{ChunksetFilterData, Filter},
storage::{Component, ComponentTypeId, Tag, TagTypeId},
world::{
@ -313,7 +313,7 @@ where
pub struct CommandBuffer {
world_id: WorldId,
commands: AtomicRefCell<VecDeque<EntityCommand>>,
entity_allocator: Arc<EntityAllocator>,
entity_allocator: Arc<GuidEntityAllocator>,
preallocated_capacity: usize,
free_list: SmallVec<[Entity; 64]>,
pending_insertion: SmallVec<[Entity; 64]>,

View File

@ -7,7 +7,7 @@ use std::fmt::Display;
use std::num::Wrapping;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
use std::{collections::HashSet, sync::Arc};
pub type EntityIndex = u32;
pub(crate) type EntityVersion = Wrapping<u32>;
@ -20,7 +20,7 @@ pub struct Entity {
}
impl Entity {
pub(crate) fn new(index: EntityIndex, version: EntityVersion) -> Entity {
pub fn new(index: EntityIndex, version: EntityVersion) -> Entity {
Entity { index, version }
}
@ -247,6 +247,64 @@ impl DerefMut for Blocks {
fn deref_mut(&mut self) -> &mut Self::Target { self.blocks.deref_mut() }
}
#[derive(Default, Debug, Clone)]
pub struct GuidEntityAllocator {
entities: Arc<RwLock<HashSet<Entity>>>,
next_ids: Arc<RwLock<Vec<Entity>>>,
}
impl GuidEntityAllocator {
pub fn is_alive(&self, entity: Entity) -> bool {
self.entities.read().contains(&entity)
}
pub fn push_next_ids(&self, ids: impl Iterator<Item = Entity>) {
self.next_ids.write().extend(ids);
}
/// Allocates a new unused `Entity` ID.
pub fn create_entity(&self) -> Entity {
if !self.next_ids.read().is_empty() {
return self.next_ids.write().pop().unwrap();
}
let entity = Entity::new(rand::random::<u32>(), Wrapping(1));
self.entities.write().insert(entity);
entity
}
/// Creates an iterator which allocates new `Entity` IDs.
pub fn create_entities(&self) -> GuidCreateEntityIter {
GuidCreateEntityIter {
allocator: self,
}
}
pub(crate) fn delete_entity(&self, entity: Entity) -> bool {
self.entities.write().remove(&entity)
}
pub(crate) fn delete_all_entities(&self) {
self.entities.write().clear();
}
pub(crate) fn merge(&self, other: GuidEntityAllocator) {
self.entities.write().extend(other.entities.write().drain())
}
}
pub struct GuidCreateEntityIter<'a> {
allocator: &'a GuidEntityAllocator
}
impl<'a> Iterator for GuidCreateEntityIter<'a> {
type Item = Entity;
fn next(&mut self) -> Option<Self::Item> {
Some(self.allocator.create_entity())
}
}
/// Manages the allocation and deletion of `Entity` IDs within a world.
#[derive(Debug)]
pub struct EntityAllocator {

View File

@ -1,5 +1,5 @@
use crate::{
entity::{Entity, EntityAllocator},
entity::{Entity, GuidEntityAllocator},
index::{ArchetypeIndex, ChunkIndex, SetIndex},
storage::{
ArchetypeData, ArchetypeDescription, Chunkset, ComponentMeta, ComponentTypeId, TagMeta,
@ -78,7 +78,7 @@ pub trait WorldDeserializer {
fn deserialize_entities<'de, D: Deserializer<'de>>(
&self,
deserializer: D,
entity_allocator: &EntityAllocator,
entity_allocator: &GuidEntityAllocator,
entities: &mut Vec<Entity>,
) -> Result<(), <D as Deserializer<'de>>::Error>;
}

View File

@ -2,9 +2,8 @@ use crate::borrow::Ref;
use crate::borrow::RefMut;
use crate::entity::BlockAllocator;
use crate::entity::Entity;
use crate::entity::EntityAllocator;
use crate::entity::EntityLocation;
use crate::entity::Locations;
use crate::entity::{GuidEntityAllocator, Locations};
use crate::event::Event;
use crate::filter::ArchetypeFilterData;
use crate::filter::ChunksetFilterData;
@ -75,7 +74,7 @@ impl Universe {
/// unique `Entity` IDs, even across worlds. See also `World::new`.
pub fn create_world(&self) -> World {
let id = WorldId::next(self.id.0);
let world = World::new_in_universe(id, EntityAllocator::new(self.allocator.clone()));
let world = World::new_in_universe(id, GuidEntityAllocator::default());
info!(universe = self.id.0, world = world.id().1, "Created world");
world
@ -99,7 +98,7 @@ impl WorldId {
pub struct World {
id: WorldId,
storage: UnsafeCell<Storage>,
pub(crate) entity_allocator: Arc<EntityAllocator>,
pub(crate) entity_allocator: Arc<GuidEntityAllocator>,
entity_locations: Locations,
defrag_progress: usize,
command_buffer_size: usize,
@ -120,11 +119,11 @@ impl World {
pub fn new() -> Self {
Self::new_in_universe(
WorldId::next(0),
EntityAllocator::new(Arc::new(Mutex::new(BlockAllocator::new()))),
GuidEntityAllocator::default(),
)
}
fn new_in_universe(id: WorldId, allocator: EntityAllocator) -> Self {
fn new_in_universe(id: WorldId, allocator: GuidEntityAllocator) -> Self {
Self {
id,
storage: UnsafeCell::new(Storage::new(id)),

View File

@ -22,6 +22,7 @@ Here are the changes made:
* ResourceTypeId, ComponentTypeId, TagTypeId use static str (std::any::type_name) instead of TypeId (std::any::TypeId is not constant across rust binaries)
* Implement "DowncastTypeName" to allow downcasting based on type name
* Upgraded derivative, smallvec, itertools to eliminate redundant dependencies
* Added GuidEntityAllocator. Generates random entityids and enables "stable" ids across serialization.
## Benchmarks