Open Bug 2078276 Opened 23 days ago Updated 16 days ago

ebGL2 shaders containing a constant Infinity or NaN fail to compile on contexts below GLSL 3.30 (maybe...?)

Categories

(Core :: Graphics: ANGLE, defect)

defect

Tracking

()

People

(Reporter: tjr, Unassigned, NeedInfo)

References

Details

In Bug 2055176 I have been debugging (with the very patient :jjaschke) a bug in https://luma.gl . They detect the Vendor /Renderer of the user and depending on what it is, might enable one or three different workaround in their code: LUMA_FP64_CODE_ELIMINATION_WORKAROUND, LUMA_FP32_TAN_PRECISION_WORKAROUND, and LUMA_FP64_HIGH_BITS_OVERFLOW_WORKAROUND.

One of those workarounds is causing flightradar24.com to break on Jan's computer. I've been having claude try to investigate and understand why, and it thinks it might have figured it out. The report is below


TOutputGLSLBase::writeFloat in third_party/angle/src/compiler/translator/glsl/OutputGLSLBase.cpp writes constant infinities and NaNs as a bit cast:

void TOutputGLSLBase::writeFloat(TInfoSinkBase &out, float f)                                                                                                                               
{                                                                                                                                                                                           
  if ((gl::isInf(f) || gl::isNaN(f)) && mShaderVersion >= 300)                                                                                                                            
  {                                                                                                                                                                                       
      out << "uintBitsToFloat(" << gl::bitCast<uint32_t>(f) << "u)";                                                                                                                      
  }                                                                                                                                                                                       
  ...                                                                                                                                                                                     
}                                                                                                                                                                                           

The guard tests mShaderVersion, which is the input ESSL version — 300 means #version 300 es. Whether uintBitsToFloat exists in the output depends on the target desktop GLSL
version: it requires GLSL 3.30, or GL_ARB_shader_bit_encoding. So for any #version 300 es shader containing a constant Inf or NaN, the translator emits a call the output language
may not define.

ANGLE already encodes the correct rule in ExtensionGLSL.cpp, keyed on mTargetVersion:

case EOpUintBitsToFloat:
  if (mTargetVersion < GLSL_VERSION_330)
      mRequiredExtensions.insert("GL_ARB_shader_bit_encoding");

That is an AST traversal over EOpUintBitsToFloat call nodes. writeFloat synthesizes the call during output, after that traversal has run, so no extension is requested and the target
version is never consulted.

Steps to reproduce

On any system with Mesa:

  1. Launch Firefox with MESA_GL_VERSION_OVERRIDE=3.2 MESA_GLSL_VERSION_OVERRIDE=150 so the WebGL2 context yields GLSL 1.50.
  2. Compile a #version 300 es fragment shader containing a constant division by zero:
#version 300 es
precision highp float;                                                                        
out vec4 o;
float f(float a){ if (a > 2.0) { return 1.0 / 0.0; } return a; }
void main(){ o = vec4(f(gl_FragCoord.x), 0, 0, 1); }
  1. Check getShaderParameter(sh, COMPILE_STATUS) and getShaderInfoLog(sh).

Actual: compilation fails. On Mesa: 0:7(9): error: no function with name 'uintBitsToFloat'. On the NVIDIA proprietary driver: error C7532: global function uintBitsToFloat requires "#version 330" or later / error C0000: ... or #extension GL_ARB_shader_bit_encoding : enable.

Expected: the shader compiles. The source contains no bit-cast function; the call is introduced by the translator.

What was tested

Read from source (third_party/angle, ANGLE revision 464fd3e879d6): TOutputGLSLBase is constructed with mShaderVersion(compiler->getShaderVersion()), the input shader version.
TExtensionGLSL is constructed with mTargetVersion(ShaderOutputTypeToGLSLVersion(output)), the output GLSL version. writeFloat gates on the former; the EOpUintBitsToFloat extension
rule gates on the latter.

Reproduced locally, with the GL version as the only variable. One machine, Mesa llvmpipe, local Firefox build. The shader above, with getTranslatedShaderSource read back via
WEBGL_debug_shaders (requires webgl.enable-privileged-extensions=true):

context translated output COMPILE_STATUS
default #version 450 true
MESA_GL_VERSION_OVERRIDE=3.2 MESA_GLSL_VERSION_OVERRIDE=150 #version 150 false

Both emit uintBitsToFloat(2139095040u) (0x7F800000 = +Inf), and neither retains the literal 1.0 / 0.0. Same GPU and driver throughout, so the output GLSL version is the sole
discriminator.

Corroborated on NVIDIA proprietary, Ubuntu (reported by the reporter of bug 2055176). Translator output language reported as #version 150, no division by zero in the translated source,
and return uintBitsToFloat(2139095040u); in its place. Compilation fails with the C7532 error above. Four source variants — bare constant, the guarded form from luma.gl, a const
initialiser, and 0.0 / 0.0 for NaN — all failed with the same error. Different vendor, different diagnostic wording, same cause.

Not tested: Windows and macOS. Only the desktop GLSL output path was examined; ESSL output should be unaffected since uintBitsToFloat exists in GLSL ES 3.00, but that is reasoning
rather than measurement. Not bisected, so there is no regression range.

Impact

Any WebGL2 content with a constant Inf or NaN anywhere in a shader fails to render on affected configurations, and the error names a function the author never wrote, which makes it
hard to diagnose from site code.

Found via deck.gl/luma.gl, whose tan_taylor_fp32 contains return 1.0 / 0.0; in a range-reduction guard. That function is compiled whenever luma.gl classifies the GPU as Intel, Apple,
or unknown, so on an affected context every layer's shader fails to build and the whole overlay disappears. See bug 2055176.

Suggested fix

Have writeFloat consult the target GLSL version rather than the input shader version — either falling back to a representable literal (e.g. clamping to FLT_MAX, as the non-Inf path
already does) or routing through the existing extension machinery so GL_ARB_shader_bit_encoding is requested when mTargetVersion < GLSL_VERSION_330.

This is upstream Chromium code, so the fix likely belongs upstream with a cherry-pick recorded in gfx/angle/cherry_picks.txt.

See Also: → 2055176

The severity field is not set for this bug.
:ahale, could you have a look please?

For more information, please visit BugBot documentation.

Flags: needinfo?(ahale)
You need to log in before you can comment on or make changes to this bug.