Fix half-float render target fallback for mobile GPUs missing EXT_color_buffer_half_float#33114
Fix half-float render target fallback for mobile GPUs missing EXT_color_buffer_half_float#33114RodrigoHamuy wants to merge 7 commits intomrdoob:devfrom
EXT_color_buffer_half_float#33114Conversation
…alf_float is unsupported
…float is unsupported
📦 Bundle sizeFull ESM build, minified and gzipped.
🌳 Bundle size after tree-shakingMinimal build including a renderer, camera, empty scene, and dependencies.
|
EXT_color_buffer_half_float
|
I would be happy to extend the guard, since it's quite an easy and rewarding fix 😄 but just would like confirmation if that's the right approach to take? We could still merge this and deal with the rest on another PR? |
|
Maybe the renderer could do this automatically? (While logging a warning in the console) |
examples/jsm/objects/Water.js
Outdated
|
|
||
| scope.onBeforeRender = function ( renderer, scene, camera ) { | ||
|
|
||
| // Lazily fall back to FloatType if EXT_color_buffer_half_float is unsupported (e.g. some mobile GPUs) |
There was a problem hiding this comment.
I would expect a fallback to UnsignedByteType if half float isn't available. The render target setup for transmission works like that:
three.js/src/renderers/WebGLRenderer.js
Lines 1950 to 1961 in 320e432
There was a problem hiding this comment.
To clarify: There is no need to use FloatType. We can keep using HalfFloatType but just request EXT_color_buffer_float instead. This extension includes formats like gl.RGBA16F.
Reference: https://registry.khronos.org/webgl/extensions/EXT_color_buffer_float/
The thing is, the renderer already requests EXT_color_buffer_float here:
three.js/src/renderers/webgl/WebGLExtensions.js
Lines 33 to 38 in 320e432
I'm now confused why the changes in the PR are required in the first place.
There was a problem hiding this comment.
There was a problem hiding this comment.
If EXT_color_buffer_float is supported on your device, HalfFloatType should work. If it doesn't, we must find out the root cause.
Do you see any console warnings when HalfFloatType is used?
There was a problem hiding this comment.
No console warnings, silently passes. Driver issue?
Btw, I moved the fallback to WebGLTextures as it seems like the right place instead as suggested by @mrdoob .
There was a problem hiding this comment.
It seems the issue was introduced with #32680 which has added bloom to the demo.
There was a problem hiding this comment.
The pixels in webgl_shaders_ocean can get very bright due to how the sky is implemented now (it does not use a built-in gamma correction since r183). If you then generate a PMREM from the scene and use it as an environment map, the objects in your scene end up bright as well. If you then add bloom to the scene, it further increases the luminance. I think this gets to a point where certain devices can't handle the values anymore.
Given that there is no issues on Desktop and the demo uses RGBAF16 on all devices, the root cause must be related to something device specific.
There was a problem hiding this comment.
I think the issue in r183 happens even if I disable bloom (which I did while debugging this).
r182 does work fine, although, damn, that was a nice upgrade in the last version 😄 . The new sky looks so much better!!
There was a problem hiding this comment.
Indeed, without bloom the rendering breaks but more subtle. Here is a test link right before the bloom addition:
With a high elevation, the box mesh becomes black (it should turn white though). Nevertheless, bloom seems to amplify the issue. The breakage on mobile starts with #32677 though.
There was a problem hiding this comment.
I think we must somehow isolate this issue into a smaller test case. Like the one from #33114 (comment). In this way, it will be easier to get feedback from the browser dev side.
…rted devices When EXT_color_buffer_half_float is unavailable, setupRenderTarget now automatically falls back to FloatType (if EXT_color_buffer_float is present) or UnsignedByteType, emitting a one-time console warning. This covers Water, PMREMGenerator, shadow maps, post-processing passes, and all other WebGL render targets uniformly. Revert per-component patches in Water.js and PMREMGenerator.js — now redundant.


On devices that support WebGL 2.0 but lack
EXT_color_buffer_half_float(e.g. Google Pixel 6 Pro), rendering intoHalfFloatTyperender targets silently fails.PMREMGeneratorandWaterwere both hardcoded toHalfFloatType. Both now fall back toFloatTypewhen the extension is unavailable.Steps to reproduce
webgl_shaders_oceanexample on a device withoutEXT_color_buffer_half_float(e.g. Google Pixel 6 Pro) and set elevation to 30 so the sun is visible.video
screen-20260303-225610-1772578560095.mp4
Potential next steps
The same extension guard is missing in several other WebGL render targets. They will silently produce black or broken output on the same class of devices:
src/renderers/webgl/WebGLShadowMap.jsexamples/jsm/objects/Reflector.jsexamples/jsm/objects/Refractor.jsexamples/jsm/objects/ReflectorForSSRPass.jsexamples/jsm/postprocessing/EffectComposer.jsexamples/jsm/postprocessing/UnrealBloomPass.jsexamples/jsm/postprocessing/OutlinePass.jsexamples/jsm/postprocessing/SSAOPass.jsexamples/jsm/postprocessing/GTAOPass.jsexamples/jsm/postprocessing/SAOPass.jsexamples/jsm/postprocessing/SMAAPass.jsexamples/jsm/postprocessing/AfterimagePass.jsexamples/jsm/postprocessing/BloomPass.jsexamples/jsm/postprocessing/SavePass.jsexamples/jsm/postprocessing/BokehPass.jsexamples/jsm/postprocessing/TAARenderPass.jsexamples/jsm/postprocessing/SSAARenderPass.jsexamples/jsm/postprocessing/SSRPass.jsexamples/jsm/postprocessing/RenderTransitionPass.jsexamples/jsm/postprocessing/RenderPixelatedPass.jssrc/renderers/webgl/WebGLOutput.jsUnsignedByteTypeoutput)