Fix shader language float literal precision truncation #78972
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #77325
Fixes #78896
Related issue, but not affected by this PR: #78204
This PR simply replaces
rtoss()
withrtos()
, which behaves much better with float/double precision and is also much more used in the Godot codebase. After this PR, rtoss() will only be used in one place invariant_parser.cpp
. This should most likely also replaced with a better behaving version. However, the remaining issue is the underlyingString::num_scientific()
, which can sometimes drop too many decimals from float or double values, as documented in #78204.Currently, the float literal conversion between Godot shader language and the final GLSL version can drop the float decimal part accuracy dramatically, which breaks many otherwise valid GLSL tutorials, demos and libraries.
Original Godot (or GLSL) shader code:
Raw GLSL code generated from Godot Shader Language (notice the dropped decimals in the 43758.5):
Raw GLSL after this PR (notice the improved precision, for example 43758.546875 instead of just 43758.5):
Precision is very important for many use cases, for example that pretty famous (if not very portable) code snippet uses fract() to extract the float fractional part and preserving as much precision as possible is important here. Many existing GLSL / HLSL demos and tutorials currently suffer from this float literal precision loss when porting them to Godot, sometimes drastically, sometimes more subtly.
In many cases this PR slightly increases the length of each float literal in the final shader code, but the added size and parsing performance impact should still be very minor. If we want to be real resource misers with this, we could maybe try to limit the decimals to a smaller float precision (for example calling
String::num(x, 10)
etc. directly), but I don't think it's necessary. Also assuming that we don't want to support GLSL 4.0 style lf/LF double literals in the future.This PR should not directly break backwards compatibility. However, this increased precision can change existing user shader code behavior, especially if people have already tweaked their code to work around this precision loss.
Generated noise values before this PR:
After this PR: