-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
[3.2] Various light culling fixes #46694
Conversation
When you say "more GL state changes" you mean "unnecessary GL state changes right"? You aren't increasing the number of GL state changes by using the light mask, you just aren't necessarily decreasing state changes resulting in unnecessary changes, right? |
The concept of necessary and unnecessary is a bit fuzzy... Before these changes all directional lights affected all objects, so there were no state changes triggered by using a cull mask. If we merge this PR, certain lights can be culled for certain objects, so even in the same directional pass, the state can change. At the time of writing the commit message I thought this would result in a bunch of GL state changes, since there is no way to correctly sort the objects for all lights at once. But after your comment I realized we just need to sort the objects respect to the first light in the list, since the rest of passes (the additive ones) just skip culled lights. So I will take a look at adding this sorting and the state changes should be minimized. We still don't solve the issue with the first pass always being in charge of the environment lighting, but in this case performance will stay the same as before, It's just a missed optimization opportunity. |
I think I misunderstood you before. You are talking about the
I agree. I don't think this can be easily solved with the current architecture in 3.2. I wouldn't worry about it |
9bf31db
to
5b23482
Compare
The regression test project + sanitizer found a heap-use-after-free, see logs in https://github.com/godotengine/godot/pull/46694/checks?check_run_id=2047848252 Here's what the CI runs if you want to try it locally: godot/.github/workflows/linux_builds.yml Lines 189 to 210 in 3d5f964
|
@akien-mga I noticed... I will take a look on monday. |
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.
Approved subject to Akien's comment about the heap use after free.
64a07d4
to
bfb90ec
Compare
e->sort_key |= SORT_KEY_NO_DIRECTIONAL_FLAG; | ||
// We sort only by the first directional light. The rest of directional lights will be drawn in additive passes that are skipped if disabled. | ||
if (first_directional_light.is_valid()) { | ||
RasterizerStorageGLES3::Light *directional = light_instance_owner.getptr(first_directional_light)->light_ptr; |
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 guess the heap-use-after-free is here, the saved instance must be pointing at a now freed Light.
I'm not familiar with this code but isn't it possible to use directional_lights[0]
(after checking the size I guess) instead of saving the first light RID in first_directional_light
?
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.
That's what I was doing before, and saving the RID was an attempt to fix the issue :)
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.
Not sure what's going on... I thought the RID and RID_Owner system would take care of invalidating freed objects, but it doesn't seem to work.
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 pushed a last attempt at fixing this, but I don't think it will succeed.
The only other way I can think for fixing this is checking here:
godot/drivers/gles3/rasterizer_scene_gles3.cpp
Lines 4948 to 4953 in 118567c
bool RasterizerSceneGLES3::free(RID p_rid) { | |
if (light_instance_owner.owns(p_rid)) { | |
LightInstance *light_instance = light_instance_owner.getptr(p_rid); | |
If
p_rid == first_directional_light
and invalidating the stored RID
in such case, but it's quite hacky...
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.
Seems like it worked. I will confirm with reduz whether RIDs are meant to be used like this, because I only found one occurrence of a similar check in the same file.
GLES3 changes: This commit makes it possible to disable 3D directional lights by using the light's cull mask. It also automatically disables directionals when the object has baked lighting and the light is set to "bake all". GLES2 changes: Added a check for the light cull mask, since it was previously ignored.
bfb90ec
to
f24f582
Compare
@akien-mga The use-after-free issue is fixed, so this should be good to merge :) |
Thanks! |
I'm assuming this PR is not supposed to disable the shadows from affected directional lights? If it is supposed to then there is some bug in v3.4.3.rc1.official |
GLES3
This commit makes it possible to disable 3D directional lights by using the light's cull mask. It also automatically disables directionals when the object has baked lighting and the light is set to "bake all".
While these changes work fine, they have some downsides due to the current structure of the GLES3 renderer:
Object sorting is done only once for all directional lights, so there is no way to sort the enabled/disabled objects for each light.
This results in more GL state changes and can have some performance hit (only happens if directional lights affect some objects and not others).
Environment lighting is always done in the first pass, regardless of the corresponding directional light being enabled or not.
This means in some cases there will be a first pass with env light only (disabled light) and a second pass with an enabled light only, instead of merging the two passes in a single one.
GLES2
Added a check for the light cull mask, since it was previously ignored.
Fixes #46332. Fixes #19438.