Skip to content
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 support for 3 directional shadow splits #75468

Merged
merged 1 commit into from
Oct 1, 2023

Conversation

Ansraer
Copy link
Contributor

@Ansraer Ansraer commented Mar 29, 2023

Currently, Godot only supports either 1, 2, or 4 shadow cascades.

However, later splits are exponentially more expensive, so especially for games with large & complex scenes having to render multiple cascades with very large shadow-frustums is not ideal.

This PR makes it possible to use three cascades. While doing so results in a slight loss of shadow quality no longer having to render an entire cascade can speed up shadow performance by 15-20% (according to Renderdoc).


This PR was sponsored by Ramatak with 💚

@Ansraer Ansraer requested review from a team as code owners March 29, 2023 17:34
@Calinou
Copy link
Member

Calinou commented Mar 29, 2023

Great work! This would be useful to have in 4.x as well, as sometimes 3 splits is exactly what you need in a scene as a compromise between performance and quality.

@@ -386,7 +386,7 @@ void DirectionalLight::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight::is_blend_splits_enabled);

ADD_GROUP("Directional Shadow", "directional_shadow_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode");
ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 3 Splits (Slow),PSSM 4 Splits (Very Slow)"), "set_shadow_mode", "get_shadow_mode");
Copy link
Member

@akien-mga akien-mga Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This breaks compatibility AFAIK, as we save integer values in scene/resource files.

So a Godot 3.5 user having configured 4 splits will find themselves automatically moved to 3 splits when opening their project in 3.6, if this is merged as is. We could consider this intentional and document it clearly as such, but we should discuss it.

An option to prevent the compatibility breakage is to add the 3 splits option at the end of the enum (both here and in the actual C++ enum), so the integer values aren't changed. It will look a bit out of order, but preserves compatibility.

Copy link
Member

@Calinou Calinou Jun 7, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So a Godot 3.5 user having configured 4 splits will find themselves automatically moved to 3 splits when opening their project in 3.6, if this is merged as is.

The default is 4 splits (2 in the enum), so it's not saved to the scene file. This conveniently avoids this issue, as non-default values (Orthogonal 0/PSSM 2 Splits 1) are stored in indices before the enum order is broken.

The only case where breakage might occur is when a user sets the shadow mode using an integer value directly (not using the enum constant), but it's very unlikely.

@clayjohn
Copy link
Member

clayjohn commented Jun 7, 2023

Before merging we need to have a sense of the performance of doing this vs 2 and 4 splits.

As a general rule of thumb, we shouldn't merge performance optimizations without profiling first.

@Calinou
Copy link
Member

Calinou commented Jun 7, 2023

I've created a testing project: test_shadow_3_splits_3.x.zip

Test results on a GeForce RTX 4090 in 3840×2160:

GLES3

Orthogonal PSSM 2 Splits PSSM 3 Splits (this PR) PSSM 4 Splits
1420 FPS (0.70 mspf) 1276 FPS (0.78 mspf) 1087 FPS (0.92 mspf) 870 FPS (1.15 mspf)

GLES2

Orthogonal PSSM 2 Splits PSSM 3 Splits (this PR) PSSM 4 Splits
1865 FPS (0.54 mspf) 1636 FPS (0.61 mspf) 1328 FPS (0.75 mspf) 1037 FPS (0.97 mspf)

In scenes with lots of instances (= lots of draw calls), there's a definitive performance improvement when using 3 splits as opposed to 4 with the same Shadow Max Distance property.

What's strange is that official 3.5.2 releases are much faster in both GLES3 and GLES2, despite my self-compiled binary using optimize=speed lto=full:

GLES3

Orthogonal PSSM 2 Splits PSSM 4 Splits
2313 FPS (0.43 mspf) 2284 FPS (0.44 mspf) 1984 FPS (0.50 mspf)

GLES2

Orthogonal PSSM 2 Splits PSSM 4 Splits
4132 FPS (0.24 mspf) 3750 FPS (0.27 mspf) 2353 FPS (0.52 mspf)

This is also an issue with my self-compiled builds with the commit from this PR reverted, so I don't know what's causing this. Either way, I can still measure a performance improvement – the figures with a local build and the PR reverted are identical to the results displayed in the first two tables.

Note that godotengine/godot-proposals#3464 could help improve the appearance of PSSM 2 Splits out of the box, making it usable in more situations without requiring a manual adjustment. That said, 3 splits is still visually superior in the vast majority of cases.

@lawnjelly
Copy link
Member

Rendering meeting today:

All are happy with this feature, but the enums should preserve backward compatibility, hence:

0: 1 split
1: 2 splits
2: 4 splits
3: 3 splits

Even though it is slightly strange.
This may affect the order of display in the inspector, apparently 4.x has a feature to change the order of display, I don't know whether this is present in 3.x. If not we could consider backporting it, but it isn't a dealbreaker if the order displayed in the Inspector is "quirky".

Copy link
Member

@lawnjelly lawnjelly left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on what @Calinou says about the previous default value (2) not being serialized, this would seem to resolve the issue about breaking compat.

@akien-mga akien-mga merged commit 5f9cbe5 into godotengine:3.x Oct 1, 2023
@akien-mga
Copy link
Member

Thanks!

@Calinou Calinou mentioned this pull request Dec 17, 2023
5 tasks
@akien-mga akien-mga changed the title [3.x] Add support for 3 dir shadow splits [3.x] Add support for 3 directional shadow splits Jan 25, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants