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

FlaggedStorage #93

Merged
merged 3 commits into from
Jun 6, 2017
Merged

FlaggedStorage #93

merged 3 commits into from
Jun 6, 2017

Conversation

Aceeri
Copy link
Member

@Aceeri Aceeri commented Jan 20, 2017

A basic implementation for a TrackedStorage (#89).

This version of the TrackedStorage does not implement a way to compare the past version and the modifies version of the components. Usage requires unwrapping the storage to the base TrackedStorage which implements Join for flagged components.

Alternative design choices:

Implement TrackedStorage similar to how MaskedStorage works.

Pros:

  • Would make it possible to have the Join of the storage only work on the flagged components without calling any additional methods.
  • Might open up a path for a generic storage wrapper like UnprotectedStorage but instead as a container for how the Join gets the components necessary for the loop (this could be interesting since it would allow for different methods other than bitsets, but would also be pretty hard to get right).

Cons:

  • Difficult to get all the components instead of just the flagged ones as there is no current way to directly get access to the container storage.
  • Might be unintuitive at a glance as you would expect to get all the components when joining the storage.

Compare modifications for more accurate flags

Pros:

  • Removes a large concern for if the user iterates over a mutable tracked storage.

Cons:

  • Performance benefits of the tracked storage decreases.
  • Difficult to implement. Requires a way to directly compare the new "modified" version with the past version.

@kvark kvark self-requested a review January 20, 2017 20:42
@kvark kvark requested a review from a user February 6, 2017 01:20
Copy link
Member

@kvark kvark left a comment

Choose a reason for hiding this comment

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

Sorry if this has already been discussed (and I forgot), but I feel that comps.get_mut(entity) is something we can do better at

src/storage.rs Outdated
/// // Instead do something like:
/// for (entity, comp) in (&entities, &comps.check()).iter() {
/// if true { // check whether you should modify this component or not.
/// let mut comp = comps.get_mut(entity);
Copy link
Member

Choose a reason for hiding this comment

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

Hmm, we should be able to make this nicer. Asking for get_mut on an entity that we are currently iterating on would be slower than just giving the value right away. Perhaps, the check() could iterate over some nice wrapper aka std::hashmap::Entry, so that actually accessing the data becomes straightforward (in terms of performance)?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm assuming the main problem is just that calling get_mut means it is checking again whether the entity has the component, even though we already know?

src/storage.rs Outdated

impl<C: Component, T: UnprotectedStorage<C>> TrackedStorage<C, T> {
/// All components will be cleared of being flagged.
pub fn clear(&mut self) {
Copy link
Member

Choose a reason for hiding this comment

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

needs to be renamed to clear_flags

@Aceeri
Copy link
Member Author

Aceeri commented Feb 6, 2017

Also, this runs into the same problem as the Join implementation for a &'a mut Storage. Maybe it might be better to have something like a Join and JoinMut?

@kvark
Copy link
Member

kvark commented Feb 6, 2017

I'm assuming the main problem is just that calling get_mut means it is checking again whether the entity has the component, even though we already know?

Yes.

Maybe it might be better to have something like a Join and JoinMut?

I don't think JoinMut would solve that. It would require all the iterated things to be mutable, while we need to mix and match them.

src/storage.rs Outdated
}
unsafe fn get_mut(&mut self, id: Index) -> &mut C {
// calling `.iter()` on a mutable reference to the storage will flag everything
self.mask.add(id);
Copy link
Member

Choose a reason for hiding this comment

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

I imagine that such a TrackedStorage would be best used in the following way:

// pos is a component storage with a `TrackedStorage`
for (vel, pos) in (&vel, &mut pos).join() {
    *pos += vel * delta_time; 
}

In another system:

for (pos_handle, matrix) in (pos.check(), &mut mvp).join() {
    if pos.flagged(pos_handle) {
        // Recalc
    } else {
        // Use cached version
    }
}

It's inconvenient to do whatever checks and often too complicated, instead we might want a method on UnprotectedStorage which is called after every iterated component, where we check for equality. This would also solve the lifetime problem of a guard.

Copy link
Member Author

@Aceeri Aceeri Jun 5, 2017

Choose a reason for hiding this comment

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

What would a cached version be? Wouldn't the matrix just stay the same? Although good idea of using Entry<'a> for TrackedStorage.

Copy link
Member

Choose a reason for hiding this comment

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

@Aceeri Yeah, the matrix would stay just the same. The comment above is just not very clear about that.

@torkleyy torkleyy self-requested a review June 6, 2017 03:58
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.

Great, thanks!

///
/// **Note: Never use `.iter()` on a mutable component storage that uses this.**
///
///# Example Usage:
Copy link
Member

Choose a reason for hiding this comment

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

Nice docs!

@torkleyy
Copy link
Member

torkleyy commented Jun 6, 2017

In case you don't want to add or change anything, I'd say you can merge it after squashing.

bors delegate=Aceeri

Not sure if bors knows "delegate", otherwise I'll merge it.

Aceeri added 2 commits June 5, 2017 22:51
Documentation about common pitfalls and usage.

Join implementations for only iterating over flagged components.
@Aceeri
Copy link
Member Author

Aceeri commented Jun 6, 2017

bors r+

@bors
Copy link
Contributor

bors bot commented Jun 6, 2017

🔒 Permission denied

/// }
///# fn main() { }
/// ```
pub struct TrackedStorage<C, T> {
Copy link
Member

Choose a reason for hiding this comment

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

I just had an idea:

Given that both this storage and ChangeStorage track the mutation of their components, maybe we should rename this one to FlaggedStorage?

@torkleyy torkleyy changed the title TrackedStorage FlaggedStorage Jun 6, 2017
@torkleyy
Copy link
Member

torkleyy commented Jun 6, 2017

Nice we finally have a FlaggedStorage 🎉
Thanks @Aceeri!

bors r+

bors bot added a commit that referenced this pull request Jun 6, 2017
93: FlaggedStorage r=torkleyy
A basic implementation for a `TrackedStorage` (#89).

This version of the `TrackedStorage` does not implement a way to compare the past version and the modifies version of the components. Usage requires unwrapping the storage to the base `TrackedStorage` which implements `Join` for flagged components.

### Alternative design choices:
**Implement `TrackedStorage` similar to how `MaskedStorage` works.**

_Pros:_
- Would make it possible to have the `Join` of the storage only work on the flagged components without calling any additional methods.
- Might open up a path for a generic storage wrapper like `UnprotectedStorage` but instead as a container for how the `Join` gets the components necessary for the loop (this could be interesting since it would allow for different methods other than bitsets, but would also be pretty hard to get right).

_Cons:_
- Difficult to get all the components instead of just the flagged ones as there is no current way to directly get access to the container storage.
- Might be unintuitive at a glance as you would expect to get all the components when joining the storage.

**Compare modifications for more accurate flags**

_Pros:_
- Removes a large concern for if the user iterates over a mutable tracked storage.

_Cons:_
- Performance benefits of the tracked storage decreases.
- Difficult to implement. Requires a way to directly compare the new "modified" version with the past version.
@bors
Copy link
Contributor

bors bot commented Jun 6, 2017

Build succeeded

@bors bors bot merged commit 302e9e3 into amethyst:master Jun 6, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants