bindUniformBlock method

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

Fills the block called blockName and binds it. False where shader declares no such block.

The two halves of the contract's rule, kept apart: a block the translator dropped because nothing read it is an ordinary false, and a block that exists without a member the caller named throws — the two ends disagree about its shape, and zeros are a plausible value for most of what goes through here.

The bytes go into the frame's uniform arena and the offset is remembered for the draw. Nothing is bound to the pass yet: a GPUBindGroup is every binding of one group at once, and the rest of this group may still be coming.

Implementation

@override
bool bindUniformBlock(
  ShaderHandle shader,
  String blockName,
  Map<String, Float32List> members,
) {
  final stage = shader.backend as WebGpuShader;
  final block = stage.blockNamed(blockName);
  if (block == null) return false;

  final data = Float32List(block.sizeInBytes ~/ 4);
  for (final entry in members.entries) {
    final member = _memberOf(block, entry.key);
    final at = member.offsetInBytes ~/ 4;
    if (at + entry.value.length > data.length) {
      throw StateError(
        'uniform block "$blockName" member "${entry.key}" wants '
        '${entry.value.length} floats at offset $at, past the block\'s '
        '${data.length}. std140 pads array elements to sixteen bytes; a '
        'tightly packed array of scalars overruns exactly like this',
      );
    }
    data.setRange(at, at + entry.value.length, entry.value);
  }

  _blocks.putIfAbsent(
    block.group,
    () => <int, WebGpuSlice>{},
  )[block.binding] = _device.uniformArena.write(
    ByteData.sublistView(data),
  );
  return true;
}