Skip to content

Commit 52b8b63

Browse files
committed
ecs: New model for plugin components, implement test query code
1 parent f3ee898 commit 52b8b63

14 files changed

+380
-182
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

quill/ecs/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ edition = "2018"
77
[dependencies]
88
ahash = "0.7"
99
arrayvec = "0.5"
10+
itertools = "0.10"
1011
thiserror = "1"

quill/ecs/src/component.rs

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
use std::{alloc::Layout, any::TypeId, sync::Arc};
2-
3-
use crate::space::MemorySpace;
1+
use std::{alloc::Layout, any::TypeId, ptr, sync::Arc};
42

53
/// A type that can be used as a component.
64
///
@@ -9,66 +7,24 @@ pub trait Component: Send + 'static {}
97

108
impl<T> Component for T where T: Send + 'static {}
119

12-
/// Type ID of a component.
13-
///
14-
/// Supports both Rust types and arbitrary
15-
/// "opaque" types identified by a `u64`.
16-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
17-
pub struct ComponentTypeId(ComponentTypeIdInner);
18-
19-
impl ComponentTypeId {
20-
pub fn of<T: 'static>() -> Self {
21-
Self(ComponentTypeIdInner::Rust(TypeId::of::<T>()))
22-
}
23-
24-
pub fn opaque(id: u64) -> Self {
25-
Self(ComponentTypeIdInner::Opaque(id))
26-
}
27-
}
28-
29-
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
30-
enum ComponentTypeIdInner {
31-
Rust(TypeId),
32-
Opaque(u64),
33-
}
34-
3510
/// Metadata for a component type.
3611
#[derive(Clone)]
3712
pub struct ComponentMeta {
38-
/// Memory space where the component is allocated.
39-
pub(crate) space: Arc<MemorySpace>,
4013
/// Component type ID.
41-
pub(crate) type_id: ComponentTypeId,
14+
pub(crate) type_id: TypeId,
4215
/// Component layout.
4316
pub(crate) layout: Layout,
4417
/// Function to drop the component.
45-
pub(crate) drop_fn: Arc<dyn Fn(*mut u8) + Send + Sync>,
18+
pub(crate) drop_fn: unsafe fn(*mut u8),
4619
}
4720

4821
impl ComponentMeta {
4922
/// Creates a `ComponentMeta` for a native Rust component.
5023
pub fn of<T: 'static>() -> Self {
5124
Self {
52-
space: Arc::new(MemorySpace::host()),
53-
type_id: ComponentTypeId::of::<T>(),
25+
type_id: TypeId::of::<T>(),
5426
layout: Layout::new::<T>(),
55-
drop_fn: Arc::new(|data| unsafe { std::ptr::drop_in_place(data.cast::<T>()) }),
56-
}
57-
}
58-
59-
/// Creates a `ComponentMeta` for an arbitrary type, maybe opaque,
60-
/// maybe allocated in a non-default memory space.
61-
pub fn custom(
62-
space: Arc<MemorySpace>,
63-
type_id: ComponentTypeId,
64-
layout: Layout,
65-
drop_fn: Arc<dyn Fn(*mut u8) + Send + Sync>,
66-
) -> Self {
67-
Self {
68-
space,
69-
type_id,
70-
layout,
71-
drop_fn,
27+
drop_fn: |ptr| unsafe { ptr::drop_in_place(ptr.cast::<T>()) },
7228
}
7329
}
7430
}

quill/ecs/src/ecs.rs

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
use std::any::type_name;
1+
use std::{any::type_name, iter};
22

33
use ahash::AHashMap;
4+
use itertools::Either;
45

56
use crate::{
67
bundle::ComponentBundle,
7-
component::{Component, ComponentMeta, ComponentTypeId},
8+
component::{Component, ComponentMeta},
89
entity::{Entities, EntityId},
910
entity_builder::EntityBuilder,
1011
storage::SparseSetStorage,
12+
QueryDriver, QueryTuple,
1113
};
1214

15+
pub use self::components::Components;
16+
17+
mod components;
18+
1319
#[derive(Debug, thiserror::Error)]
1420
pub enum ComponentError {
1521
#[error("entity does not have a component of type {0}")]
@@ -32,7 +38,7 @@ pub struct EntityDead;
3238
/// Feather, the `World` stores blocks, not entities.)
3339
#[derive(Default)]
3440
pub struct Ecs {
35-
components: AHashMap<ComponentTypeId, SparseSetStorage>,
41+
components: Components,
3642
entities: Entities,
3743
}
3844

@@ -46,11 +52,8 @@ impl Ecs {
4652
///
4753
/// Time complexity: O(1)
4854
pub fn get<T: Component>(&self, entity: EntityId) -> Result<&T, ComponentError> {
49-
let storage = self.storage_for::<T>()?;
5055
self.check_entity(entity)?;
51-
storage
52-
.get::<T>(entity.index())
53-
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
56+
self.components.get(entity.index())
5457
}
5558

5659
/// Inserts a component for an entity.
@@ -65,23 +68,19 @@ impl Ecs {
6568
component: T,
6669
) -> Result<(), EntityDead> {
6770
self.check_entity(entity)?;
68-
let storage = self.storage_or_insert_for::<T>();
69-
storage.insert(entity.index(), component);
71+
self.components.insert(entity.index(), component);
7072
Ok(())
7173
}
7274

7375
/// Removes a component from an entity.
7476
///
7577
/// Returns `Err` if the entity does not exist
7678
/// or if it did not have the component.
79+
///
80+
/// Time complexity: O(1)
7781
pub fn remove<T: Component>(&mut self, entity: EntityId) -> Result<(), ComponentError> {
7882
self.check_entity(entity)?;
79-
let storage = self.storage_mut_for::<T>()?;
80-
if storage.remove(entity.index()) {
81-
Ok(())
82-
} else {
83-
Err(ComponentError::MissingComponent(type_name::<T>()))
84-
}
83+
self.components.remove::<T>(entity.index())
8584
}
8685

8786
/// Creates a new entity with no components.
@@ -101,9 +100,9 @@ impl Ecs {
101100
let entity = self.spawn_empty();
102101

103102
for (component_meta, component) in builder.drain() {
104-
let storage = self.storage_or_insert_for_untyped(component_meta);
105103
unsafe {
106-
storage.insert_raw(entity.index(), component.as_ptr());
104+
self.components
105+
.insert_raw(entity.index(), component_meta, component.as_ptr());
107106
}
108107
}
109108

@@ -134,43 +133,41 @@ impl Ecs {
134133

135134
// PERF: could we somehow optimize this linear search
136135
// by only checking storages containing the entity?
137-
for storage in self.components.values_mut() {
136+
for (_, storage) in self.components.storages_mut() {
138137
storage.remove(entity.index());
139138
}
140139

141140
Ok(())
142141
}
143142

143+
/// Queries for all entities that have the given set of components.
144+
///
145+
/// Returns an iterator over tuples of `(entity, components)`.
146+
pub fn query<'a, Q: QueryTuple>(
147+
&'a self,
148+
) -> impl Iterator<Item = (EntityId, Q::Output<'a>)> + 'a
149+
where
150+
Q::Output<'a>: 'a,
151+
{
152+
let sparse_sets = match Q::sparse_sets(&self.components) {
153+
Some(s) => s,
154+
None => return Either::Left(iter::empty()),
155+
};
156+
let sparse_set_refs: Vec<_> = sparse_sets.iter().map(|s| s.to_ref()).collect();
157+
let dense_indices = Q::dense_indices();
158+
159+
let driver = QueryDriver::new(&sparse_set_refs, &dense_indices);
160+
161+
Either::Right(driver.iter().map(move |item| {
162+
let components = unsafe { Q::make_output(&sparse_sets, item.dense_indices) };
163+
let entity = self.entities.get(item.sparse_index);
164+
(entity, components)
165+
}))
166+
}
167+
144168
fn check_entity(&self, entity: EntityId) -> Result<(), EntityDead> {
145169
self.entities
146170
.check_generation(entity)
147171
.map_err(|_| EntityDead)
148172
}
149-
150-
fn storage_for<T: Component>(&self) -> Result<&SparseSetStorage, ComponentError> {
151-
self.components
152-
.get(&ComponentTypeId::of::<T>())
153-
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
154-
}
155-
156-
fn storage_mut_for<T: Component>(&mut self) -> Result<&mut SparseSetStorage, ComponentError> {
157-
self.components
158-
.get_mut(&ComponentTypeId::of::<T>())
159-
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
160-
}
161-
162-
fn storage_or_insert_for<T: Component>(&mut self) -> &mut SparseSetStorage {
163-
self.components
164-
.entry(ComponentTypeId::of::<T>())
165-
.or_insert_with(|| SparseSetStorage::new(ComponentMeta::of::<T>()))
166-
}
167-
168-
fn storage_or_insert_for_untyped(
169-
&mut self,
170-
component_meta: ComponentMeta,
171-
) -> &mut SparseSetStorage {
172-
self.components
173-
.entry(component_meta.type_id)
174-
.or_insert_with(|| SparseSetStorage::new(component_meta))
175-
}
176173
}

quill/ecs/src/ecs/components.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
use std::any::{type_name, TypeId};
2+
3+
use ahash::AHashMap;
4+
5+
use crate::{component::ComponentMeta, storage::SparseSetStorage, Component, ComponentError};
6+
7+
/// A raw ECS that stores only components but does not track
8+
/// entities.
9+
///
10+
/// Operates on entity indices but does not account for generations.
11+
#[derive(Default)]
12+
pub struct Components {
13+
storages: AHashMap<TypeId, SparseSetStorage>,
14+
}
15+
16+
impl Components {
17+
pub fn new() -> Self {
18+
Self::default()
19+
}
20+
21+
pub fn insert<T: Component>(&mut self, index: u32, component: T) {
22+
self.storage_or_insert_for::<T>().insert(index, component);
23+
}
24+
25+
/// # Safety
26+
/// `component` must point to a valid instance of the component.
27+
pub unsafe fn insert_raw(
28+
&mut self,
29+
index: u32,
30+
component_meta: ComponentMeta,
31+
component: *const u8,
32+
) {
33+
self.storage_or_insert_for_untyped(component_meta)
34+
.insert_raw(index, component)
35+
}
36+
37+
pub fn remove<T: Component>(&mut self, index: u32) -> Result<(), ComponentError> {
38+
let was_removed = self.storage_mut_for::<T>()?.remove(index);
39+
if was_removed {
40+
Ok(())
41+
} else {
42+
Err(ComponentError::MissingComponent(type_name::<T>()))
43+
}
44+
}
45+
46+
pub fn get<T: Component>(&self, index: u32) -> Result<&T, ComponentError> {
47+
self.storage_for::<T>()?
48+
.get(index)
49+
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
50+
}
51+
52+
pub fn storages(&self) -> impl Iterator<Item = (TypeId, &SparseSetStorage)> + '_ {
53+
self.storages
54+
.iter()
55+
.map(|(&type_id, storage)| (type_id, storage))
56+
}
57+
58+
pub fn storages_mut(&mut self) -> impl Iterator<Item = (TypeId, &mut SparseSetStorage)> + '_ {
59+
self.storages
60+
.iter_mut()
61+
.map(|(&type_id, storage)| (type_id, storage))
62+
}
63+
64+
pub fn storage_for<T: Component>(&self) -> Result<&SparseSetStorage, ComponentError> {
65+
self.storages
66+
.get(&TypeId::of::<T>())
67+
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
68+
}
69+
70+
fn storage_mut_for<T: Component>(&mut self) -> Result<&mut SparseSetStorage, ComponentError> {
71+
self.storages
72+
.get_mut(&TypeId::of::<T>())
73+
.ok_or_else(|| ComponentError::MissingComponent(type_name::<T>()))
74+
}
75+
76+
fn storage_or_insert_for<T: Component>(&mut self) -> &mut SparseSetStorage {
77+
self.storages
78+
.entry(TypeId::of::<T>())
79+
.or_insert_with(|| SparseSetStorage::new(ComponentMeta::of::<T>()))
80+
}
81+
82+
fn storage_or_insert_for_untyped(
83+
&mut self,
84+
component_meta: ComponentMeta,
85+
) -> &mut SparseSetStorage {
86+
self.storages
87+
.entry(component_meta.type_id)
88+
.or_insert_with(|| SparseSetStorage::new(component_meta))
89+
}
90+
}

quill/ecs/src/entity.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,14 @@ impl Entities {
8080
Ok(())
8181
}
8282
}
83+
84+
/// Gets the entity with generation for the given index.
85+
pub fn get(&self, index: u32) -> EntityId {
86+
EntityId {
87+
index,
88+
generation: self.generations[index as usize],
89+
}
90+
}
8391
}
8492

