Skip to content
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

Merged
merged 10 commits into from
Jul 13, 2017
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ pub use storage::{BTreeStorage, CheckStorage, DenseVecStorage, DistinctStorage,
HashMapStorage, InsertResult, NullStorage, ReadStorage, Storage,
UnprotectedStorage, VecStorage, WriteStorage};
pub use world::{Component, CreateIter, CreateIterAtomic, EntitiesRes, Entity, EntityBuilder,
Generation, LazyInsert, LazyInsertions, World};
Generation, LazyInsert, LazyUpdate, World};

#[cfg(feature = "serialize")]
pub use storage::{MergeError, PackedData};
Expand Down
95 changes: 73 additions & 22 deletions src/world.rs
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;
Expand Down Expand Up @@ -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);
Expand All @@ -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> {
Copy link
Member

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?

pub fn new() -> Self {
LazyRemovalMarker(PhantomData)
}
}

struct LazyRemovalInfo<C> {
entity: Entity,
marker: LazyRemovalMarker<C>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can actually inline that type.

Copy link
Contributor Author

@eldyer eldyer Jul 12, 2017

Choose a reason for hiding this comment

The 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)?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just use PhantomData here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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
Copy link
Member

Choose a reason for hiding this comment

The 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.
///
Expand All @@ -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)
Copy link
Member

@torkleyy torkleyy Jul 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to do a "lazy insert" - so just pub fn insert? Also, the plural is not always applicable.

where L: LazyInsert + 'static
{
self.stack.push(Box::new(l));
}

/// Adds a removal. Please note that this method takes `&self`
Copy link
Member

Choose a reason for hiding this comment

The 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then this one would be just remove.

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() {}
Expand Down Expand Up @@ -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);
}
}

Expand All @@ -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,
Expand Down Expand Up @@ -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());
}
}