-
-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Visibility as a marker component #3796
Conversation
I think we should consider computed visibility at the same time, and benchmark this. As noted on Discord, for 2D/UI graphs where the computed visibility is only (for now?) dependent on the user-configured visibility and the render layers, and as such it is the usual case that there would not be large numbers of visibility changes happening very often, I think this should be a win for query speed. It would be interesting to see if it has a noticeable impact on bevymark for example. For 3D we have frustum culling that impacts the computed visibility. As the view is moved around, which will happen all the time in basically all types of 3D game, entities will change visibility as they come into and go out of view. This means that adding/removing marker components will happen frequently and there is a cost to this as it changes the archetype of the entity which I understand involves some allocations and copies of data. Notably this happens at the edges of the frustum and not throughout its volume which could mean it's a less bad problem than it may initially feel. Regardless, this could still be a win compared to iterating over all entities and having to check their computed visibility component's boolean member. Particularly for 3D I feel like to understand which approach is better, we should design some 'heavy' tests that push at the boundaries of people could reasonably want to do with the engine that stress these different aspects and measure the two solutions to identify the best one. So I propose an example 3D test scene that:
What do you think @cart ? |
Just some thoughts, take them with a grain of salt, I'm a newcomer to bevy! I also may be missing context. SemanticsNot having the fn enable_visible(query: Query<&mut Visibility>) {
for visiblity in query.iter_mut() {
*visibility.is_visible = true;
}
} After this PR, querying for all entities and adding PerformanceIs the claim that fn system(query: Query<&mut Transform, (With<Visible>,)>) {
// do stuff
} should always be faster than fn system(query: Query<&mut Transform, Visibility>) {
// check if `visibility.is_visible` is true and only then do stuff
} Would the relative performance depend quite a bit on the specific scene? A question for those knowledgeable in the internals of the ECS, i.e. for entities that change visibility often, could the fact that visible / not-visible entities have different archetypes impact performance? Which doesn't happen when using a booleans. What @superdump suggets re: benchmarking and considering ErgonomicsOn the subject of "simpler querying", you may be right, but it is not immediately obvious to me this is a simpler. I.e. representing the 2 states as a piece of data (visible or not visible) vs. the absence or presence of a component make it hard to write code that manipulates visibility. E.g. how would a function that toggles the visibility of all components that can be visible look like: fn blink(time: Res<Time>, query: Query<mut Visibility>) {
// Toggle visiblity every second
if time.seconds_since_startup().as_secs() % 2 == 1 {
for visibility in query.iter_mut() {
visibility.is_visible = !visibility.is_visible;
}
}
} InspectorLastly, maybe not the best argument, but today with something like inspector-egui, it's nice to have a checkbox to enable / disable visibility. To reproduce this, it may need to be extended to allow adding components dynamically. |
I removed My objective is to simplify, if If user visibility is toggled a lot (object pooling for example) the query gain might be counter balanced by the regular use of About visibility change, since you can then filter Edit: A small gain by the marker component concept is that systems that toggle visibility don't use mutable queries |
Self { is_visible: true } | ||
} | ||
} | ||
pub struct Visible; |
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.
Once we have benchmarks for this, we should be testing how changing this component's storage type impacts performance.
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'm in favor of this change. It is, in theory at least, the faster approach. And we have a lot of head-room to accelerate component insertion and removal, especially of unit structs.
More importantly though, this is the more idiomatic approach. Being able to filter at the query level makes this section of the code base easier to read and extend for users.
There are some issues with marker components that we should discuss before embracing them for central components like Marker components have a "visual editor" problem. With a standard "display current entity components" approach, marker components aren't discoverable. You need to know that a given entity supports them. With the current bool approach, Theres also the general "discoverability" problem: marker components that aren't part of the default bundle will have no direct correlation to the entities that support them. Ex: the We should have an answer to these issues before we start embracing marker components throughout the engine / before we start using words like "idiomatic". |
Maybe bundles could provide an enum of the available marker components? This could be useful for the visual editor as well. |
@cart maybe something like slots for particular entities/bundles would be a solution to this? Like this entity supports |
@colepoirier see extended discussion on this point in #3833. |
Ah apologies @alice-i-cecile, I missed the migration of this discussion. |
@bevyengine/rendering-team, I'd like to unblock #2087 or (more likely) a replacement PR. Could I get another set of eyes on this to verify that it's good to merge, now that we have tentative blessing from Cart. |
I still want to see benchmarks, at least for Visible, if not also ComputedVisible. Maybe some of us can do some and post results here? Maybe I’ll have a look at making some… |
I've been testing using: This PR at least doesn't seem to make any significant difference. Unrelated to this PR I was trying out a few other ideas to try to optimise some bottlenecks for #4126 and I did try to see what the impact of using a |
Here are my test results on an M1 Max: https://gist.github.com/superdump/c103b45aa0a7b28a9519c4d20b193fc0 |
My conclusions:
Recommendations:
|
This is a real use-case: imagine you're attempting to toggle visibility of map layers in an RTS. That said, I'm not convinced it would be frequent, even if it is en-masse. |
If we had propagated visibility, would it be less of an issue for many entities? or is there a use case that wouldn't have parent defined? |
There are render layers for that kind if thing I suppose. Then you’re flipping a bit in a mask on the camera and suddenly a whole slew of entities with render layers that intersect become computed visible. Good point! |
I don’t understand enough about how hierarchies work now nor will work to comment. I saw that @james7132 has been thinking about hierarchies so perhaps they could comment? |
Just for exhaustiveness of benchmark, would it be worth trying to add
on |
Also, @mockersf suggested testing using sparse set storage for the COMPUTED (not vomited… autocorrect. Lol!) visibility component which supposedly has worse iteration performance but much better insertion/removal. I asked how the dense and sparseset storage works. Dense was said to be a vec with a hash map of entity to vec index. If using swap remove that sounds like constant time, but hashing has a cost. |
Haha! See above. :) Yes, I’ll try it when I can. |
If anything, that may make it worse, since you would need to dirty/remove the component from all of the children as well. |
Haha @ concurrent comments |
Agree in theory, but given it's a one line change, I was curious to see the benchmarks, even if just for pedagogical reasons |
To clarify, that was in response to the question about mixing this change with visibility inheritance. Not the sparse set storage test. Using |
Use of SparseSet storage for ComputedVisible has been added to the gist. You may have to scroll the table as it's the last column. It is slower than default (Table?) storage for these usages. |
@cart I think we decided against this in the end, right? If so, should we close it? |
Objective
The
Visibility
component could be a marker component allowing simpler and probably faster queryingSolution
I changed the
Visibility
component to a marker structVisible
and adapted the queries.This is a breaking change that could be expanded to
ComputedVisibility
Associated discussion: #3833