Skip to content

Commit 0571fbe

Browse files
committed
implement subset locking
This has required some substantial changes to the lifetimes on the pipeline and for_each implementations. Unfortunately, my abuses of the type system have unearthed a compiler bug in the form of rust-lang/rust#33364, which is currently blocking users from calling Query::for_each with a closure that destructures the data tuple in the arguments list. Instead, they have to destructure it with a "let" binding within the closure body. This is less ergonomic but acceptable.
1 parent 4164807 commit 0571fbe

File tree

2 files changed

+30
-25
lines changed

2 files changed

+30
-25
lines changed

src/ecs/mod.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ use std::collections::VecDeque;
44
use std::marker::PhantomData;
55
use std::ops::Deref;
66

7-
use self::set::*;
87
use self::query::*;
98

109
const ID_BITS: usize = 24;
@@ -13,6 +12,8 @@ const MIN_UNUSED: usize = 1024;
1312
pub mod query;
1413
pub mod set;
1514

15+
pub use self::set::Set;
16+
1617
/// A component is a piece of raw data which is associated with an entity.
1718
///
1819
/// "Systems" will typically iterate over all entities with a specific set of components,
@@ -297,7 +298,7 @@ impl<'a, S: 'a + Set> WorldHandle<'a, S> {
297298
///
298299
/// # Examples
299300
/// ```
300-
/// use snorkium::ecs::*;
301+
/// # use snorkium::ecs::*;
301302
/// #[derive(Clone, Copy)]
302303
/// struct Position(f32, f32);
303304
/// #[derive(Clone, Copy)]
@@ -310,7 +311,10 @@ impl<'a, S: 'a + Set> WorldHandle<'a, S> {
310311
/// impl System for DotSystem {
311312
/// // draw a dot for each entity with a position and the zero-sized dot component.
312313
/// fn process<'a, S: 'a + Set>(&mut self, wh: WorldHandle<'a, S>) {
313-
/// wh.query::<(Position, Dot)>().for_each(|e, (p, d)| {
314+
/// // when https://github.com/rust-lang/rust/issues/33364 is fixed,
315+
/// // you'll be able to match on the tuple inside the closure arguments list.
316+
/// wh.query::<(Position, Dot)>().for_each(|e, item| {
317+
/// let (p, _) = item;
314318
/// draw_dot(p);
315319
/// });
316320
/// }

src/ecs/query.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::marker::PhantomData;
22

33
use super::*;
4-
use super::set::{Set, LockedSubset};
4+
use super::set::{LockGroup, LockedSubset};
55

66
/// Filters are used to test properties of entities' data.
77
///
@@ -40,13 +40,14 @@ impl<T: Component> Filter for Has<T> {
4040
/// and `Push`.
4141
pub trait Pipeline<'a>: Sized {
4242
type Item: 'a;
43+
type LockGroup: for<'b> LockGroup<'b>;
4344

4445
/// Consume self along with handles to ECS state to pass all entities
4546
/// fulfilling the pipeline's predicates to the functions along with
4647
/// relevant component data. This will output a vector of the returned
4748
/// outputs from the function.
48-
fn for_each<F, U: Send, S: LockedSubset>(self, &S, &EntityManager, F) -> Vec<U>
49-
where F: Sync + for <'b> Fn(VerifiedEntity, <Self as Pipeline<'b>>::Item) -> U;
49+
fn for_each<F, U: Send, S: LockedSubset>(self, &'a S, &'a EntityManager, F) -> Vec<U>
50+
where F: Sync + Fn(VerifiedEntity, Self::Item) -> U;
5051
}
5152

5253
/// Convenience trait for extending tuples of filters.
@@ -75,7 +76,7 @@ pub struct Query<'a, S: Set + 'a, P: 'a> {
7576
pipeline: P,
7677
}
7778

78-
impl<'a, S: 'a + Set, P: 'a + Pipeline<'a>> Query<'a, S, P> {
79+
impl<'a, S: 'a + Set, P> Query<'a, S, P> where P: 'a + for<'b> Pipeline<'b> {
7980
/// Create a new query. Use of `WorldHandle::query()` is advised
8081
/// over this.
8182
pub fn new(s: &'a S, entities: &'a EntityManager, pipeline: P) -> Self {
@@ -113,11 +114,9 @@ impl<'a, S: 'a + Set, P: 'a + Pipeline<'a>> Query<'a, S, P> {
113114
/// Perform an action for each entity which fits the properties of
114115
/// the filter.
115116
pub fn for_each<F, U: Send>(self, f: F) -> Vec<U>
116-
where F: Sync + for<'b> Fn(VerifiedEntity, <P as Pipeline<'b>>::Item) -> U {
117-
// TODO: amend Pipeline so that we can get a LockGroup to pass to lock_subset.
118-
// TODO: have for_each return the locked subset along with the items.
119-
let empty = ::ecs::set::Empty;
120-
self.pipeline.for_each(&empty, self.entities, f)
117+
where F: Sync + for<'r> Fn(VerifiedEntity, <P as Pipeline<'r>>::Item) -> U {
118+
let subset = self.set.lock_subset::<P::LockGroup>();
119+
self.pipeline.for_each(&subset, self.entities, f)
121120
}
122121
}
123122

@@ -163,10 +162,10 @@ macro_rules! factory {
163162
};
164163
}
165164

166-
// factory!(A B C D E F);
167-
// factory!(A B C D E);
168-
// factory!(A B C D);
169-
// factory!(A B C);
165+
factory!(A B C D E F);
166+
factory!(A B C D E);
167+
factory!(A B C D);
168+
factory!(A B C);
170169
factory!(A B);
171170
factory!(A);
172171
factory!();
@@ -208,9 +207,10 @@ macro_rules! pipeline_impl {
208207
() => {
209208
impl<'a> Pipeline<'a> for () {
210209
type Item = ();
210+
type LockGroup = ();
211211

212-
fn for_each<F, U: Send, S: LockedSubset>(self, _: &S, _: &EntityManager, _: F) -> Vec<U>
213-
where F: 'a + Sync + for<'b> Fn(VerifiedEntity, <Self as Pipeline<'b>>::Item) -> U {
212+
fn for_each<F, U: Send, S: LockedSubset>(self, _: &'a S, _: &'a EntityManager, _: F) -> Vec<U>
213+
where F: 'a + Sync + Fn(VerifiedEntity, Self::Item) -> U {
214214
Vec::new()
215215
}
216216
}
@@ -219,11 +219,12 @@ macro_rules! pipeline_impl {
219219
($f_id: ident $f_num: tt $($id: ident $num: tt)*) => {
220220
impl<'a, $f_id: Filter, $($id: Filter,)*> Pipeline<'a> for
221221
($f_id, $($id,)*) {
222-
type Item = (&'a <$f_id as Filter>::Component, $(&'a <$id as Filter>::Component,)*);
222+
type Item = (&'a $f_id::Component, $(&'a $id::Component,)*);
223+
type LockGroup = ($f_id::Component, $($id::Component,)*);
223224

224225
#[allow(unused_mut)]
225-
fn for_each<OP, U: Send, SET: LockedSubset>(self, set: &SET, entities: &EntityManager, f: OP) -> Vec<U>
226-
where OP: 'a + Sync + for<'b> Fn(VerifiedEntity, <Self as Pipeline<'b>>::Item) -> U {
226+
fn for_each<OP, U: Send, SET: LockedSubset>(self, set: &'a SET, entities: &'a EntityManager, f: OP) -> Vec<U>
227+
where OP: 'a + Sync + Fn(VerifiedEntity, Self::Item) -> U {
227228
// it's ok to unwrap the calls to get_storage() since this function is called with a subset
228229
// that has been locked with this pipeline in mind.
229230

@@ -253,10 +254,10 @@ macro_rules! pipeline_impl {
253254
};
254255
}
255256

256-
// pipeline_impl!(A 0 B 1 C 2 D 3 E 4 F 5);
257-
// pipeline_impl!(A 0 B 1 C 2 D 3 E 4);
258-
// pipeline_impl!(A 0 B 1 C 2 D 3);
259-
// pipeline_impl!(A 0 B 1 C 2);
257+
pipeline_impl!(A 0 B 1 C 2 D 3 E 4 F 5);
258+
pipeline_impl!(A 0 B 1 C 2 D 3 E 4);
259+
pipeline_impl!(A 0 B 1 C 2 D 3);
260+
pipeline_impl!(A 0 B 1 C 2);
260261
pipeline_impl!(A 0 B 1);
261262
pipeline_impl!(A 0);
262263
pipeline_impl!();

0 commit comments

Comments
 (0)