bindUniformBlock method

  1. @override
bool bindUniformBlock(
  1. ShaderHandle shader,
  2. String blockName,
  3. Map<String, Float32List> members
)
override

Fills the uniform block blockName of shader from members and binds it. False when the shader has no such block.

A block the compiler dropped is false; a block missing a member the caller named throws. The two look alike and are not: a compiler drops a whole block nothing reads, which is ordinary and which the caller is entitled to shrug at, but a block that exists without one of the members written into it means the two ends disagree about its shape. Zeros are a plausible value for most of what goes through here — a shadow strength of zero is a scene with no shadows and no error — so silence there is indistinguishable from working.

That distinction is load-bearing in the other direction too: binding a block a compiled shader does not have takes the process down with no Dart stack, which is why the block case is a return value rather than a throw.

This doc comment used to say the opposite, on the grounds that "the unlit model legitimately has no light_direction". It has one: FragInfo is declared once, in lib/surface.glsl, and every shader that reads any of it includes the whole block. The web backend threw all along and drew every golden, which is what settled it — the same bundle was a named exception in a browser and a quietly wrong picture on a phone.

A backend with no reflection of its shaders cannot tell the two apart and is not asked to: the software rasteriser hands the block to a Dart shader that looks up what it needs by name, so a member nobody reads costs a map entry. What is forbidden is a backend that can see the disagreement and says nothing.

So the return value is true unconditionally on such a backend, and a caller branching on it must know that. There is nothing for the software rasteriser to answer false about — a Dart stage declares no blocks, so "the compiler dropped this one" is not a state it has — and returning false for a block it had merely never been handed would be inventing an answer. The value is therefore a report from the backends that reflect and a constant from the one that does not, which is the honest shape and not a forgotten branch. The conformance check a block missing a member the caller named is refused uses exactly this to tell the two kinds of backend apart before holding either to the member rule.

Every uniform in this engine is a float vector, a matrix or an array of either, which is why Float32List is the only value type. Integer and boolean uniforms are encoded as floats in the shaders on purpose.

Implementation

@override
bool bindUniformBlock(
  ShaderHandle shader,
  String blockName,
  Map<String, Float32List> members,
) {
  commands.add(RecordedUniformBlock(shader, blockName, members));
  return true;
}