Last active
September 29, 2023 12:02
-
-
Save Janglee123/501b385c51fb0fbafff64c67ded041a5 to your computer and use it in GitHub Desktop.
Dumb wip ecs engine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::any::{Any, TypeId}; | |
use std::collections::HashMap; | |
use std::fmt::Debug; | |
use std::hash::{Hash}; | |
use std::hint::black_box; | |
use std::io::Read; | |
use std::marker::PhantomData; | |
use std::time::Instant; | |
use rand::Rng; | |
// This is dumb idea, I should use hibitset | |
// But anyway I think total 256 components are enough for me | |
// pub struct ComponentId(u8); | |
pub type ComponentId = u8; | |
#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] | |
pub struct BitSet { | |
bitmask: [u64; 4], | |
pub id: u8, | |
} | |
impl BitSet { | |
pub fn new() -> Self { | |
Self { | |
bitmask: [0u64; 4], | |
id: 0, | |
} | |
} | |
pub fn insert_id(&mut self, id: u8) -> &mut Self { | |
let index = id / 64; | |
let position = id - index * 64; | |
self.bitmask[index as usize] = self.bitmask[index as usize] | 1 << position; | |
self | |
} | |
pub fn remove_id(&mut self, id: u8) -> &mut Self { | |
let index = id / 64; | |
let position = id - index * 64; | |
let bit_to_remove = 1 << position; | |
self.bitmask[index as usize] = !(self.bitmask[index as usize] ^ bit_to_remove); | |
self | |
} | |
pub fn from_id(id: u8) -> Self { | |
let mut bitmask = [0u64; 4]; | |
let index = id / 64; | |
let position = id - index * 64; | |
bitmask[index as usize] = 1 << position; | |
Self { | |
id, | |
bitmask, | |
} | |
} | |
pub fn contains(&self, other: &BitSet) -> bool { | |
let mut result = true; | |
for (a, b) in self.bitmask.iter().zip(other.bitmask.iter()) { | |
result &= a & b == *b | |
} | |
result | |
} | |
pub fn contains_id(&self, id: u8) -> bool { | |
let index = id / 64; | |
let position = id - index * 64; | |
self.bitmask[index as usize] & 1 << position > 0 | |
} | |
} | |
pub trait Component: 'static + Debug { | |
fn get_type_id(&self) -> TypeId { | |
TypeId::of::<Self>() | |
} | |
} | |
#[derive(Debug)] | |
pub struct ComponentVec<T: Component> | |
{ | |
pub list: Vec<T>, | |
} | |
impl<T: Component> ComponentVec<T> { | |
pub fn new() -> Self { | |
Self { | |
list: Vec::new() | |
} | |
} | |
} | |
#[derive(Debug)] | |
pub struct TypelessComponentVec | |
{ | |
list: Box<dyn Any>, | |
} | |
impl TypelessComponentVec { | |
pub fn new<T: Component>() -> Self { | |
Self { | |
list: Box::new(ComponentVec::<T>::new()), | |
} | |
} | |
pub fn get_mut<T: Component>(&mut self) -> &mut ComponentVec<T> | |
{ | |
self.list.downcast_mut::<ComponentVec<T>>().unwrap() | |
} | |
pub fn get<T: Component>(&self) -> &ComponentVec<T> | |
{ | |
self.list.downcast_ref::<ComponentVec<T>>().unwrap() | |
} | |
} | |
#[derive(Debug)] | |
pub struct Archetype { | |
pub set: HashMap<TypeId, TypelessComponentVec>, | |
pub entity_row_map: HashMap<u64, usize>, | |
} | |
impl Archetype { | |
pub fn new() -> Self { | |
Self { | |
set: HashMap::new(), | |
entity_row_map: HashMap::new(), | |
} | |
} | |
pub fn get<T: Component>(&self) -> &ComponentVec<T> { | |
let list = self.set.get(&TypeId::of::<T>()).unwrap().get::<T>(); | |
list | |
} | |
pub fn get_mut<T: Component>(&mut self) -> &mut ComponentVec<T> { | |
let list = self.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>(); | |
list | |
} | |
pub fn print_list<T: Component>(&self) { | |
let list = self.set.get(&TypeId::of::<T>()).unwrap().get::<T>(); | |
for a in list.list.iter() { | |
println!("{:?}", a); | |
} | |
} | |
} | |
pub trait ComponentSet { | |
fn get_bit_id_set(id_map: &HashMap<TypeId, ComponentId>) -> BitSet; | |
fn insert(self, archetype: &mut Archetype, entity_id: u64); | |
fn create_archetype(&self) -> Archetype; | |
fn get_type_id() -> Vec<TypeId>; | |
} | |
impl<T: Component> ComponentSet for T { | |
fn get_bit_id_set(id_map: &HashMap<TypeId, ComponentId>) -> BitSet { | |
let mut bitset = BitSet::new(); | |
let id = id_map.get(&TypeId::of::<T>()).unwrap().clone(); | |
bitset.insert_id(id as u8); | |
bitset | |
} | |
fn insert(self, archetype: &mut Archetype, entity_id: u64) { | |
let b = archetype.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>(); | |
b.list.push(self); | |
archetype.entity_row_map.insert(entity_id, b.list.len() - 1); | |
} | |
fn create_archetype(&self) -> Archetype { | |
let mut archetype = Archetype::new(); | |
archetype.set.insert(TypeId::of::<T>(), TypelessComponentVec::new::<T>()); | |
archetype | |
} | |
fn get_type_id() -> Vec<TypeId> { | |
vec![TypeId::of::<T>()] | |
} | |
} | |
impl<T: Component> ComponentSet for (T, ) | |
{ | |
fn get_bit_id_set(id_map: &HashMap<TypeId, ComponentId>) -> BitSet { | |
let mut bitset = BitSet::new(); | |
let id = id_map.get(&TypeId::of::<T>()).unwrap().clone(); | |
bitset.insert_id(id as u8); | |
bitset | |
} | |
fn insert(self, archetype: &mut Archetype, entity_id: u64) { | |
archetype.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>().list.push(self.0); | |
let len = archetype.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>().list.len() - 1; | |
archetype.entity_row_map.insert(entity_id, len); | |
} | |
fn create_archetype(&self) -> Archetype { | |
let mut archetype = Archetype::new(); | |
archetype.set.insert(TypeId::of::<T>(), TypelessComponentVec::new::<T>()); | |
archetype | |
} | |
fn get_type_id() -> Vec<TypeId> { | |
vec![TypeId::of::<T>()] | |
} | |
} | |
impl<T: Component, V: Component> ComponentSet for (T, V) | |
{ | |
fn get_bit_id_set(id_map: &HashMap<TypeId, ComponentId>) -> BitSet { | |
let mut bitset = BitSet::new(); | |
let id = id_map.get(&TypeId::of::<T>()).unwrap().clone(); | |
bitset.insert_id(id as u8); | |
let id = id_map.get(&TypeId::of::<V>()).unwrap().clone(); | |
bitset.insert_id(id as u8); | |
bitset | |
} | |
fn insert(self, archetype: &mut Archetype, entity_id: u64) { | |
archetype.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>().list.push(self.0); | |
archetype.set.get_mut(&TypeId::of::<V>()).unwrap().get_mut::<V>().list.push(self.1); | |
let len = archetype.set.get_mut(&TypeId::of::<T>()).unwrap().get_mut::<T>().list.len() - 1; | |
archetype.entity_row_map.insert(entity_id, len); | |
} | |
fn create_archetype(&self) -> Archetype { | |
let mut archetype = Archetype::new(); | |
archetype.set.insert(TypeId::of::<T>(), TypelessComponentVec::new::<T>()); | |
archetype.set.insert(TypeId::of::<V>(), TypelessComponentVec::new::<V>()); | |
archetype | |
} | |
fn get_type_id() -> Vec<TypeId> { | |
vec![TypeId::of::<T>(), TypeId::of::<V>()] | |
} | |
} | |
pub trait Query<'a> { | |
type ResultType; | |
fn get_bitset(component_map: &HashMap<TypeId, ComponentId>) -> BitSet; | |
fn run(component_map: &'a HashMap<TypeId, ComponentId>, archetype_map: &'a HashMap<BitSet, Archetype>) -> Box<dyn Iterator<Item=Self::ResultType> + 'a>; | |
} | |
impl<'a, T: Component> Query<'a> for (T, ) { | |
type ResultType = (&'a T, ); | |
#[inline(always)] | |
fn get_bitset(component_map: &HashMap<TypeId, ComponentId>) -> BitSet { | |
let mut bitset = BitSet::new(); | |
bitset.insert_id(component_map.get(&TypeId::of::<T>()).unwrap().clone()); | |
bitset | |
} | |
#[inline(always)] | |
fn run(component_map: &'a HashMap<TypeId, ComponentId>, archetype_map: &'a HashMap<BitSet, Archetype>) -> Box<dyn Iterator<Item=Self::ResultType> + 'a> { | |
let bitset = Self::get_bitset(component_map); | |
Box::new(archetype_map | |
.iter() | |
.filter(move |x| x.0.contains(&bitset)) | |
.map(|x| &x.1.get::<T>().list) | |
.flatten().map(|x| (x,))) | |
} | |
} | |
impl<'a, T: Component, V: Component> Query<'a> for (T, V) { | |
type ResultType = (&'a T, &'a V); | |
#[inline(always)] | |
fn get_bitset(component_map: &HashMap<TypeId, ComponentId>) -> BitSet { | |
let mut bitset = BitSet::new(); | |
bitset.insert_id(component_map.get(&TypeId::of::<T>()).unwrap().clone()); | |
bitset.insert_id(component_map.get(&TypeId::of::<V>()).unwrap().clone()); | |
bitset | |
} | |
#[inline(always)] | |
fn run(component_map: &'a HashMap<TypeId, ComponentId>, archetype_map: &'a HashMap<BitSet, Archetype>) -> Box<dyn Iterator<Item=Self::ResultType> + 'a> { | |
let bitset = Self::get_bitset(component_map); | |
let a = archetype_map | |
.iter() | |
.filter(move |x| x.0.contains(&bitset)) | |
.map(|x| x.1) | |
.map(|x| x.get::<T>()) | |
.map(|x| &x.list) | |
.flatten(); | |
let b = archetype_map | |
.iter() | |
.filter(move |x| x.0.contains(&bitset)) | |
.map(|x| x.1) | |
.map(|x| x.get::<V>()) | |
.map(|x| &x.list) | |
.flatten(); | |
let result = a.zip(b); | |
Box::new(result) | |
} | |
} | |
pub struct World { | |
entity_id_counter: u64, | |
component_id_counter: u8, | |
pub component_id_map: HashMap<TypeId, ComponentId>, | |
pub archetype_id_map: HashMap<BitSet, Archetype>, | |
} | |
impl World { | |
pub fn new() -> Self { | |
Self { | |
entity_id_counter: 0, | |
component_id_counter: 0, | |
component_id_map: HashMap::new(), | |
archetype_id_map: HashMap::new(), | |
} | |
} | |
fn get_new_entity_id(&mut self) -> u64 { | |
self.entity_id_counter += 1; | |
self.entity_id_counter | |
} | |
fn get_new_component_id(&mut self) -> u8 { | |
self.component_id_counter += 1; | |
self.component_id_counter | |
} | |
pub fn insert_entity<T: ComponentSet>(&mut self, component_set: T) -> u64 { | |
let entity_id = self.get_new_entity_id(); | |
for type_id in T::get_type_id() { | |
if !self.component_id_map.contains_key(&type_id) { | |
let new_id = self.get_new_component_id(); | |
self.component_id_map.insert(type_id, new_id); | |
} | |
} | |
// let set_id = component_set.get_type_id(); | |
let bitset = T::get_bit_id_set(&self.component_id_map); | |
if !self.archetype_id_map.contains_key(&bitset) { | |
let new_archetype = component_set.create_archetype(); | |
self.archetype_id_map.insert(bitset, new_archetype); | |
} | |
let archetye = self.archetype_id_map.get_mut(&bitset).unwrap(); | |
component_set.insert(archetye, entity_id); | |
entity_id | |
} | |
#[inline(always)] | |
pub fn query<'a, T: Query<'a>>(&'a mut self) -> Box<dyn Iterator<Item=T::ResultType> + 'a> { | |
T::run(&self.component_id_map, &self.archetype_id_map) | |
} | |
} | |
#[derive(Debug)] | |
struct Foo { | |
foo: u8, | |
} | |
impl Component for Foo {} | |
#[derive(Debug, Copy, Clone)] | |
struct Bar { | |
bar: u8, | |
} | |
impl Component for Bar {} | |
fn main() { | |
let mut world = World::new(); | |
let mut rng = rand::thread_rng(); | |
const N: usize = 10_000_000; | |
let mut vec = Vec::with_capacity(N); | |
for _ in 0..N { | |
vec.push(Bar { bar: black_box(rng.gen()) }); | |
} | |
let mut vec2 = Vec::with_capacity(N); | |
for i in 0..N { | |
vec2.push((&vec[i],)); | |
} | |
let start_time = Instant::now(); | |
let mut sum = 0u64; | |
for (i,) in vec2 { | |
sum += black_box(i.bar) as u64; | |
} | |
println!("Array: {}ms Sum: {}", start_time.elapsed().as_micros(), sum); | |
for _ in 0..N { | |
world.insert_entity(Bar { bar: black_box(rng.gen()) }); | |
} | |
let query = world.query::<(Bar, )>(); | |
let start_time = Instant::now(); | |
let mut sum = 0u64; | |
for (bar,) in query { | |
sum += bar.bar as u64; | |
} | |
println!("Query: {:?}ms Sum: {}", start_time.elapsed().as_micros(), sum); | |
let bitset = <(Bar,) as Query>::get_bitset(&world.component_id_map); | |
let query = world.archetype_id_map | |
.iter() | |
.filter(move |x| x.0.contains(&bitset)) | |
.map(|x| &x.1.get::<Bar>().list).flatten(); | |
let start_time = Instant::now(); | |
let mut sum = 0u64; | |
for (bar) in query { | |
sum += black_box(bar.bar) as u64; | |
} | |
println!("Direct: {:?}ms Sum: {}", start_time.elapsed().as_micros(), sum); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment