mirror of
https://github.com/lisk77/comet.git
synced 2025-10-23 21:38:50 +00:00
feat(ecs): limited the creation of archetypes to three unique components to lower the creation to polynomial complexity
This commit is contained in:
parent
0da5200916
commit
d04c706a94
5 changed files with 409 additions and 421 deletions
|
@ -1,7 +1,5 @@
|
|||
use comet_colors::{Color as ColorTrait, LinearRgba};
|
||||
use comet_ecs::{
|
||||
Camera2D, Color, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D,
|
||||
};
|
||||
use comet_ecs::{Camera2D, Component, Entity, Render2D, Scene, Text, Transform2D, Transform3D};
|
||||
use comet_input::keyboard::Key;
|
||||
use comet_log::*;
|
||||
use comet_renderer::renderer::Renderer;
|
||||
|
|
|
@ -215,16 +215,14 @@ impl Scene {
|
|||
self.create_archetype(new_component_set.clone());
|
||||
}
|
||||
|
||||
let powerset = ComponentSet::powerset(new_component_set.to_vec());
|
||||
let subsets = ComponentSet::compute_subsets_up_to_size_3(new_component_set.to_vec());
|
||||
|
||||
for subset in powerset {
|
||||
let component_set = ComponentSet::from_ids(subset.iter().cloned().collect());
|
||||
|
||||
if !self.archetypes.contains_archetype(&component_set) {
|
||||
self.create_archetype(component_set.clone());
|
||||
for subset in subsets {
|
||||
if !self.archetypes.contains_archetype(&subset) {
|
||||
self.create_archetype(subset.clone());
|
||||
}
|
||||
|
||||
self.add_entity_to_archetype(entity_id as u32, component_set);
|
||||
self.add_entity_to_archetype(entity_id as u32, subset);
|
||||
}
|
||||
|
||||
info!(
|
||||
|
@ -256,16 +254,14 @@ impl Scene {
|
|||
self.create_archetype(new_component_set.clone());
|
||||
}
|
||||
|
||||
let powerset = ComponentSet::powerset(new_component_set.to_vec());
|
||||
let subsets = ComponentSet::compute_subsets_up_to_size_3(new_component_set.to_vec());
|
||||
|
||||
for subset in powerset {
|
||||
let component_set = ComponentSet::from_ids(subset.iter().cloned().collect());
|
||||
|
||||
if !self.archetypes.contains_archetype(&component_set) {
|
||||
self.create_archetype(component_set.clone());
|
||||
for subset in subsets {
|
||||
if !self.archetypes.contains_archetype(&subset) {
|
||||
self.create_archetype(subset.clone());
|
||||
}
|
||||
|
||||
self.add_entity_to_archetype(entity_id as u32, component_set);
|
||||
self.add_entity_to_archetype(entity_id as u32, subset);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,6 +291,10 @@ impl Scene {
|
|||
/// Returns a list of entities that have the given components.
|
||||
pub fn get_entities_with(&self, components: Vec<TypeId>) -> Vec<usize> {
|
||||
let component_set = ComponentSet::from_ids(components);
|
||||
if component_set.size() > 3 {
|
||||
error!("An entity query should only contain at most 3 different components!");
|
||||
return Vec::new();
|
||||
}
|
||||
if self.archetypes.contains_archetype(&component_set) {
|
||||
return self
|
||||
.archetypes
|
||||
|
@ -316,7 +316,7 @@ impl Scene {
|
|||
}
|
||||
}
|
||||
|
||||
/// Iterates over all entities that have the given components and calls the given function.
|
||||
/// Iterates over all entities that have the two given components and calls the given function.
|
||||
pub fn foreach<C: Component, K: Component>(&mut self, func: fn(&mut C, &mut K)) {
|
||||
let entities = self.get_entities_with(vec![C::type_id(), K::type_id()]);
|
||||
for entity in entities {
|
||||
|
|
|
@ -1,17 +1,10 @@
|
|||
use std::{
|
||||
alloc::{
|
||||
handle_alloc_error,
|
||||
Layout
|
||||
},
|
||||
any::TypeId,
|
||||
hash::{
|
||||
Hash,
|
||||
Hasher
|
||||
},
|
||||
mem::MaybeUninit,
|
||||
ptr::NonNull
|
||||
};
|
||||
use std::ptr;
|
||||
use std::{
|
||||
alloc::{handle_alloc_error, Layout},
|
||||
any::TypeId,
|
||||
mem::MaybeUninit,
|
||||
ptr::NonNull,
|
||||
};
|
||||
|
||||
// The following two structs are just blatantly stolen from Bevy - another Rust game engine.
|
||||
// I just need them for the ComponentStorage system, and I was too lazy to write them myself.
|
||||
|
@ -23,7 +16,7 @@ pub struct BlobVec {
|
|||
len: usize,
|
||||
data: NonNull<u8>,
|
||||
swap_scratch: NonNull<u8>,
|
||||
drop: unsafe fn(*mut u8)
|
||||
drop: unsafe fn(*mut u8),
|
||||
}
|
||||
|
||||
impl BlobVec {
|
||||
|
@ -32,13 +25,12 @@ impl BlobVec {
|
|||
BlobVec {
|
||||
swap_scratch: NonNull::dangling(),
|
||||
data: NonNull::dangling(),
|
||||
capacity: usize:: MAX,
|
||||
capacity: usize::MAX,
|
||||
len: 0,
|
||||
item_layout,
|
||||
drop,
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
let swap_scratch = NonNull::new(unsafe { std::alloc::alloc(item_layout) })
|
||||
.unwrap_or_else(|| handle_alloc_error(item_layout));
|
||||
|
||||
|
@ -100,7 +92,6 @@ impl BlobVec {
|
|||
self.capacity
|
||||
}
|
||||
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn get_ptr(&self) -> NonNull<u8> {
|
||||
self.data
|
||||
|
@ -129,7 +120,7 @@ impl BlobVec {
|
|||
pub unsafe fn push_element<T>(&mut self, element: T) {
|
||||
let index = self.push_uninit();
|
||||
let ptr = self.get_unchecked(index) as *mut T;
|
||||
ptr::write(ptr,element);
|
||||
ptr::write(ptr, element);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
|
@ -217,7 +208,7 @@ const fn padding_needed_for(layout: &Layout, align: usize) -> usize {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Column {
|
||||
pub data: BlobVec
|
||||
pub data: BlobVec,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
|
@ -282,7 +273,10 @@ impl Column {
|
|||
}
|
||||
|
||||
pub fn swap(&mut self, index1: usize, index2: usize) {
|
||||
assert!(index1 < self.data.len() && index2 < self.data.len(), "Index out of bounds");
|
||||
assert!(
|
||||
index1 < self.data.len() && index2 < self.data.len(),
|
||||
"Index out of bounds"
|
||||
);
|
||||
|
||||
unsafe {
|
||||
let ptr1 = self.data.get_unchecked(index1);
|
||||
|
|
|
@ -4,22 +4,47 @@ use std::hash::{Hash, Hasher};
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ComponentSet {
|
||||
set: HashSet<TypeId>
|
||||
set: HashSet<TypeId>,
|
||||
}
|
||||
|
||||
impl ComponentSet {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
set: HashSet::new()
|
||||
set: HashSet::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_ids(ids: Vec<TypeId>) -> Self {
|
||||
Self {
|
||||
set: ids.into_iter().collect()
|
||||
set: ids.into_iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_subsets_up_to_size_3(ids: Vec<TypeId>) -> Vec<ComponentSet> {
|
||||
let mut result = Vec::new();
|
||||
let n = ids.len();
|
||||
|
||||
for i in 0..n {
|
||||
result.push(ComponentSet::from_ids(vec![ids[i]]));
|
||||
}
|
||||
|
||||
for i in 0..n {
|
||||
for j in (i + 1)..n {
|
||||
result.push(ComponentSet::from_ids(vec![ids[i], ids[j]]));
|
||||
}
|
||||
}
|
||||
|
||||
for i in 0..n {
|
||||
for j in (i + 1)..n {
|
||||
for k in (j + 1)..n {
|
||||
result.push(ComponentSet::from_ids(vec![ids[i], ids[j], ids[k]]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn powerset(ids: Vec<TypeId>) -> Vec<HashSet<TypeId>> {
|
||||
let n = ids.len();
|
||||
let mut subsets: Vec<HashSet<TypeId>> = Vec::with_capacity(1 << n);
|
||||
|
@ -45,6 +70,10 @@ impl ComponentSet {
|
|||
pub fn to_vec(&self) -> Vec<TypeId> {
|
||||
self.set.iter().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn size(&self) -> usize {
|
||||
self.set.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for ComponentSet {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
use crate::Column;
|
||||
use std::hash::{
|
||||
Hash,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SparseSet {
|
||||
sparse: Vec<Option<Vec<Option<usize>>>>,
|
||||
dense: Column,
|
||||
page_size: usize
|
||||
page_size: usize,
|
||||
}
|
||||
|
||||
impl SparseSet {
|
||||
|
@ -15,7 +12,7 @@ impl SparseSet {
|
|||
Self {
|
||||
sparse: Vec::new(),
|
||||
dense: Column::new::<T>(capacity),
|
||||
page_size
|
||||
page_size,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,17 +35,32 @@ impl SparseSet {
|
|||
}
|
||||
|
||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T> {
|
||||
if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(page_vec) = self
|
||||
.sparse
|
||||
.get(index / self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
if let Some(sparse_index) = page_vec
|
||||
.get(index % self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
let dense_index = *sparse_index;
|
||||
let last_index = self.dense.data.len() - 1;
|
||||
if dense_index != last_index {
|
||||
self.dense.swap(dense_index, last_index);
|
||||
if let Some(page_vec) = self.sparse.get_mut(last_index / self.page_size).and_then(|x| x.as_mut()) {
|
||||
if let Some(page_vec) = self
|
||||
.sparse
|
||||
.get_mut(last_index / self.page_size)
|
||||
.and_then(|x| x.as_mut())
|
||||
{
|
||||
page_vec[last_index % self.page_size] = Some(dense_index);
|
||||
}
|
||||
}
|
||||
if let Some(page_vec) = self.sparse.get_mut(index / self.page_size).and_then(|x| x.as_mut()) {
|
||||
if let Some(page_vec) = self
|
||||
.sparse
|
||||
.get_mut(index / self.page_size)
|
||||
.and_then(|x| x.as_mut())
|
||||
{
|
||||
page_vec[index % self.page_size] = None;
|
||||
}
|
||||
return self.dense.remove::<T>(last_index);
|
||||
|
@ -58,85 +70,40 @@ impl SparseSet {
|
|||
}
|
||||
|
||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
||||
if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(page_vec) = self
|
||||
.sparse
|
||||
.get(index / self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
if let Some(sparse_index) = page_vec
|
||||
.get(index % self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
self.dense.get::<T>(*sparse_index)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
||||
if let Some(page_vec) = self.sparse.get(index / self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(sparse_index) = page_vec.get(index % self.page_size).and_then(|x| x.as_ref()) {
|
||||
if let Some(page_vec) = self
|
||||
.sparse
|
||||
.get(index / self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
if let Some(sparse_index) = page_vec
|
||||
.get(index % self.page_size)
|
||||
.and_then(|x| x.as_ref())
|
||||
{
|
||||
self.dense.get_mut::<T>(*sparse_index)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*#[derive(Debug, Clone)]
|
||||
pub struct SparseSet {
|
||||
sparse: Vec<Option<usize>>,
|
||||
dense: Column,
|
||||
}
|
||||
|
||||
impl SparseSet {
|
||||
pub fn new<T: 'static>(capacity: usize) -> Self {
|
||||
Self {
|
||||
sparse: Vec::new(),
|
||||
dense: Column::new::<T>(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert<T: 'static>(&mut self, index: usize, value: T) {
|
||||
if index >= self.sparse.len() {
|
||||
self.sparse.resize(index + 1, None);
|
||||
}
|
||||
self.sparse[index] = Some(self.dense.data.len());
|
||||
self.dense.push(value);
|
||||
}
|
||||
|
||||
pub fn remove<T: 'static>(&mut self, index: usize) -> Option<T>{
|
||||
if let Some(sparse_index) = self.sparse.get(index).and_then(|x| x.as_ref()) {
|
||||
let dense_index = *sparse_index;
|
||||
let last_index = self.dense.data.len() - 1;
|
||||
if dense_index != last_index {
|
||||
self.dense.swap(dense_index, last_index);
|
||||
if let Some(sparse) = self.sparse.get_mut(last_index) {
|
||||
*sparse = Some(dense_index);
|
||||
}
|
||||
}
|
||||
self.sparse[index] = None;
|
||||
self.dense.remove::<T>(last_index)
|
||||
}
|
||||
else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get<T: 'static>(&self, index: usize) -> Option<&T> {
|
||||
match self.sparse.get(index).and_then(|x| x.as_ref()) {
|
||||
Some(sparse_index) => self.dense.get::<T>(*sparse_index),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut<T: 'static>(&mut self, index: usize) -> Option<&mut T> {
|
||||
match self.sparse.get(index).and_then(|x| x.as_ref()) {
|
||||
Some(sparse_index) => self.dense.get_mut::<T>(*sparse_index),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}*/
|
Loading…
Add table
Add a link
Reference in a new issue