Skip to content

WebGL/WebGL2: any integer-valued number ≥ 1e21 emits invalid GLSL (1e+30.0) #864

Description

@fuzzie360

Summary

On the WebGL and WebGL2 backends, an integer-valued number of 1e21 or larger produces a fragment shader that fails to compile:

Error compiling fragment shader: ERROR: 0:518: '.0' : syntax error

The generated GLSL contains 1e+30.0, which is not a valid float literal.

1e30 is a natural thing to write — it is the usual "infinitely far away" sentinel in a ray marcher or path tracer, which is where I hit it.

Reproduction

Verified on gpu.js 2.21.0, Chrome 150, Apple M1 Max (apple metal-3), via dist/gpu-browser.js.

// FAILS — literal in the kernel body
new GPU({ mode: 'webgl2' })
  .createKernel(function () { return 1e30; })
  .setOutput([1])();

// FAILS — value passed through setConstants
new GPU({ mode: 'webgl2' })
  .createKernel(function () { return this.constants.big * 1; })
  .setConstants({ big: 1e30 })
  .setOutput([1])();

// OK — runtime argument, uploaded as a uniform rather than emitted into source
new GPU({ mode: 'webgl2' })
  .createKernel(function (v) { return v * 1; })
  .setOutput([1])(1e30);

Affected backends

backend 1e30 1e21 1e20 123456
webgl2 FAIL FAIL ok ok
webgl FAIL FAIL ok ok
webgpu ok ok ok ok

Cause

The threshold is exactly 1e21, which is where Number.prototype.toString() switches to exponential notation:

(1e20).toString()  // "100000000000000000000"  -> "100000000000000000000.0"  valid
(1e21).toString()  // "1e+21"                  -> "1e+21.0"                  invalid

src/backend/web-gl/function-node.js (around line 237) appends .0 whenever the value is an integer:

if (Number.isInteger(ast.value)) {
  ...
  retArr.push(`${ast.value}.0`);

Number.isInteger(1e30) is true, so the .0 is appended to a string that is already in exponential form.

Suggested fix

Only append .0 when the rendered string does not already make the literal a float. GLSL accepts 1e30 as a float literal, so the exponential form can pass through unchanged:

const s = `${ast.value}`;
retArr.push(/[.eE]/.test(s) ? s : `${s}.0`);

The same ${value}.0 pattern appears in src/backend/function-node.js and src/backend/web-gpu/function-node.js; I only observed the failure on the two WebGL backends, so I have not checked whether the others are reachable with the same input.

Workaround

Keep sentinels below 1e211e20 renders as digits and works, and for most scenes something like 1e9 is plenty.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions