Skip to content

Commit f5ec9e0

Browse files
committed
ecs: Working implementation of static queries
It took some wrestling with lifetimes, but static queries now work. They have roughly the same interface as `hecs`.
1 parent 5f75dab commit f5ec9e0

File tree

3 files changed

+138
-50
lines changed

3 files changed

+138
-50
lines changed

quill/ecs/src/ecs.rs

Lines changed: 60 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use std::{any::type_name, iter};
1+
use std::{any::type_name, borrow::Cow, iter, marker::PhantomData};
22

33
use ahash::AHashMap;
44
use itertools::Either;
@@ -8,6 +8,7 @@ use crate::{
88
component::{Component, ComponentMeta},
99
entity::{Entities, EntityId},
1010
entity_builder::EntityBuilder,
11+
query::{QueryDriverIter, QueryTuple},
1112
storage::SparseSetStorage,
1213
QueryDriver,
1314
};
@@ -140,36 +141,75 @@ impl Ecs {
140141
Ok(())
141142
}
142143

143-
/*
144144
/// Queries for all entities that have the given set of components.
145145
///
146146
/// Returns an iterator over tuples of `(entity, components)`.
147-
pub fn query<'a, Q: QueryTuple>(
148-
&'a self,
149-
) -> impl Iterator<Item = (EntityId, Q::Output<'a>)> + 'a
150-
where
151-
Q::Output<'a>: 'a,
152-
{
153-
let sparse_sets = match Q::sparse_sets(&self.components) {
154-
Some(s) => s,
155-
None => return Either::Left(iter::empty()),
156-
};
157-
let sparse_set_refs: Vec<_> = sparse_sets.iter().map(|s| s.to_ref()).collect();
147+
pub fn query<'w, 'q, Q: QueryTuple<'w>>(&'w self) -> Query<'w, 'q, Q> {
148+
let sparse_sets = Q::sparse_sets(&self.components).unwrap_or_else(|| todo!());
149+
let sparse_set_refs: Vec<_> = sparse_sets.iter().map(|set| set.to_ref()).collect();
158150
let dense_indices = Q::dense_indices();
159151

160-
let driver = QueryDriver::new(&sparse_set_refs, &dense_indices);
152+
let driver = QueryDriver::new(Cow::Owned(sparse_set_refs), Cow::Owned(dense_indices));
161153

162-
Either::Right(driver.iter().map(move |item| {
163-
let components = unsafe { Q::make_output(&sparse_sets, item.dense_indices) };
164-
let entity = self.entities.get(item.sparse_index);
165-
(entity, components)
166-
}))
154+
Query {
155+
driver,
156+
sparse_sets,
157+
entities: &self.entities,
158+
_marker: PhantomData,
159+
}
167160
}
168-
*/
169161

170162
fn check_entity(&self, entity: EntityId) -> Result<(), EntityDead> {
171163
self.entities
172164
.check_generation(entity)
173165
.map_err(|_| EntityDead)
174166
}
175167
}
168+
169+
/// An iterator over a statically-typed query.
170+
///
171+
/// Call [`iter`] to iterate over the items.
172+
pub struct Query<'w, 'q, Q> {
173+
driver: QueryDriver<'w, 'q>,
174+
sparse_sets: Vec<&'w SparseSetStorage>,
175+
entities: &'w Entities,
176+
_marker: PhantomData<Q>,
177+
}
178+
179+
impl<'w, 'q, Q> Query<'w, 'q, Q>
180+
where
181+
Q: QueryTuple<'w>,
182+
{
183+
pub fn iter(&'q mut self) -> QueryIter<'w, 'q, Q> {
184+
QueryIter {
185+
driver: self.driver.iter(),
186+
sparse_sets: &self.sparse_sets,
187+
entities: self.entities,
188+
_marker: self._marker,
189+
}
190+
}
191+
}
192+
193+
pub struct QueryIter<'w, 'q, Q> {
194+
driver: QueryDriverIter<'w, 'q>,
195+
sparse_sets: &'q [&'w SparseSetStorage],
196+
entities: &'w Entities,
197+
_marker: PhantomData<Q>,
198+
}
199+
200+
impl<'w, 'q, Q> Iterator for QueryIter<'w, 'q, Q>
201+
where
202+
Q: QueryTuple<'w>,
203+
{
204+
type Item = (EntityId, Q::Output);
205+
206+
fn next(&mut self) -> Option<Self::Item> {
207+
let item = self.driver.next()?;
208+
209+
let components = unsafe { Q::make_output(self.sparse_sets, item.dense_indices) };
210+
211+
let entity = self.entities.get(item.sparse_index);
212+
213+
Some((entity, components))
214+
}
215+
}

quill/ecs/src/query.rs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
//! Dynamic query infrastructure.
22
3-
use std::{any::TypeId, cell::Cell, ops::Deref};
3+
use std::{any::TypeId, borrow::Cow, cell::Cell, ops::Deref};
44

55
use crate::{storage::sparse_set, Component, Components, Ecs, SparseSetRef, SparseSetStorage};
66

77
/// Drives a query by yielding the entities
88
/// whose components satisfy the query parameters.
99
pub struct QueryDriver<'w, 'q> {
1010
/// A sparse set for each component in the query.
11-
sparse_sets: &'q [SparseSetRef<'w>],
11+
sparse_sets: Cow<'q, [SparseSetRef<'w>]>,
1212

1313
/// The "lead" sparse set, chosen as the set with
1414
/// the smallest number of components.
15-
lead: SparseSetRef<'q>,
15+
lead: SparseSetRef<'w>,
1616

1717
/// Used as the yielded value for the iterator.
1818
/// (We can't own this because of the lack of GATs.)
19-
dense_indices: &'q [Cell<u32>],
19+
dense_indices: Cow<'q, [Cell<u32>]>,
2020
}
2121

2222
impl<'w, 'q> QueryDriver<'w, 'q> {
@@ -27,15 +27,19 @@ impl<'w, 'q> QueryDriver<'w, 'q> {
2727
///
2828
/// # Panics
2929
/// Panics if `sparse_sets.len() != dense_indices.len()`.
30-
pub fn new(sparse_sets: &'q [SparseSetRef<'w>], dense_indices: &'q [Cell<u32>]) -> Self {
30+
pub fn new(
31+
sparse_sets: Cow<'q, [SparseSetRef<'w>]>,
32+
dense_indices: Cow<'q, [Cell<u32>]>,
33+
) -> Self {
3134
let lead = sparse_sets
3235
.iter()
3336
.min_by_key(|set| set.len())
34-
.unwrap_or(SparseSetRef::empty());
37+
.copied()
38+
.unwrap_or(*SparseSetRef::empty());
3539

3640
Self {
3741
sparse_sets,
38-
lead: *lead,
42+
lead,
3943
dense_indices,
4044
}
4145
}
@@ -60,10 +64,8 @@ pub struct QueryDriverIter<'w, 'q> {
6064
lead_iter: sparse_set::Iter<'q>,
6165
}
6266

63-
impl<'w, 'q> Iterator for QueryDriverIter<'w, 'q> {
64-
type Item = QueryItem<'q>;
65-
66-
fn next(&mut self) -> Option<Self::Item> {
67+
impl<'w, 'q> QueryDriverIter<'w, 'q> {
68+
pub fn next(&mut self) -> Option<QueryItem> {
6769
loop {
6870
let (sparse_index, lead_dense_index) = self.lead_iter.next()?;
6971

@@ -98,36 +100,35 @@ pub struct QueryItem<'q> {
98100

99101
// -- Static queries
100102

101-
/*
102103
/// A typed query element.
103-
pub trait QueryParameter {
104-
type Output;
104+
pub trait QueryParameter<'a> {
105+
type Output: 'a;
105106
type Component: Component;
106107

107108
unsafe fn get_unchecked_by_dense_index(
108-
storage: &SparseSetStorage,
109+
storage: &'a SparseSetStorage,
109110
dense_index: u32,
110111
) -> Self::Output;
111112
}
112113

113-
impl<'a, T> QueryParameter for &'a T
114+
impl<'a, T> QueryParameter<'a> for &'a T
114115
where
115116
T: Component,
116117
{
117-
type Output<'b> = &'b T;
118+
type Output = &'a T;
118119
type Component = T;
119120

120121
unsafe fn get_unchecked_by_dense_index(
121-
storage: &SparseSetStorage,
122+
storage: &'a SparseSetStorage,
122123
dense_index: u32,
123-
) -> Self::Output<'_> {
124+
) -> Self::Output {
124125
storage.get_unchecked_by_dense_index(dense_index)
125126
}
126127
}
127128

128129
/// A tuple of query parameters.
129-
pub trait QueryTuple {
130-
type Output<'s>;
130+
pub trait QueryTuple<'a> {
131+
type Output: 'a;
131132

132133
// avoiding allocations here is blocked on const generics and/or GATs
133134
fn sparse_sets(components: &Components) -> Option<Vec<&SparseSetStorage>>;
@@ -141,16 +142,16 @@ pub trait QueryTuple {
141142
/// `dense_indices` and `sparse_sets` must have a length equal
142143
/// to the length of the vectors returned by the corresponding methods
143144
/// of this trait.
144-
unsafe fn make_output<'s>(
145-
sparse_sets: &'s [&'s SparseSetStorage],
145+
unsafe fn make_output(
146+
sparse_sets: &[&'a SparseSetStorage],
146147
dense_indices: &[Cell<u32>],
147-
) -> Self::Output<'s>;
148+
) -> Self::Output;
148149
}
149150

150151
macro_rules! query_tuple_impl {
151152
($count:literal, $(($ty:ident, $index:literal)),* $(,)?) => {
152-
impl <$($ty: QueryParameter),*> QueryTuple for ($($ty),*) {
153-
type Output<'s> = ($($ty::Output<'s>),*);
153+
impl <'a, $($ty: QueryParameter<'a>),*> QueryTuple<'a> for ($($ty),*) {
154+
type Output = ($($ty::Output),*);
154155

155156
fn sparse_sets(components: &Components) -> Option<Vec<&SparseSetStorage>> {
156157
Some(vec![
@@ -164,10 +165,10 @@ macro_rules! query_tuple_impl {
164165
vec![Cell::new(0); $count]
165166
}
166167

167-
unsafe fn make_output<'s>(
168-
sparse_sets: &'s [&'s SparseSetStorage],
168+
unsafe fn make_output(
169+
sparse_sets: &[&'a SparseSetStorage],
169170
dense_indices: &[Cell<u32>],
170-
) -> Self::Output<'s> {
171+
) -> Self::Output {
171172
(
172173
$(
173174
$ty::get_unchecked_by_dense_index(sparse_sets.get_unchecked($index), dense_indices.get_unchecked($index).get())
@@ -179,4 +180,4 @@ macro_rules! query_tuple_impl {
179180
}
180181

181182
query_tuple_impl!(1, (T1, 0));
182-
*/
183+
query_tuple_impl!(2, (T1, 0), (T2, 1));

quill/ecs/tests/ecs.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::collections::HashMap;
2+
13
use quill_ecs::*;
24

35
#[test]
@@ -83,3 +85,48 @@ fn remove_nonexisting() {
8385
let entity = ecs.spawn_bundle((10i32,));
8486
assert!(ecs.remove::<usize>(entity).is_err());
8587
}
88+
89+
#[test]
90+
fn query_basic() {
91+
let mut ecs = Ecs::new();
92+
93+
let entity1 = ecs.spawn_bundle((10i32, "name1"));
94+
let entity2 = ecs.spawn_bundle((15i32, "name2", 50.0f32));
95+
96+
let mut query = ecs.query::<(&i32, &&'static str)>();
97+
let mut iter = query.iter();
98+
99+
assert_eq!(iter.next(), Some((entity1, (&10, &"name1"))));
100+
assert_eq!(iter.next(), Some((entity2, (&15, &"name2"))));
101+
assert_eq!(iter.next(), None);
102+
}
103+
104+
#[test]
105+
fn query_big_ecs_after_despawn() {
106+
let mut ecs = Ecs::new();
107+
108+
let mut entities = Vec::new();
109+
for i in 0..10_000usize {
110+
let mut builder = EntityBuilder::new();
111+
if i % 3 == 0 {
112+
builder.add(format!("entity #{}", i));
113+
}
114+
builder.add(i);
115+
let entity = builder.spawn_into(&mut ecs);
116+
if i % 3 == 0 {
117+
entities.push(entity);
118+
}
119+
}
120+
121+
let last = entities.len() - 1;
122+
ecs.despawn(entities.remove(last)).unwrap();
123+
124+
let queried: HashMap<EntityId, (&String, &usize)> =
125+
ecs.query::<(&String, &usize)>().iter().collect();
126+
127+
for (i, entity) in entities.iter().copied().enumerate() {
128+
assert_eq!(queried[&entity], (&format!("entity #{}", i * 3), &(i * 3)));
129+
}
130+
131+
assert_eq!(queried.len(), entities.len());
132+
}

0 commit comments

Comments
 (0)