-
-
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
Allow Visibility to be inherited #5146
Conversation
I was thinking about this very change recently when working on my project and I naively presumed adding As such I am in support of this change. |
Since |
It's either this current implementation or require ComputedVisibility for propagation, which isn't true for for some extraction pipelines like lights and sprites. Also only fetching Visibility does make the queries faster. It'd also preclude |
The propagation system will always change the components, so it becomes impossible from the user to rely on |
This is indeed a downside, though I guess we could put change detection back in to remedy this. My question is if we actually need change detection here. Even with this change, |
Change detection isn't just about optimization. It can be used for implementing logic. Ex: "send an event every time visibility changes for an entity" |
OK, added a few small changes to then avoid triggering it when nothing has actually changed. This still keeps the same structure as before, but |
@alice-i-cecile removing the Controversial tag since I think the only point that was of major concern was the change detection issue. Unless the overall design is of a concern, which I think might be quelled by the fact that cart suggested basically the same API about a year ago. |
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 looks good to me and I imagine you've done benchmarking to see that it doesn't significantly impact performance. There are some 'stress tests' for transform hierarchy that could probably be leveraged for testing if not.
@@ -76,6 +111,7 @@ pub enum VisibilitySystems { | |||
UpdateOrthographicFrusta, | |||
UpdatePerspectiveFrusta, | |||
UpdateProjectionFrusta, | |||
VisibilityPropagate, |
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 consistent with TransformPropagate
and so I think it should remain like this for this PR but I'd prefer VerbNoun
like PropagateTransform
and PropagateVisibility
. It just reads better. :)
.spawn() | ||
.insert(Visibility { |
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 have never understood why one might want to use the .spawn().insert()
pattern and not just basically always .spawn_bundle((Component,))
. Is there a reason?
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 find this cleaner than creating a tuple with a single element
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 also makes no perf difference: spawn_bundle on a single element still does a spawn + bundle insert with a single component. I think the only thing you spare is the command encoding.
Whoops, forgot to put those results up in the original one as perf justification for this approach. Just to verify, I looked to ensure that the system parallelized with transform propagation, and blocks
For a stress test with deeper hierarchies, I tried Unfortunately I don't think we have many stress tests that target both in deep hierarchies. |
There's the |
The visibility propagate currently requires visibility to be at every level of the hierarchy (like transforms): the transform hierarchy stress test wouldn't be a good test. I could try reworking it to support hierarchies with gaps, but this would be a huge hit to perf as it would likely match all entities, not just those bound to the hierarchy. |
Fair enough. Sounds like we need a new test then. |
Oops. Finger slipped. |
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.
Broadly in favor of this, but I still think ComputedVisibility should own this info leaving Visibility as the "user-defined configuration component".
inherited: bool, | ||
} | ||
|
||
impl Visibility { |
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 should have a constructor to make disabling visibility "inline" during entity spawning possible. As it stands, this requires defining calling visibility.set_visible(false)
on an intermediate variable.
#[reflect(Component, Default)] | ||
pub struct Visibility { | ||
pub is_visible: bool, | ||
self_visible: bool, | ||
inherited: 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.
I do still think this belongs on ComputedVisibility, and that we should be encouraging people to write systems that read from ComputedVisibility when they care about "actual visibility of a thing".
The stated counterarguments to this (lights and sprites), are both examples of things that should probably be using ComputedVisibility (sprites will probably ultimately support some sort of visibility culling and they will definitely need to support RenderLayers. same goes for lights). Earlier in the new renderers' lifecycle I did make the call not to include ComputedVisibility for sprites for performance reasons, but we have enough usability reasons to include it at this point (multiple cameras / controllable render layers / visibility inheritance / etc) that i'm convinced its worth the cost.
I'm currently putting together the changes I outlined above (store computed hierarchy visibility on ComputedVisibility) so we can compare performance and make a call. This will also move sprites and ui to ComputedVisibility and will enable RenderLayers functionality. So it would also be an alternative to #5114 and #4007. I suspect it to perform worse than #5114 because it will be populating camera "visible entity" lists. But imo unifying these systems makes a lot of sense. Keeps the code smaller and the behaviors in sync / consistent. And if the current impl isn't good / fast enough for sprites, is it really good enough for 3d meshes? Unifying them would create pressure to optimize the system as a whole, which I like. |
Still working out implementation details, but heres some bevymark performance numbers (100,000 sprites) with various approaches:
What this tells me is that the ComputedVisibility switch doesn't inherently change the cost (which makes a lot of sense to me), but adding render layer filter checks, checking for (non existent) frustum culling, and populating each view's "visible entity list" does have a cost. And that feels like a cost we should pay (but also try to minimize as much as possible). |
I think I have everything working at this point. I added fixedbitset-based view-visibility-checks to the So as expected this definitely isn't free, but it buys us:
That being said, merging this puts us into "performance debt". Leaving Heres a "split screen" example that uses render layers to hide the blue sprite on the left camera. And here is my current branch: https://github.com/cart/bevy/tree/computed-visibility |
2D frustum culling could maybe help for something like many_sprites with lots of off-screen sprites, but it’s possible that clipping them in rasterisation is faster than CPU frustum culling, as the geometry is so simple. For bevymark it would only be a cost as everything is on screen and in view. The gains for the bevymark case seem to be about reducing overdraw and not recreating the vertex buffer, rather doing instancing/vertex pulling of some kind to deduplicate all the vertex attribute data into per-instance data, including the position and orientation of the sprite. The combination of using an instanced draw of 6 vertices where the instance contains translation and half extents, and using a depth buffer and greater than depth comparison function made bevymark hit 120fps up from 60fps for 160k sprites in batches of 10k on an M1 Max. Using a depth buffer for transparent sprites is wrong but it demonstrates the value. With just instancing there was no performance gain. With just the depth buffer there was no performance gain. I guess as GPU-side rendering is in parallel with main app simulation and render app CPU-side rendering, the bottlenecks must be on both CPU and GPU. I think sprite performance could benefit from opaque and alpha mask and a depth buffer but transparent stuff would still cause performance problems due to back to front rendering and overdraw. I discussed on Discord that we can make tighter-fitting meshes and use UV mapping like Unity and TexturePacker do and that would reduce overdraw though it makes batching more complicated and moves sprites more toward Mesh2d. Batching would still line up with the ‘one big vertex buffer + meshlet’ approach I’ve been discussing in the GPU instancing issue. I think that is a good direction from a rendering perspective, but it’s a more complicated workflow so I would only want to do it when helpers/other infrastructure is in-place. Such as asset pipeline and asset metadata stuff to allow a sprite/spritesheet import workflow that automatically generates the meshes and uvs internally. Coming back to the topic of this PR - even if frustum culling is only a cost for bevymark, one can opt out of doing it using the NoFrustumCulling component. Also, culling for the many_sprites problem could probably be better served by some coarse space division like a tiled world and just load all the content from the tiles that intersect the screen, maybe simulating some entities anyway but marking them as not visible to skip the more costly frustum culling if you know they’re in a tile that is off screen or something. I feel like the bevymark case is unrealistic as it stacks lots of sprites on top of each other to the point that you can’t really see what is going on. It’s still a good stress test. many_sprites is perhaps more reasonable but there’s just a lot of sprites off screen for which there are strategies to reduce the load. So maybe we don’t need to worry too much about artificial extreme cases for sprites? |
Thanks for the update on what you've been exploring. I love the extra context on culling and im glad we've got more perf wins ahead of us. For clarity: I'm testing with bevymark largely because it highlights per-drawn-entity CPU costs in a roughly-linear and reasonably consistent way, not because its a great real world benchmark. Its a good way to "isolate the variables" that are affected by the changes being made. The GPU-side costs of bevymark (in the context of this pr) are equivalent across implementations, so we can easily compare frame times. And we'd be paying these per-entity costs with or without frustum (or some other) culling, so it feels orthogonal. |
Yup, I agree that they're useful stress tests to focus on performance of various relevant systems and hopefully by now it's clear that I care about performance. :) That said, I'm asking the question - do we need to prioritise improving sprite performance from roughly where we're at compared to other things? My guess is that having some 10s or maybe 100s of sprites on-screen, or at a stretch some 1000s, is going to be the vast majority of use cases for real applications. Maybe some Touhou-style bullet-hell shooter would have a lot of sprites but I would still doubt anywhere near 100k. If we think the answer is that we don't need to prioritise it so much from where we're at, and my argument was also that there are other ways to improve performance if really needed, then taking the performance hit for consistency of |
That is a welcome dose of pragmatism. Thanks for the (friendly) bonk on the head. Lets review the changes and a make a call as a group. I think the changes are substantial enough (and the context has changed enough) to merit a separate PR with a new description. That will also make it easier to merge this PR if we decide my changes are the wrong direction. I maintained the original authorship of all commits in this pr and @james7132 still deserves all of the credit for the inheritance logic (and the "first position" author slot on the blog post section for this feature). |
As someone heavily interested in making 2D games, including performance intensive one, I'm extremely okay sacrificing rendering performance here for more features and a better experience. |
New pr is up! Still a few changes to make / things to test (hence the draft), but please do start looking at it + forming opinions. |
Closing this out to collect efforts there. |
…support (#5310) # Objective Fixes #4907. Fixes #838. Fixes #5089. Supersedes #5146. Supersedes #2087. Supersedes #865. Supersedes #5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](#5146 (comment)) for more info. Note that #5146 in combination with #5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite. ![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png) Note that this builds directly on #5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](#5146 (comment)) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
…support (bevyengine#5310) # Objective Fixes bevyengine#4907. Fixes bevyengine#838. Fixes bevyengine#5089. Supersedes bevyengine#5146. Supersedes bevyengine#2087. Supersedes bevyengine#865. Supersedes bevyengine#5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](bevyengine#5146 (comment)) for more info. Note that bevyengine#5146 in combination with bevyengine#5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite. ![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png) Note that this builds directly on bevyengine#5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](bevyengine#5146 (comment)) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
…support (bevyengine#5310) # Objective Fixes bevyengine#4907. Fixes bevyengine#838. Fixes bevyengine#5089. Supersedes bevyengine#5146. Supersedes bevyengine#2087. Supersedes bevyengine#865. Supersedes bevyengine#5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](bevyengine#5146 (comment)) for more info. Note that bevyengine#5146 in combination with bevyengine#5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite. ![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png) Note that this builds directly on bevyengine#5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](bevyengine#5146 (comment)) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
…support (bevyengine#5310) # Objective Fixes bevyengine#4907. Fixes bevyengine#838. Fixes bevyengine#5089. Supersedes bevyengine#5146. Supersedes bevyengine#2087. Supersedes bevyengine#865. Supersedes bevyengine#5114 Visibility is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities. Additionally, the semantics of `Visibility` vs `ComputedVisibility` are inconsistent across entity types. 3D meshes use `ComputedVisibility` as the "definitive" visibility component, with `Visibility` being just one data source. Sprites just use `Visibility`, which means they can't feed off of `ComputedVisibility` data, such as culling information, RenderLayers, and (added in this pr) visibility inheritance information. ## Solution Splits `ComputedVisibilty::is_visible` into `ComputedVisibilty::is_visible_in_view` and `ComputedVisibilty::is_visible_in_hierarchy`. For each visible entity, `is_visible_in_hierarchy` is computed by propagating visibility down the hierarchy. The `ComputedVisibility::is_visible()` function combines these two booleans for the canonical "is this entity visible" function. Additionally, all entities that have `Visibility` now also have `ComputedVisibility`. Sprites, Lights, and UI entities now use `ComputedVisibility` when appropriate. This means that in addition to visibility inheritance, everything using Visibility now also supports RenderLayers. Notably, Sprites (and other 2d objects) now support `RenderLayers` and work properly across multiple views. Also note that this does increase the amount of work done per sprite. Bevymark with 100,000 sprites on `main` runs in `0.017612` seconds and this runs in `0.01902`. That is certainly a gap, but I believe the api consistency and extra functionality this buys us is worth it. See [this thread](bevyengine#5146 (comment)) for more info. Note that bevyengine#5146 in combination with bevyengine#5114 _are_ a viable alternative to this PR and _would_ perform better, but that comes at the cost of api inconsistencies and doing visibility calculations in the "wrong" place. The current visibility system does have potential for performance improvements. I would prefer to evolve that one system as a whole rather than doing custom hacks / different behaviors for each feature slice. Here is a "split screen" example where the left camera uses RenderLayers to filter out the blue sprite. ![image](https://user-images.githubusercontent.com/2694663/178814868-2e9a2173-bf8c-4c79-8815-633899d492c3.png) Note that this builds directly on bevyengine#5146 and that @james7132 deserves the credit for the baseline visibility inheritance work. This pr moves the inherited visibility field into `ComputedVisibility`, then does the additional work of porting everything to `ComputedVisibility`. See my [comments here](bevyengine#5146 (comment)) for rationale. ## Follow up work * Now that lights use ComputedVisibility, VisibleEntities now includes "visible lights" in the entity list. Functionally not a problem as we use queries to filter the list down in the desired context. But we should consider splitting this out into a separate`VisibleLights` collection for both clarity and performance reasons. And _maybe_ even consider scoping `VisibleEntities` down to `VisibleMeshes`?. * Investigate alternative sprite rendering impls (in combination with visibility system tweaks) that avoid re-generating a per-view fixedbitset of visible entities every frame, then checking each ExtractedEntity. This is where most of the performance overhead lives. Ex: we could generate ExtractedEntities per-view using the VisibleEntities list, avoiding the need for the bitset. * Should ComputedVisibility use bitflags under the hood? This would cut down on the size of the component, potentially speed up the `is_visible()` function, and allow us to cheaply expand ComputedVisibility with more data (ex: split out local visibility and parent visibility, add more culling classes, etc). --- ## Changelog * ComputedVisibility now takes hierarchy visibility into account. * 2D, UI and Light entities now use the ComputedVisibility component. ## Migration Guide If you were previously reading `Visibility::is_visible` as the "actual visibility" for sprites or lights, use `ComputedVisibilty::is_visible()` instead: ```rust // before (0.7) fn system(query: Query<&Visibility>) { for visibility in query.iter() { if visibility.is_visible { log!("found visible entity"); } } } // after (0.8) fn system(query: Query<&ComputedVisibility>) { for visibility in query.iter() { if visibility.is_visible() { log!("found visible entity"); } } } ``` Co-authored-by: Carter Anderson <mcanders1@gmail.com>
Objective
Fixes #4907. Fixes #838.
Supercedes #2087. Supercedes #865.
Visibility
is currently entirely local. Set a parent entity to be invisible, and the children are still visible. This makes it hard for users to hide entire hierarchies of entities.Solution
Expand
Visibility
to hold both a "self" visibility, and a "inherited" visibility. The entity is visible if and only if both are true. A system similar totransform_propagate_system
is then implemented to propagate visibility down through the hierarchy. Unlike Transforms, the action of setting visible or not is relatively light computationally so zero change detection is used for the system.The
visibility_propagate_system
can run in parallel withtransform_propagate_system
, and is set as a dependency ofcheck_visibiility
, which will copy the same state toComputedVisibilty
once propagated. This structure is to avoid making a serialized chain oftransform_propagate
->check_visibiliity
->visibility_propagate
. Due to the lighter nature of the visibility propagation,visibility_propagate_system
will almost always finish faster thantransform_propagate_system
for perfectly overlapped hierarchies, so there is zero overhead on platforms that support multithreaded execution (basically everywhere except wasm32 platforms right now).Changelog
TODO
Migration Guide
TODO