|
|
|
@ -1,42 +1,80 @@ |
|
|
|
|
use std::alloc::Layout; |
|
|
|
|
use std::collections::HashMap; |
|
|
|
|
use std::collections::{HashMap, HashSet}; |
|
|
|
|
|
|
|
|
|
use super::archetable::{Archetable, EntityRow}; |
|
|
|
|
use super::archetype::Archetype; |
|
|
|
|
use super::component::Component; |
|
|
|
|
use super::entity::Entity; |
|
|
|
|
use super::entity_index::EntityIndex; |
|
|
|
|
use super::prelude::Archetype; |
|
|
|
|
use super::world::World; |
|
|
|
|
|
|
|
|
|
/// Graph of [`Archetable`]s contained within [`Node`]s.
|
|
|
|
|
///
|
|
|
|
|
/// Allows for traversal by of `Archetables` by their [`Archetype`], one
|
|
|
|
|
/// [`Component`] at a time. For example to get all [`entities`][`Entity`] with
|
|
|
|
|
/// both `Position` and `Velocity` you would start at `root()`, then traverse
|
|
|
|
|
/// to `Position`, then `Velocity`, or the other way around, since you end up
|
|
|
|
|
/// at the same `Node` either way. You can then `iter()` all `Archetables` that
|
|
|
|
|
/// have those same components.
|
|
|
|
|
pub struct Archegraph { |
|
|
|
|
node_count: usize, |
|
|
|
|
node_storage: Vec<Option<Node>>, |
|
|
|
|
node_storage: Vec<Option<NodeData>>, |
|
|
|
|
archetable_storage: Vec<Option<Archetable>>, |
|
|
|
|
// unused_indices: Vec<NodeIndex>,
|
|
|
|
|
type_lookup: HashMap<Archetype, NodeIndex>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub type NodeIndex = usize; |
|
|
|
|
pub type GraphEntityIndex = super::entity_index::EntityIndex<(NodeIndex, EntityRow)>; |
|
|
|
|
|
|
|
|
|
pub struct Node { |
|
|
|
|
index: NodeIndex, |
|
|
|
|
archetable: Archetable, |
|
|
|
|
with: HashMap<Component, NodeIndex>, |
|
|
|
|
without: HashMap<Component, NodeIndex>, |
|
|
|
|
type NodeIndex = usize; |
|
|
|
|
|
|
|
|
|
pub struct Node<'a> { |
|
|
|
|
archegraph: &'a Archegraph, |
|
|
|
|
node_index: NodeIndex, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub struct NodeMut<'a> { |
|
|
|
|
archegraph: &'a mut Archegraph, |
|
|
|
|
node_index: NodeIndex, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub type GraphEntityIndex = EntityIndex<(NodeIndex, EntityRow)>; |
|
|
|
|
#[derive(Default)] |
|
|
|
|
struct NodeData { |
|
|
|
|
add: HashMap<Component, NodeIndex>, |
|
|
|
|
remove: HashMap<Component, NodeIndex>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Archegraph { |
|
|
|
|
pub fn new() -> Self { |
|
|
|
|
let empty_node = Node::new(0, Archetable::new_empty()); |
|
|
|
|
let mut type_lookup = HashMap::new(); |
|
|
|
|
type_lookup.insert(Archetype::EMPTY, 0); |
|
|
|
|
// Empty archetable is added in `World::bootstrap_archetable`.
|
|
|
|
|
Self { |
|
|
|
|
node_count: 1, |
|
|
|
|
node_storage: vec![Some(empty_node)], |
|
|
|
|
node_count: 0, |
|
|
|
|
node_storage: Vec::new(), |
|
|
|
|
archetable_storage: Vec::new(), |
|
|
|
|
// unused_indices: Vec::new(),
|
|
|
|
|
type_lookup, |
|
|
|
|
type_lookup: HashMap::new(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn bootstrap(&mut self, pairs: Vec<(Entity, Layout)>) { |
|
|
|
|
// Inner function is just so call to `bootstrap` is prettier.
|
|
|
|
|
fn inner(graph: &mut Archegraph, pairs: Vec<(Entity, Layout)>, mut start_index: usize) { |
|
|
|
|
// Iterate all the subsets of the pairs and initialize them, first.
|
|
|
|
|
// The `start_index` should ensure that no subset is bootstrapped twice.
|
|
|
|
|
for i in start_index..pairs.len() { |
|
|
|
|
let mut pairs_subset = pairs.clone(); |
|
|
|
|
pairs_subset.remove(i); |
|
|
|
|
inner(graph, pairs_subset, start_index); |
|
|
|
|
start_index += 1; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let layouts = pairs.iter().map(|p| Some(p.1)).collect(); |
|
|
|
|
let archetype = Archetype::new(pairs.iter().map(|p| p.0)); |
|
|
|
|
let archetable = Archetable::new(archetype, layouts); |
|
|
|
|
|
|
|
|
|
// No entity lookups necessary during bootstrapping.
|
|
|
|
|
graph.insert(archetable, &GraphEntityIndex::EMPTY); |
|
|
|
|
} |
|
|
|
|
inner(self, pairs, 0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
@ -46,22 +84,22 @@ impl Archegraph { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn root(&self) -> &Node { |
|
|
|
|
pub fn root(&self) -> &NodeData { |
|
|
|
|
self.node_storage[0].as_ref().unwrap() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn root_mut(&mut self) -> &mut Node { |
|
|
|
|
pub fn root_mut(&mut self) -> &mut NodeData { |
|
|
|
|
self.node_storage[0].as_mut().unwrap() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get(&self, index: NodeIndex) -> Option<&Node> { |
|
|
|
|
pub fn get(&self, index: NodeIndex) -> Option<&NodeData> { |
|
|
|
|
self.node_storage.get(index).and_then(Option::as_ref) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_mut(&mut self, index: NodeIndex) -> Option<&mut Node> { |
|
|
|
|
pub fn get_mut(&mut self, index: NodeIndex) -> Option<&mut NodeData> { |
|
|
|
|
self.node_storage.get_mut(index).and_then(Option::as_mut) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -70,23 +108,29 @@ impl Archegraph { |
|
|
|
|
&mut self, |
|
|
|
|
archetype: &Archetype, |
|
|
|
|
entity_index: &GraphEntityIndex, |
|
|
|
|
) -> &mut Node { |
|
|
|
|
) -> NodeMut { |
|
|
|
|
if let Some(index) = self.type_lookup.get(&archetype) { |
|
|
|
|
// SAFETY: `type_lookup` is guaranteed to point to a valid archetable.
|
|
|
|
|
self.node_storage[*index].as_mut().unwrap() |
|
|
|
|
} else { |
|
|
|
|
let index = self.reserve_archetable_index(); |
|
|
|
|
let layouts = self.get_layouts(archetype, entity_index); |
|
|
|
|
let archetable = Archetable::new(archetype.clone(), layouts); |
|
|
|
|
let mut node = Node::new(index, archetable); |
|
|
|
|
self.insert(archetable, entity_index) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
self.type_lookup.insert(archetype.clone(), index); |
|
|
|
|
pub fn insert(&mut self, archetable: Archetable, entity_index: &GraphEntityIndex) -> NodeMut { |
|
|
|
|
let archetype = archetable.archetype().clone(); |
|
|
|
|
let index = self.reserve_archetable_index(); |
|
|
|
|
|
|
|
|
|
let mut node = Default::default(); |
|
|
|
|
self.fill_edges(&mut node, entity_index); |
|
|
|
|
|
|
|
|
|
let entry = &mut self.node_storage[index]; |
|
|
|
|
*entry = Some(node); |
|
|
|
|
entry.as_mut().unwrap() |
|
|
|
|
} |
|
|
|
|
self.node_storage[index] = Some(node); |
|
|
|
|
self.archetable_storage[index] = Some(archetable); |
|
|
|
|
self.type_lookup.insert(archetype, index); |
|
|
|
|
|
|
|
|
|
() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn get_layout(&self, component: Component, entity_index: &GraphEntityIndex) -> Option<Layout> { |
|
|
|
@ -95,7 +139,7 @@ impl Archegraph { |
|
|
|
|
let (node_index, row) = entity_index.get(entity.id())?.data; |
|
|
|
|
let archetable = self.get(node_index).unwrap().get(); |
|
|
|
|
let component_index = archetable.archetype().position(World::LAYOUT)?; |
|
|
|
|
let column = archetable.get_column(component_index).unwrap(); |
|
|
|
|
let column = archetable.column_by_index(component_index).unwrap(); |
|
|
|
|
let column = unsafe { column.as_typed_slice::<Layout>() }; |
|
|
|
|
Some(column[row]) |
|
|
|
|
} |
|
|
|
@ -118,43 +162,162 @@ impl Archegraph { |
|
|
|
|
// })
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn fill_edges(&mut self, node: &mut Node, entity_index: &GraphEntityIndex) { |
|
|
|
|
fn fill_edges(&mut self, node: &mut NodeData, entity_index: &GraphEntityIndex) { |
|
|
|
|
let archetype = node.archetable.archetype(); |
|
|
|
|
for component in archetype.components() { |
|
|
|
|
let other_archetype = archetype.clone().without(*component); |
|
|
|
|
let other_node = self.lookup_or_create(&other_archetype, entity_index); |
|
|
|
|
node.without.insert(*component, other_node.index); |
|
|
|
|
other_node.with.insert(*component, node.index); |
|
|
|
|
node.remove.insert(*component, other_node.index); |
|
|
|
|
other_node.add.insert(*component, node.index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Node { |
|
|
|
|
impl<'a> Node<'a> { |
|
|
|
|
#[must_use] |
|
|
|
|
fn new(index: NodeIndex, archetable: Archetable) -> Self { |
|
|
|
|
Self { |
|
|
|
|
index, |
|
|
|
|
archetable, |
|
|
|
|
with: HashMap::new(), |
|
|
|
|
without: HashMap::new(), |
|
|
|
|
pub fn iter(&'a self) -> impl Iterator<Item = &Archetable> { |
|
|
|
|
let storage = &self.archegraph.archetable_storage; |
|
|
|
|
GraphIter::new(&self.archegraph.node_storage, self.node_index) |
|
|
|
|
.map(|index| storage[index].as_ref().unwrap()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get(&self) -> &Archetable { |
|
|
|
|
let storage = &self.archegraph.archetable_storage; |
|
|
|
|
storage[self.node_index].as_ref().unwrap() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
impl<'a> NodeMut<'a> { |
|
|
|
|
#[must_use] |
|
|
|
|
pub fn index(&self) -> NodeIndex { |
|
|
|
|
self.index |
|
|
|
|
pub fn iter(&'a mut self) -> impl Iterator<Item = &'a mut Archetable> { |
|
|
|
|
let node_storage = &self.archegraph.node_storage; |
|
|
|
|
let archetable_storage = &mut self.archegraph.archetable_storage; |
|
|
|
|
GraphIter::new(node_storage, self.node_index) |
|
|
|
|
.map(|index| archetable_storage[index].as_mut().unwrap()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get(&self) -> &Archetable { |
|
|
|
|
&self.archetable |
|
|
|
|
pub fn get(&mut self) -> &mut Archetable { |
|
|
|
|
let storage = &mut self.archegraph.archetable_storage; |
|
|
|
|
storage[self.node_index].as_mut().unwrap() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
struct GraphIter<'a> { |
|
|
|
|
node_storage: &'a Vec<Option<NodeData>>, |
|
|
|
|
stack: Vec<NodeIndex>, |
|
|
|
|
visited: HashSet<NodeIndex>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl GraphIter<'_> { |
|
|
|
|
fn new(node_storage: &Vec<Option<NodeData>>, start_index: NodeIndex) -> GraphIter { |
|
|
|
|
GraphIter { |
|
|
|
|
node_storage, |
|
|
|
|
stack: vec![start_index], |
|
|
|
|
visited: HashSet::new(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[inline] |
|
|
|
|
impl<'a> Iterator for GraphIter<'a> { |
|
|
|
|
type Item = NodeIndex; |
|
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> { |
|
|
|
|
while let Some(index) = self.stack.pop() { |
|
|
|
|
if !self.visited.contains(&index) { |
|
|
|
|
let node = self.archegraph.get(index).unwrap(); |
|
|
|
|
self.stack.extend(node.add.values()); |
|
|
|
|
self.visited.insert(index); |
|
|
|
|
return Some(index); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
None |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl NodeData { |
|
|
|
|
#[must_use] |
|
|
|
|
pub fn get_mut(&mut self) -> &mut Archetable { |
|
|
|
|
&mut self.archetable |
|
|
|
|
fn new(archetable: Archetable) -> Self { |
|
|
|
|
Self { |
|
|
|
|
archetable, |
|
|
|
|
add: HashMap::new(), |
|
|
|
|
remove: HashMap::new(), |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
|
mod tests { |
|
|
|
|
use super::*; |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn bootstrap() { |
|
|
|
|
let mut archegraph = Archegraph::new(); |
|
|
|
|
|
|
|
|
|
let a = Entity::new_checked(1, 0).unwrap(); |
|
|
|
|
let b = Entity::new_checked(2, 0).unwrap(); |
|
|
|
|
let c = Entity::new_checked(10, 0).unwrap(); |
|
|
|
|
let d = Entity::new_checked(2000, 0).unwrap(); |
|
|
|
|
|
|
|
|
|
archegraph.bootstrap(vec![ |
|
|
|
|
(a, Layout::new::<u8>()), |
|
|
|
|
(b, Layout::new::<u16>()), |
|
|
|
|
(c, Layout::new::<u32>()), |
|
|
|
|
(d, Layout::new::<u64>()), |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
// {a, b, c, d} has 16 subsets including {} (the empty root archetype)
|
|
|
|
|
// and {a, b, c, d} (archetype containing all components at the same time).
|
|
|
|
|
assert_eq!(16, archegraph.node_count()); |
|
|
|
|
|
|
|
|
|
let mut archetypes = archegraph |
|
|
|
|
.root() |
|
|
|
|
.iter(&archegraph) |
|
|
|
|
.map(Archetable::archetype) |
|
|
|
|
.map(Archetype::components) |
|
|
|
|
.collect::<Vec<_>>(); |
|
|
|
|
|
|
|
|
|
// Not guaranteed to iter in any specific order.
|
|
|
|
|
archetypes.sort(); |
|
|
|
|
|
|
|
|
|
assert_eq!( |
|
|
|
|
vec![ |
|
|
|
|
vec![], |
|
|
|
|
vec![a], |
|
|
|
|
vec![a, b], |
|
|
|
|
vec![a, b, c], |
|
|
|
|
vec![a, b, c, d], |
|
|
|
|
vec![a, b, d], |
|
|
|
|
vec![a, c], |
|
|
|
|
vec![a, c, d], |
|
|
|
|
vec![a, d], |
|
|
|
|
vec![b], |
|
|
|
|
vec![b, c], |
|
|
|
|
vec![b, c, d], |
|
|
|
|
vec![b, d], |
|
|
|
|
vec![c], |
|
|
|
|
vec![c, d], |
|
|
|
|
vec![d], |
|
|
|
|
], |
|
|
|
|
archetypes |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// Ensure the columns' layouts are correct for a given Archetype.
|
|
|
|
|
let archetype = Archetype::new([a, c, d]); |
|
|
|
|
let index = &GraphEntityIndex::EMPTY; |
|
|
|
|
let table = &archegraph.lookup_or_create(&archetype, index).get(); |
|
|
|
|
assert_eq!( |
|
|
|
|
Layout::new::<u8>(), |
|
|
|
|
table.column_by_index(0).unwrap().layout() |
|
|
|
|
); |
|
|
|
|
assert_eq!( |
|
|
|
|
Layout::new::<u32>(), |
|
|
|
|
table.column_by_index(1).unwrap().layout() |
|
|
|
|
); |
|
|
|
|
assert_eq!( |
|
|
|
|
Layout::new::<u64>(), |
|
|
|
|
table.column_by_index(2).unwrap().layout() |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|