-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Fix transform propagation of orphaned entities #7264
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
Conversation
480ab13 to
0436c5f
Compare
crates/bevy_transform/src/systems.rs
Outdated
| 1, | ||
| |(entity, children, transform, mut global_transform)| { | ||
| let changed = transform.is_changed(); | ||
| let changed = transform.is_changed() || orphaned.binary_search(&entity).is_ok(); |
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.
It may actually be faster to do a sparse set contains check here than do the binary search. It'd also avoid the allocation and sort.
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.
How would this look? RemovedComponents only exposes the iter() method. all other fields private. I guess the best way would be to add a method on RemovedComponents to allow sparse set contains checks.
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.
Looks like the entities in World::removed_components are stored in a Vec. It's unlikely that it's possible to outperform sorting + binary search. I expect the Vec to be always relatively small and not spill out of cache.
The alternative is to create a method that exposes a Option<&[Entity]> in RemovedComponents, and iterate through it for each root entities.
Another option is to store orphaned in a Local to avoid allocation at each frame. Of course, since the iterator returned by RemovedComponents::iter is Cloned<slice::Iter<Entity>>, I don't expect it to allocate when it's empty. And storing the Vec in a Local smells memory leak.
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.
This is non-actionable. If performance is a concern, could you point to ways of benchmarking testing for regression?
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.
Another option is to store
orphanedin aLocalto avoid allocation at each frame. Of course, since the iterator returned byRemovedComponents::iterisCloned<slice::Iter<Entity>>, I don't expect it to allocate when it's empty. And storing theVecin aLocalsmells memory leak.
This would be a nice (but not necessary) optimization. It would just allow the system to reuse a single allocation, it shouldn't cause a memory leak or anything bad.
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.
My worry is that orphaning happens in bursts (think of unloading/loading scenes), so we end up with reserved memory for the worst case while the Vec is only fully used one frame every minutes/hours.
I think a cool thing bevy could provide is some sort of smart Local<Vec<T>> that clears intelligently. I think it would benefit the engine itself already.
I've added a Local<Vec<Entity>> to avoid the allocation, but see the two previous paragraphs.
0436c5f to
e59d44e
Compare
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.
LGTM other than this one nit.
efa3c16 to
b63cbc8
Compare
|
It looks like your PR is a breaking change, but you didn't provide a migration guide. Could you add some context on what users should update when this change get released in a new version of Bevy? |
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.
Good fix, there's something I'm not sure about yet though.
crates/bevy_transform/src/systems.rs
Outdated
| 1, | ||
| |(entity, children, transform, mut global_transform)| { | ||
| let changed = transform.is_changed(); | ||
| let changed = transform.is_changed() || orphaned.binary_search(&entity).is_ok(); |
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.
Another option is to store
orphanedin aLocalto avoid allocation at each frame. Of course, since the iterator returned byRemovedComponents::iterisCloned<slice::Iter<Entity>>, I don't expect it to allocate when it's empty. And storing theVecin aLocalsmells memory leak.
This would be a nice (but not necessary) optimization. It would just allow the system to reuse a single allocation, it shouldn't cause a memory leak or anything bad.
b63cbc8 to
d1af08a
Compare
Co-authored-by: JoJoJet <[email protected]>
…tities query (#9518) # Objective `sync_simple_transforms` only checks for removed parents and doesn't filter for `Without<Parent>`, so it overwrites the `GlobalTransform` of non-orphan entities that were orphaned and then reparented since the last update. Introduced by #7264 ## Solution Filter for `Without<Parent>`. Fixes #9517, #9492 ## Changelog `sync_simple_transforms`: * Added a `Without<Parent>` filter to the orphaned entities query.
…tities query (bevyengine#9518) # Objective `sync_simple_transforms` only checks for removed parents and doesn't filter for `Without<Parent>`, so it overwrites the `GlobalTransform` of non-orphan entities that were orphaned and then reparented since the last update. Introduced by bevyengine#7264 ## Solution Filter for `Without<Parent>`. Fixes bevyengine#9517, bevyengine#9492 ## Changelog `sync_simple_transforms`: * Added a `Without<Parent>` filter to the orphaned entities query.
Objective
GlobalTransformdid not get updated whenParentwas removed #7263This has nothing to do with #7024. This is for the case where the
user opted to not keep the same global transform on update.
Solution
RemovedComponent<Parent>topropagate_transformsRemovedComponent<Parent>andLocal<Vec<Entity>>tosync_simple_transformsPerformance note
This should only incur a cost in cases where a parent is removed.
A minimal overhead (one look up in the
removed_componentssparse set) per root entities without children which transform didn't
change. A
Vecthe size of the largest number of entities removedwith a
Parentcomponent in a single frame, and a binary search ona
Vecper root entities.It could slow up considerably in situations where a lot of entities are
orphaned consistently during every frame, since
sync_simple_transformsis not parallel. But in this situation,it is likely that the overhead of archetype updates overwhelms everything.
Changelog
GlobalTransformnot getting updated whenParentis removedMigration Guide
bevy_transform::systems::sync_simple_transformsandbevy_transform::systems::propagate_transforms(which is not re-exported by bevy) you need to account for the additionalRemovedComponents<Parent>parameter.