Repository navigation
Lighting: uniform buffer objects for light data (raise MAX_LIGHTS, modernize the lit shaders to GLSL ES 3.00) #1552
Description
Summary
Move the per-light uniform data (uLightDir[], uLightColor[], uLightCount, uAmbient, and the 3D equivalents) out of individual uniform arrays and into a uniform buffer object, uploaded once per frame instead of set with N gl.uniform* calls. This requires porting the lit shaders to GLSL ES 3.00, and lets MAX_LIGHTS rise well above its current value of 8.
Motivation
MAX_LIGHTS = 8 (video/webgl/lighting/constants.ts) is not a design choice — it exists to keep the GLSL uniform arrays comfortably inside conservative per-shader uniform limits. Scenes that want more than 8 simultaneous lights silently drop the extras.
A UBO removes that pressure: light data becomes one buffer bound to a block, sized by the buffer rather than by the uniform-slot budget, and updated with a single bufferSubData instead of per-light uniform calls.
Prerequisite: GLSL ES 3.00 for the lit shaders
Uniform blocks do not exist in GLSL ES 1.00 — they were introduced in ES 3.00, and WebGL 2 only accepts them in #version 300 es shaders. The lit shaders are ES 1.00 today (varying, plain uniform vec3 uLightDir[__MAX_LIGHTS__]).
So this ticket is the first legitimate trigger of the rule set in #1509: individual shaders migrate to ES 3.00 when they need an ES 3.00 feature — as opposed to a wholesale dialect rewrite, which remains explicitly out of scope (the ShaderEffect wrapper and user-authored effect bodies stay ES 1.00; mixed dialects already coexist per-program, e.g. the ES 3.00 GPU tilemap shader).
Shaders in scope:
video/webgl/shaders/mesh-lit.frag/mesh-lit.vertvideo/webgl/shaders/quad-multi-lit.vert- the
video/webgl/shaders/multitexture-lit.jsgenerator
JS side: video/webgl/lighting/pack.ts and pack3d.ts already pack light data into flat arrays, so they are the natural place to write std140-laid-out buffer contents instead.
Scope
- Port the four lit shader sources to
#version 300 es(attribute→in,varying→in/out,texture2D→texture, explicitoutfragment color).utils/precision.jsis already#version-aware;utils/attributes.jsalready parses both dialects. - Declare a
std140uniform block for the light array; bind it withbindBufferBase/uniformBlockBinding. - Have
pack.ts/pack3d.tswrite std140-compatible layouts (mindvec3padding to 16 bytes — the classic std140 trap). - Raise
MAX_LIGHTS, driven by the buffer size rather than uniform slots; pick a new cap and document it. - Visual verification of the lit paths (
spriteIlluminator,lights, the 3D lit mesh examples) — these are pixel-visible changes.
Relationship to the WebGPU backend (#1184) — honest scoping
This is not strict WebGPU groundwork, and shouldn't be justified as such:
- The WebGPU backend rewrites these shaders in WGSL regardless, so the GLSL uniform-block work does not port.
- The GL UBO API (
bindBufferBase,uniformBlockBinding) has no WebGPU counterpart either. - What does transfer is the JS-side model: pack a struct into a typed array and upload it once, rather than issuing N loose uniform calls. That is exactly the WebGPU shape (write a buffer, bind a bind group), so the packing code in
pack.ts/pack3d.tsgains lasting value.
Contrast with #1551, where the artifact being made neutral is a shared JS record both backends read directly — that one transfers whole. This one is mostly a WebGL 2 improvement in its own right, with a WebGPU-shaped side effect.
Scheduling
No ordering constraint — nothing in #1507, #1508, #1551 or #1184 depends on this landing first. Best picked up whenever lighting work comes up naturally. The headline is "more lights, modernized lit shaders"; WebGPU-shaped uniform packing is a bonus, not the reason.
Related: #1509 (established the "migrate a shader to ES 3.00 only when it needs a 3.00 feature" rule), #1184, #1536.
Activity
feat(video): light data in a std140 uniform buffer, 32-light cap (mel…
refactor(video): allocate uniform buffer binding points from the rend…