8593
#[cfg(test)]

quill/ecs/src/entity_builder.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
use std::{
2+
any::TypeId,
23
mem::{size_of, MaybeUninit},
34
ptr::{self, NonNull},
45
};
56

6-
use crate::{component::ComponentMeta, Component, ComponentTypeId, Ecs, EntityId};
7+
use crate::{component::ComponentMeta, Component, Ecs, EntityId};
78

89
/// A utility to build an entity's components.
910
///
@@ -52,7 +53,7 @@ impl EntityBuilder {
5253
pub fn has<T: Component>(&self) -> bool {
5354
self.entries
5455
.iter()
55-
.any(|entry| entry.component_meta.type_id == ComponentTypeId::of::<T>())
56+
.any(|entry| entry.component_meta.type_id == TypeId::of::<T>())
5657
}
5758

5859
/// Spawns the entity builder into an `Ecs`.
@@ -100,18 +101,18 @@ mod tests {
100101
unsafe {
101102
let mut iter = builder.drain();
102103
let (meta, data) = iter.next().unwrap();
103-
assert_eq!(meta.type_id, ComponentTypeId::of::<i32>());
104+
assert_eq!(meta.type_id, TypeId::of::<i32>());
104105
assert_eq!(ptr::read_unaligned::<i32>(data.cast().as_ptr()), 10i32);
105106

106107
let (meta, data) = iter.next().unwrap();
107-
assert_eq!(meta.type_id, ComponentTypeId::of::<String>());
108+
assert_eq!(meta.type_id, TypeId::of::<String>());
108109
assert_eq!(
109110
ptr::read_unaligned::<String>(data.cast().as_ptr()),
110111
"a string"
111112
);
112113

113114
let (meta, data) = iter.next().unwrap();
114-
assert_eq!(meta.type_id, ComponentTypeId::of::<usize>());
115+
assert_eq!(meta.type_id, TypeId::of::<usize>());
115116
assert_eq!(ptr::read_unaligned::<usize>(data.cast().as_ptr()), 50usize);
116117

117118
assert!(iter.next().is_none());

0 commit comments

Comments
 (0)