-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
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
Split ComputedVisibility
into two components to allow for accurate change detection and speed up visibility propagation
#9497
Conversation
Could you run some benchmarks? I expect this to be quite performance sensitive, but I'd put even odds on improvements vs regressions. |
/// | ||
/// [`VisibilitySystems::CheckVisibility`] | ||
#[derive(Component, Deref, Debug, Default, Clone, Copy, Reflect, PartialEq, Eq)] | ||
pub struct ViewVisibility(bool); |
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.
Given that we have a trivial getter, and a public way to set values as both true and false, I have a hard time seeing why we shouldn't just make this field pub.
The docs about how to use this are excellent, but they could just be moved to the struct level.
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.
In previous discussion about components like Parent
, I think it was decided that we should avoid the .0
syntax in public APIs, which is why I opted to derive deref and add a getter.
I decided not to provide a .set(false)
option, as marking an entity as visible in view is meant to be irreversible during a given frame (this is a property I carried over from the old API, which had .set_visible_in_view()
). Of course you could still reset the view visibility by just overwriting the entire component, but at least this API nudges users in the right direction.
Agreed about the documentation.
/// Entities that are visible will be marked as such later this frame | ||
/// by a [`VisibilitySystems::CheckVisibility`] system. | ||
fn reset_view_visibility(mut query: Query<&mut ViewVisibility>) { | ||
for mut view_visibility in &mut query { |
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.
Non-blocking: this may benefit from parallel query iteration: it's a trivial loop with a ton of entities in it.
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.
I ran cargo run --release --example many_foxes
- Without par_iter: avg fps 288.0 over 5285 frames.
- With par_iter: avg fps 281.8 over 5073.
Parallel iteration doesn't seem to have much of an effect, though it's possible that it would scale better for scenes with a huge number of entities.
From an architectural and usability perspective I really like these proposed changes. I am nervous about performance costs here: I often see these systems as among the most expensive in real applications. |
I ran the stress test
|
With |
Would it be possible to keep it as one component and check if the value changes before updating it? Or triggering change manually when needed? |
That's the "naive" solution I mentioned in #8267. In order for that approach to work, we'd have to overhaul how view visibility is calculated. Each frame, the view visibility of every entity is reset. Then any number of systems belonging to the Furthermore, I think it is good to split it into separate components, even without the change detection issue. Inherited visibility and view visibility are separate concepts, and it's odd to bundle them the way we do now. |
@JoJoJet How do I run it single threaded? |
You should be able to just disable the |
Without the |
Nice, thanks for testing it! |
ComputedVisibility
into two components to allow for accurate change detectionComputedVisibility
into two components to allow for accurate change detection and speed up visibility propagation
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.
Just a few minor comments. Looks generally good.
Co-authored-by: Robert Swain <robert.swain@gmail.com>
Thanks for the review! I believe that's everything. |
# Objective This PR aims to fix a handful of problems with the `SpatialBundle` docs: The docs describe the role of the single components of the bundle, overshadowing the purpose of `SpatialBundle` itself. Also, those items may be added, removed or changed over time, as it happened with #9497, requiring a higher maintenance effort, which will often result in errors, as it happened. ## Solution Just describe the role of `SpatialBundle` and of the transform and visibility concepts, without mentioning the specific component types. Since the bundle has public fields, the reader can easily click them and read the documentation if they need to know more. I removed the mention of numbers of components since they were four, now they are five, and who knows how many they will be in the future. In this process, I removed the bullet points, which are no longer needed, and were contextually wrong in the first place, since they were meant to list the components, but ended up describing use-cases and requirements for hierarchies. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective Fix a typo introduced by #9497. While drafting the PR, the type was originally called `VisibleInHierarchy` before I renamed it to `InheritedVisibility`, but this field got left behind due to a typo.
… according to bevyengine/bevy#9497
… according to bevyengine/bevy#9497
* Bump bevy to 0.12 * Replace ComputedVisibility … according to bevyengine/bevy#9497
…change detection and speed up visibility propagation (bevyengine#9497) # Objective Fix bevyengine#8267. Fixes half of bevyengine#7840. The `ComputedVisibility` component contains two flags: hierarchy visibility, and view visibility (whether its visible to any cameras). Due to the modular and open-ended way that view visibility is computed, it triggers change detection every single frame, even when the value does not change. Since hierarchy visibility is stored in the same component as view visibility, this means that change detection for inherited visibility is completely broken. At the company I work for, this has become a real issue. We are using change detection to only re-render scenes when necessary. The broken state of change detection for computed visibility means that we have to to rely on the non-inherited `Visibility` component for now. This is workable in the early stages of our project, but since we will inevitably want to use the hierarchy, we will have to either: 1. Roll our own solution for computed visibility. 2. Fix the issue for everyone. ## Solution Split the `ComputedVisibility` component into two: `InheritedVisibilty` and `ViewVisibility`. This allows change detection to behave properly for `InheritedVisibility`. View visiblity is still erratic, although it is less useful to be able to detect changes for this flavor of visibility. Overall, this actually simplifies the API. Since the visibility system consists of self-explaining components, it is much easier to document the behavior and usage. This approach is more modular and "ECS-like" -- one could strip out the `ViewVisibility` component entirely if it's not needed, and rely only on inherited visibility. --- ## Changelog - `ComputedVisibility` has been removed in favor of: `InheritedVisibility` and `ViewVisiblity`. ## Migration Guide The `ComputedVisibilty` component has been split into `InheritedVisiblity` and `ViewVisibility`. Replace any usages of `ComputedVisibility::is_visible_in_hierarchy` with `InheritedVisibility::get`, and replace `ComputedVisibility::is_visible_in_view` with `ViewVisibility::get`. ```rust // Before: commands.spawn(VisibilityBundle { visibility: Visibility::Inherited, computed_visibility: ComputedVisibility::default(), }); // After: commands.spawn(VisibilityBundle { visibility: Visibility::Inherited, inherited_visibility: InheritedVisibility::default(), view_visibility: ViewVisibility::default(), }); ``` ```rust // Before: fn my_system(q: Query<&ComputedVisibilty>) { for vis in &q { if vis.is_visible_in_hierarchy() { // After: fn my_system(q: Query<&InheritedVisibility>) { for inherited_visibility in &q { if inherited_visibility.get() { ``` ```rust // Before: fn my_system(q: Query<&ComputedVisibilty>) { for vis in &q { if vis.is_visible_in_view() { // After: fn my_system(q: Query<&ViewVisibility>) { for view_visibility in &q { if view_visibility.get() { ``` ```rust // Before: fn my_system(mut q: Query<&mut ComputedVisibilty>) { for vis in &mut q { vis.set_visible_in_view(); // After: fn my_system(mut q: Query<&mut ViewVisibility>) { for view_visibility in &mut q { view_visibility.set(); ``` --------- Co-authored-by: Robert Swain <robert.swain@gmail.com>
# Objective This PR aims to fix a handful of problems with the `SpatialBundle` docs: The docs describe the role of the single components of the bundle, overshadowing the purpose of `SpatialBundle` itself. Also, those items may be added, removed or changed over time, as it happened with bevyengine#9497, requiring a higher maintenance effort, which will often result in errors, as it happened. ## Solution Just describe the role of `SpatialBundle` and of the transform and visibility concepts, without mentioning the specific component types. Since the bundle has public fields, the reader can easily click them and read the documentation if they need to know more. I removed the mention of numbers of components since they were four, now they are five, and who knows how many they will be in the future. In this process, I removed the bullet points, which are no longer needed, and were contextually wrong in the first place, since they were meant to list the components, but ended up describing use-cases and requirements for hierarchies. --------- Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
# Objective Fix a typo introduced by bevyengine#9497. While drafting the PR, the type was originally called `VisibleInHierarchy` before I renamed it to `InheritedVisibility`, but this field got left behind due to a typo.
Objective
Fix #8267.
Fixes half of #7840.
The
ComputedVisibility
component contains two flags: hierarchy visibility, and view visibility (whether its visible to any cameras). Due to the modular and open-ended way that view visibility is computed, it triggers change detection every single frame, even when the value does not change. Since hierarchy visibility is stored in the same component as view visibility, this means that change detection for inherited visibility is completely broken.At the company I work for, this has become a real issue. We are using change detection to only re-render scenes when necessary. The broken state of change detection for computed visibility means that we have to to rely on the non-inherited
Visibility
component for now. This is workable in the early stages of our project, but since we will inevitably want to use the hierarchy, we will have to either:Solution
Split the
ComputedVisibility
component into two:InheritedVisibilty
andViewVisibility
.This allows change detection to behave properly for
InheritedVisibility
.View visiblity is still erratic, although it is less useful to be able to detect changes
for this flavor of visibility.
Overall, this actually simplifies the API. Since the visibility system consists of
self-explaining components, it is much easier to document the behavior and usage.
This approach is more modular and "ECS-like" -- one could
strip out the
ViewVisibility
component entirely if it's not needed, and rely only on inherited visibility.Changelog
ComputedVisibility
has been removed in favor of:InheritedVisibility
andViewVisiblity
.Migration Guide
The
ComputedVisibilty
component has been split intoInheritedVisiblity
andViewVisibility
. Replace any usages ofComputedVisibility::is_visible_in_hierarchy
with
InheritedVisibility::get
, and replaceComputedVisibility::is_visible_in_view
with
ViewVisibility::get
.