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
Merged

LazyUpdate: lazily insert and remove components, execute closures on world #221

merged 10 commits into from
Jul 13, 2017

Conversation

eldyer
Copy link
Contributor

@eldyer eldyer commented Jul 9, 2017

Remove a component lazily without having to borrow its storage. To be used like this:

let lazy = world.read_resource::<LazyUpdate>();
lazy.add_removal::<SomeComponentType>(some_entity);

@kvark
Copy link
Member

kvark commented Jul 10, 2017

Looks ok to me. Thanks for the PR!

@torkleyy torkleyy self-requested a review July 10, 2017 14:23
Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! I'd prefer not to have a second struct actually, see below.

src/world.rs Outdated
/// component storages have to be borrowed mutably.
///
/// This resource is added to the world by default.
pub struct LazyRemovals {
Copy link
Member

@torkleyy torkleyy Jul 10, 2017

Choose a reason for hiding this comment

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

There is no reason to have a second struct, actually. Given that LazyInsertions haven't been released yet, let's just rename it to LazyUpdate and have a method there.

src/world.rs Outdated
pub struct LazyInsertions {
stack: TreiberStack<Box<LazyInsertInternal>>
pub struct LazyUpdate {
insert_stack: TreiberStack<Box<LazyInsertInternal>>,
Copy link
Member

Choose a reason for hiding this comment

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

Let's also rename LazyInsertInternal to LazyUpdateInternal so we do not need two stacks.

Copy link
Member

Choose a reason for hiding this comment

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

To elaborate this a bit, LazyInsertInternal can just access the World, so we can just use it for both (after renaming). Just accept entities and a type parameter then, have a private struct with PhantomData and implement the removal, please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, I will try!

@eldyer eldyer changed the title Add LazyRemovals LazyUpdate for lazy component insertions and removals Jul 11, 2017
Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Looks pretty good! I'd actually prefer to not have a tuple for removals, but that's up to you. I'll review it completely later.

src/world.rs Outdated
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?

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Sorry - have to do some more nitpicking, I actually wonder if we should just allow lazy.execute(|world| world.hello()), additionally. But that could also be done in another PR.

src/world.rs Outdated

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!

src/world.rs Outdated
}
}

/// 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.

src/world.rs Outdated
/// }
/// }
/// ```
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.

src/world.rs Outdated

/// Adds a removal. Please note that this method takes `&self`
/// 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.

src/world.rs Outdated
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.

@eldyer
Copy link
Contributor Author

eldyer commented Jul 12, 2017

Thanks for your feedback and no need to apologize, I agree with all points and I'm working on it!

@eldyer
Copy link
Contributor Author

eldyer commented Jul 12, 2017

Also implemented execute. Tell me what you think, I'm happy to get your feedback.

src/world.rs Outdated
/// entity in a system. This way, none of the actual
/// component storages have to be borrowed mutably.
///
impl LazyUpdateInternal for Box<Fn(&World) + Send + Sync> {
Copy link
Member

Choose a reason for hiding this comment

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

Do we really need Box 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.

Not sure, I tried without that Box first, but ran into problems. The compiler suggests to add a 'static bound to F in execute, but the passed in closures apparently don't have 'static lifetime. Any idea how to solve this?

Copy link
Member

Choose a reason for hiding this comment

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

Did you try impl<F> LazyUpdateInternal for F where F: Fn(&World) + Send + Sync + 'static?

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, works!

src/world.rs Outdated
self.stack.push(Box::new(LazyRemovalInfo::<C>(e, PhantomData)));
}

/// Lazily executes a closure with world access.
Copy link
Member

Choose a reason for hiding this comment

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

Could also say that "lazily" means "when calling maintain()".

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Thanks for your work. I'm wondering if we need the LazyInsert and the LazyRemoval now that we have execute.

src/world.rs Outdated
where
C: Component + Send + Sync,
{
self.stack.push(Box::new(LazyRemovalInfo::<C>(e, PhantomData)));
Copy link
Member

Choose a reason for hiding this comment

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

Actually, this could just use execute now. That way, there's no need for the removal struct.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice!

src/world.rs Outdated
/// }
/// }
/// ```
pub fn add<L>(&self, l: L)
pub fn insert<L>(&self, l: L)
Copy link
Member

Choose a reason for hiding this comment

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

We could probably divide this method into insert and insert_all to increase performance for multiple insertions; insert_all could just accept an iterator.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed, will do that.
It would be nice if we could use execute in insert as well, to get rid of LazyInsert. But I think then FnOnce would be required, because the component would have to be moved into the closure, right?

Copy link
Member

Choose a reason for hiding this comment

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

Yes. I'm not sure, however, if executing call_once on Box<FnOnce is permitted / if there's a workaround.

@eldyer
Copy link
Contributor Author

eldyer commented Jul 12, 2017

Could you take a look? Still needs cleanup, docs and examples, just wanted to get some feedback at this point.

@eldyer
Copy link
Contributor Author

eldyer commented Jul 12, 2017

Oops, forgot to remove LazyInsert..

@eldyer
Copy link
Contributor Author

eldyer commented Jul 12, 2017

Hm, would it make sense to renameLazyUpdate into LazyExecute or LazyExecution?

src/world.rs Outdated
self.stack.push(Box::new(
Box::new(f) as Box<Fn(&World) + Send + Sync>)
);
self.stack.push(Box::new(f));
Copy link
Member

Choose a reason for hiding this comment

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

Much better 😉

Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Okay, we're nearly there. Just the few nits below - and please don't forget to update the changelog. Since this partially amends my PR, let's summarize it on one line and mention both PRs in brackets.

Really appreciating your efforts 👍

src/world.rs Outdated
fn insert(self: Box<Self>, world: &World) {
L::insert(*self, world);
impl<F> LazyUpdateInternal for F
where F: FnOnce(&World) + Send + Sync + 'static {
Copy link
Member

Choose a reason for hiding this comment

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

Formatting: { is supposed to be on a new line.

src/world.rs Outdated
pub struct LazyInsertions {
stack: TreiberStack<Box<LazyInsertInternal>>
/// Please note that the provided methods take `&self`
/// so there's no need to fetch `LazyUpdate` mutably.
Copy link
Member

Choose a reason for hiding this comment

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

Let's leave the note that it is added to the world by default.

src/world.rs Outdated
}
});
}
///
Copy link
Member

Choose a reason for hiding this comment

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

Missing new line, docs and example usage.

@eldyer eldyer changed the title LazyUpdate for lazy component insertions and removals LazyUpdate: lazily insert and remove components, execute closures on world Jul 13, 2017
Copy link
Member

@torkleyy torkleyy left a comment

Choose a reason for hiding this comment

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

Thank you! These are great changes 👍

@torkleyy
Copy link
Member

I guess we can merge this now.

bors r+

bors bot added a commit that referenced this pull request Jul 13, 2017
221: LazyUpdate: lazily insert and remove components, execute closures on world r=torkleyy

Remove a component lazily without having to borrow its storage. To be used like this:

`let lazy = world.read_resource::<LazyUpdate>();`
`lazy.add_removal::<SomeComponentType>(some_entity);`
@bors
Copy link
Contributor

bors bot commented Jul 13, 2017

Canceled

@torkleyy
Copy link
Member

Hold on, rebase is wrong

bors r-

@eldyer
Copy link
Contributor Author

eldyer commented Jul 13, 2017

Yes I was just about to fix the Changelog...

@torkleyy
Copy link
Member

The line in the changelog should be in the 0.9.3 section

@eldyer
Copy link
Contributor Author

eldyer commented Jul 13, 2017

Yeah just committed... 😄

@torkleyy
Copy link
Member

Okay, now it's perfect.

bors r+

bors bot added a commit that referenced this pull request Jul 13, 2017
221: LazyUpdate: lazily insert and remove components, execute closures on world r=torkleyy

Remove a component lazily without having to borrow its storage. To be used like this:

`let lazy = world.read_resource::<LazyUpdate>();`
`lazy.add_removal::<SomeComponentType>(some_entity);`
@eldyer
Copy link
Contributor Author

eldyer commented Jul 13, 2017

Thank you by the way for guiding me through this. It's actually my first PR and I learned a lot of things. I totally like that you want to get all the details right... it's a great thing to learn from such talented developers! 👍

@bors
Copy link
Contributor

bors bot commented Jul 13, 2017

Build succeeded

@bors bors bot merged commit 746da1a into amethyst:master Jul 13, 2017
@eldyer eldyer deleted the lazy_remove branch July 16, 2017 12:12
/// ```
pub fn execute<F>(&self, f: F)
where
F: FnOnce(&World) + 'static + Send + Sync,

Choose a reason for hiding this comment

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

Why is there a Sync requirement here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants