-
-
Notifications
You must be signed in to change notification settings - Fork 21.1k
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.x] Add an option to update shadow maps less often #54516
base: 3.x
Are you sure you want to change the base?
Conversation
This is a very interesting proof of concept! I would love to see this developed into a full fledged feature. But to be mergeable, it is going to require some work, and likely larger changes. First of all, the update logic should be split so half of the shadow maps can be updated one frame and the rest the next. Otherwise you will create a subtle jitter because every second frame runs slower. Second, the update logic should likely be connected to distance to camera, or size in frame or some other LOD logic so that shadows near the camera are still updated every frame. Third, directional light should be treated differently than omni lights and spot lights as it is cast over most objects in the scene while the other two are more likely to only affect a few objects. However, omni light and spot lights are more likely to move themselves. |
I think that spot/omni lights should be grouped based on the atlas split they are in. The user should then be able to specify the number of frames to skip for each of the 4 atlas subdivisions, plus one for directional lights. As for subdividing lights into subgroups to spread the cost of the updates, that could be tricky as lights could constantly move from one atlas subdivision to another. |
This may add a fair bit of complexity, so in the meantime, I guess I could just make point lights update on even frames and directional lights update on odd frames. This would at least allow spreading the load evenly when using both directional light and point light shadows. Of course, this won't allow spreading the load over frames if you only use directional shadows or only point lights with shadows (indoor scenes). It'll still help in most outdoor scenes though. |
I updated this PR to stagger updates over frames by updating point light shadows in one frame, and directional light shadows in another. See OP for updated benchmarks. |
b17db4f
to
7d60d68
Compare
This can be used to improve performance on low-end setups, at the cost of shadows visibly lagging behind for dynamic lights/objects when up close. This option works best with lights that use Reverse Cull Face and a low negative bias value. Co-authored-by: Manuele Finocchiaro <m4nu3lf@gmail.com>
7d60d68
to
02a238c
Compare
71cb8d3
to
c58391c
Compare
// directional lights | ||
{ | ||
if (bool(GLOBAL_GET("rendering/quality/shadows/update_every_2_frames"))) { | ||
// Toggle between directional and point light shadow updating every frame. |
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 technique is discouraged to be used every frame, as these string methods are not efficient.
If you look in the comment section at the top of project_settings.h
, there are now two methods to only query this when project settings have changed.
From VisualServerScene
, as it cannot receive signals, you probably want to cache this value, and call ProjectSettings::has_changes()
and only call GLOBAL_GET if changes have occurred.
We can probably centralize this for all such project settings that need to be cached in e.g. VisualServer
. If you are not sure how to do this, just remind me after such a change and I can put this in.
We may alternatively be able to use a little macro to do this and store the cached value in a static, much like the WARN_PRINT_ONCE
type macros.
} else { | ||
shadow_map_update = ShadowMapUpdate::SHADOW_MAP_UPDATE_POINT_AND_DIRECTIONAL; | ||
} | ||
|
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.
If this is just to rotate between 3 modes, we can alternatively just cast to an int and increment then wraparound to zero.
I think as mrjustaguy hints in the master PR, one big problem might be with the non-directional lights that they update on demand, rather than every frame. So they could end up getting "stuck" with the shadow map one frame behind, or miss one important big update, and be completely wrong. |
3.x
version of #55000.The new
rendering/quality/shadows/update_every_2_frames
project setting can be used to improve performance on low-end setups, at the cost of shadows visibly lagging behind for dynamic lights/objects when up close.This option works best with lights that use Reverse Cull Face and a low negative bias value.
Thanks to @m4nu3lf for providing help with the implementation 🙂
I'll look into implementing a similar feature for
master
sometime in the future.This partially addresses godotengine/godot-proposals#2745.
Performance comparison
Using the following testing project: test_animated_shadows_3.x.zip
OS: Fedora 34
CPU: Intel Core i7-6700K
GPU: GeForce GTX 1080
Resolution: 2560×1440
Moving DirectionalLight + 3 OmniLights + 1 SpotLight
Shadows updated every frame
Shadows updated every 2 frames
Moving DirectionalLight
Shadows updated every frame
Shadows updated every 2 frames
Moving 3 OmniLights + 1 SpotLight
Shadows updated every frame
Shadows updated every 2 frames