X Tutup
Skip to content

Use half float precision buffer for 3D when HDR2D is enabled#109971

Merged
Repiteo merged 1 commit intogodotengine:masterfrom
clayjohn:hdr2d-mobile
Oct 22, 2025
Merged

Use half float precision buffer for 3D when HDR2D is enabled#109971
Repiteo merged 1 commit intogodotengine:masterfrom
clayjohn:hdr2d-mobile

Conversation

@clayjohn
Copy link
Member

@clayjohn clayjohn commented Aug 25, 2025

Fixes: #86098
Fixes: #106282
Closes: godotengine/godot-proposals#7878

This took a bit of refactoring. Originally we selected a 3D framebuffer format based purely on whether we were in the mobile renderer or the Forward+ renderer. For the 2D framebuffer, we selected based on the presence of the HDR2D setting.

This created a strange incompatibility when using glow with HDR2D with the mobile renderer. Your 2D content would be rendered in HDR. Then copied into an LDR framebuffer. Then copied back to HDR with glow applied.

While there is a theoretical benefit to using an LDR framebuffer in 3D (especially when using glow), it just isn't compatiblity with 2D HDR. It is much better quality-wise to keep everything in HDR from beginning to end.

The biggest change is that we now look to the RenderBuffers class to determine if we should use an HDR format and whether we need to use a luminance multiplier. The RenderBuffers are automatically recreated if we switch to HDR, so this ensures that every part of the pipeline uses the same settings.

Reflections and sky still use LDR always since they can be shared between several Viewports and not all viewports necessarily have the same HDR2D setting. In the future we may want to consider making ReflectionProbes and Sky always use HDR. But that decision is out of scope for this PR.

@beicause
Copy link
Contributor

beicause commented Sep 2, 2025

Does this mean even not targeting 2D, enabling HDR 2D will cause 3D to use an RGBAH buffer? (Related: godotengine/godot-proposals#7878)

@clayjohn
Copy link
Member Author

clayjohn commented Sep 2, 2025

Does this mean even not targeting 2D, enabling HDR 2D will cause 3D to use an RGBAH buffer? (Related: godotengine/godot-proposals#7878)

That's right! Thank you for pointing that out

@beicause
Copy link
Contributor

beicause commented Sep 2, 2025

If so, this may cause a performance regression in 3D projects with HDR2D enabled, which could be unexpected (the term "HDR 2D" is not accurate anymore, docs need changes).

@clayjohn
Copy link
Member Author

clayjohn commented Sep 2, 2025

If so, this may cause a performance regression in 3D projects with HDR2D enabled, which could be unexpected (the term "HDR 2D" is not accurate anymore).

That's right. We discussed the performance tradeoff at length in #86098 and in subsequent rendering meetings and decided that it was worth the change. If 2D is using full HDR, then the entire pipeline should use full HDR.

That being said, its not necessarily going to be a performance regression because:

  1. Not all games are bandwidth limited. This change only hurts bandwidth limited games
  2. It opens up more opportunities to share resources between 2D and 3D which can have a positive impact on performance
  3. Most modern phones support framebuffer compression for RGBA16 buffers now. So the original reason to limit to RGB10A2 is gone now for many devices
  4. Once we support HDR output, this change is 100% necessary and will improve performance since we won't have to do a needless copy to promote the RGB10A2 buffer to full RGBA16.
  5. If users were already using HDR2D, they are paying the cost of having a RGBA16 buffer already, so they have already decided to accept reduced performance for the benefits it provides.

Overall, we felt that making this change was worthwhile.

Copy link
Member

@Calinou Calinou left a comment

Choose a reason for hiding this comment

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

Tested locally on many demo projects and https://github.com/Calinou/godot-reflection, it works as expected.

However, the sky appears visibly darker in Truck Town when HDR 2D is enabled in the project settings:

hdr_2d_mobile.mp4

This also carries over to the running project. However, reflection probes are not affected by this issue, even if you force them to update by moving them after changing the setting:

hdr_2d_mobile_reflection_probes.mp4

(The NaN you see in the reflection probe on the sun also occurs in master; I'll open a separate issue. Edit: #110207)

Either way, this has the upside of making viewport debanding functional in Mobile if HDR 2D is also enabled.

It's worth investigating for a future PR if we should also switch over the sky and reflection probes to HDR. This would improve visual consistency with Forward+ and increase the viability of HDR output for the Mobile renderer, but this needs its own proposal.

@clayjohn
Copy link
Member Author

clayjohn commented Sep 2, 2025

It's worth investigating for a future PR if we should also switch over the sky and reflection probes to HDR. This would improve visual consistency with Forward+ and increase the viability of HDR output for the Mobile renderer, but this needs its own proposal.

See the last paragraph in the PR description above

Reflections and sky still use LDR always since they can be shared between several Viewports and not all viewports necessarily have the same HDR2D setting. In the future we may want to consider making ReflectionProbes and Sky always use HDR. But that decision is out of scope for this PR.

@beicause

This comment was marked as off-topic.

@allenwp
Copy link
Contributor

allenwp commented Sep 21, 2025

I'm actually not immediately sure why from glancing at the code changes, but this PR breaks debanding in the following configuration:

  • Mobile renderer
  • SMAA antialiasing
  • HDR 2D disabled

View these screenshots in a dark viewing environment at 100% scaling:

Godot 4.5 This PR
godot-4 5 this-pr

MRP: mobile-hdr-2d-debanding.zip

SMAA causes the first tonemap pass to match the scene rendering buffer format, which is 10-bit when HDR 2D is disabled, so 10-bit debanding in the tonemap shader is required for this configuration.

The code that detects what buffer format should be used for debanding makes a lot of assumptions about data formats because the exact format of a given buffer isn't really saved in an accessible way for easy access. It's possible that the debanding code now thinks that this configuration is using a 16 bit buffer for even though it is actually using a 10 bit buffer?

Edit: Sorry, some of what I wrote in the first pass of this comment wasn't quite right... I think I've corrected it(?) To be honest, I need to dig deep into this every time I try and think about it because it's a little complicated to keep track of the different buffer formats used through the different passes with each renderer and HDR 2D configuration 😵‍💫

@clayjohn
Copy link
Member Author

@allenwp I think I may have fixed that issue in the last rebase when I fixed the conflicts. I can't repro that issue right now. Your MRP looks totally fine and matches the behaviour in Godot 4.5

Screenshot 2025-10-20 at 9 08 58 PM

This is necessary for Environment effects like Glow to work correctly.
@Repiteo Repiteo merged commit ec62f12 into godotengine:master Oct 22, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Oct 22, 2025

Thanks!

@allenwp
Copy link
Contributor

allenwp commented Oct 22, 2025

@allenwp I think I may have fixed that issue in the last rebase when I fixed the conflicts. I can't repro that issue right now. Your MRP looks totally fine and matches the behaviour in Godot 4.5

Just got around to testing this and reviewing the updated PR that was merged: yes, this looks good! Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

6 participants

X Tutup