-
Notifications
You must be signed in to change notification settings - Fork 220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
LazyUpdate: lazily insert and remove components, execute closures on world #221
Changes from 4 commits
e853998
0c81409
42d6ae4
13ceaec
16cc88e
941c6f1
6bd7693
66582b5
0e0db9c
746da1a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
use std::marker::PhantomData; | ||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
|
||
use crossbeam::sync::TreiberStack; | ||
|
@@ -368,7 +369,7 @@ impl Generation { | |
} | ||
|
||
/// A type implementing `LazyInsert` can be inserted | ||
/// using `LazyInsertions`. | ||
/// using `LazyUpdate`. | ||
pub trait LazyInsert: Send + Sync { | ||
/// Inserts the component(s) into the world. | ||
fn insert(self, world: &World); | ||
|
@@ -390,28 +391,49 @@ impl<L> LazyInsert for Vec<L> | |
} | ||
} | ||
|
||
trait LazyInsertInternal: Send + Sync { | ||
fn insert(self: Box<Self>, world: &World); | ||
struct LazyRemovalMarker<F>(PhantomData<F>); | ||
|
||
impl<F> LazyRemovalMarker<F> { | ||
pub fn new() -> Self { | ||
LazyRemovalMarker(PhantomData) | ||
} | ||
} | ||
|
||
struct LazyRemovalInfo<C> { | ||
entity: Entity, | ||
marker: LazyRemovalMarker<C>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we can actually inline that type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you mean using #[inline] on a get function (like with the Entity struct)? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, just use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah right, of course! |
||
} | ||
|
||
trait LazyUpdateInternal: Send + Sync { | ||
fn update(self: Box<Self>, world: &World); | ||
} | ||
|
||
impl<L> LazyInsertInternal for L | ||
impl<L> LazyUpdateInternal for L | ||
where L: LazyInsert | ||
{ | ||
fn insert(self: Box<Self>, world: &World) { | ||
fn update(self: Box<Self>, world: &World) { | ||
L::insert(*self, world); | ||
} | ||
} | ||
|
||
/// Lazy insertions can be used after creating a new | ||
impl<C> LazyUpdateInternal for LazyRemovalInfo<C> | ||
where C: Component + Send + Sync | ||
{ | ||
fn update(self: Box<Self>, world: &World) { | ||
world.write::<C>().remove(self.entity); | ||
} | ||
} | ||
|
||
/// Lazy updates can be used after creating a new | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd say in general, they can be used for stuff that should better happen at the end because it needs to borrow many resources. Dealing with initialization of components could be shown as an example of that. |
||
/// entity in a system. This way, none of the actual | ||
/// component storages have to be borrowed mutably. | ||
/// | ||
/// This resource is added to the world by default. | ||
pub struct LazyInsertions { | ||
stack: TreiberStack<Box<LazyInsertInternal>> | ||
pub struct LazyUpdate { | ||
stack: TreiberStack<Box<LazyUpdateInternal>>, | ||
} | ||
|
||
impl LazyInsertions { | ||
impl LazyUpdate { | ||
/// Adds an insertion. Please note that this method takes `&self` | ||
/// so there's no need to fetch it mutably. | ||
/// | ||
|
@@ -429,31 +451,45 @@ impl LazyInsertions { | |
/// struct InsertPos; | ||
/// | ||
/// impl<'a> System<'a> for InsertPos { | ||
/// type SystemData = (Entities<'a>, Fetch<'a, LazyInsertions>); | ||
/// type SystemData = (Entities<'a>, Fetch<'a, LazyUpdate>); | ||
/// | ||
/// fn run(&mut self, (ent, lazy): Self::SystemData) { | ||
/// let a = ent.create(); | ||
/// let b = ent.create(); | ||
/// | ||
/// lazy.add(vec![(a, Pos(3.0, 1.0)), (b, Pos(0.0, 4.0))]); | ||
/// lazy.add_insertions(vec![(a, Pos(3.0, 1.0)), (b, Pos(0.0, 4.0))]); | ||
/// } | ||
/// } | ||
/// ``` | ||
pub fn add<L>(&self, l: L) | ||
pub fn add_insertions<L>(&self, l: L) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe it would be better to do a "lazy insert" - so just |
||
where L: LazyInsert + 'static | ||
{ | ||
self.stack.push(Box::new(l)); | ||
} | ||
|
||
/// Adds a removal. Please note that this method takes `&self` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would also be nice to have a usage example somewhere. |
||
/// so there's no need to fetch it mutably. | ||
pub fn add_removal<C>(&self, e: Entity) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then this one would be just |
||
where | ||
C: Component + Send + Sync, | ||
{ | ||
self.stack.push(Box::new(LazyRemovalInfo { | ||
entity: e, | ||
marker: LazyRemovalMarker::<C>::new(), | ||
})); | ||
} | ||
} | ||
|
||
impl Default for LazyInsertions { | ||
impl Default for LazyUpdate { | ||
fn default() -> Self { | ||
// TODO: derive (`Default` is not yet implemented for `TreiberStack`) | ||
LazyInsertions { stack: TreiberStack::new() } | ||
LazyUpdate { | ||
stack: TreiberStack::new(), | ||
} | ||
} | ||
} | ||
|
||
impl Drop for LazyInsertions { | ||
impl Drop for LazyUpdate { | ||
fn drop(&mut self) { | ||
// TODO: remove as soon as leak is fixed in crossbeam | ||
while self.stack.pop().is_some() {} | ||
|
@@ -714,16 +750,16 @@ impl World { | |
/// and deleted entities into the persistent generations vector. | ||
/// Also removes all the abandoned components. | ||
/// | ||
/// Additionally, `LazyInsertions` will be merged. | ||
/// Additionally, `LazyUpdate` will be merged. | ||
pub fn maintain(&mut self) { | ||
let deleted = self.entities_mut().alloc.merge(); | ||
self.delete_components(&deleted); | ||
|
||
let mut lazy_insertions = self.write_resource::<LazyInsertions>(); | ||
let lazy = &mut lazy_insertions.stack; | ||
let mut lazy_update = self.write_resource::<LazyUpdate>(); | ||
let lazy = &mut lazy_update.stack; | ||
|
||
while let Some(l) = lazy.pop() { | ||
l.insert(&*self); | ||
l.update(&*self); | ||
} | ||
} | ||
|
||
|
@@ -742,7 +778,7 @@ impl Default for World { | |
fn default() -> Self { | ||
let mut res = Resources::new(); | ||
res.add(EntitiesRes::default()); | ||
res.add(LazyInsertions::default()); | ||
res.add(LazyUpdate::default()); | ||
|
||
World { | ||
res: res, | ||
|
@@ -770,13 +806,28 @@ mod tests { | |
let e; | ||
{ | ||
let entities = world.read_resource::<EntitiesRes>(); | ||
let lazy = world.read_resource::<LazyInsertions>(); | ||
let lazy = world.read_resource::<LazyUpdate>(); | ||
|
||
e = entities.create(); | ||
lazy.add((e, Pos)); | ||
lazy.add_insertions((e, Pos)); | ||
} | ||
|
||
world.maintain(); | ||
assert!(world.read::<Pos>().get(e).is_some()); | ||
} | ||
|
||
#[test] | ||
fn lazy_removal() { | ||
let mut world = World::new(); | ||
world.register::<Pos>(); | ||
|
||
let e = world.create_entity().with(Pos).build(); | ||
{ | ||
let lazy = world.read_resource::<LazyUpdate>(); | ||
lazy.add_removal::<Pos>(e); | ||
} | ||
|
||
world.maintain(); | ||
assert!(world.read::<Pos>().get(e).is_none()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just have a struct containing both the entity and the marker?