bindTexture method

  1. @override
void bindTexture(
  1. ShaderHandle shader,
  2. String slot,
  3. TextureHandle texture, {
  4. SamplerOptions? sampler,
})

Binds texture to the sampler called slot in shader.

One name becomes two bindings, which is the shape of the whole problem this package's GLSL is edited to solve: sampler2D is one object in GLSL and two in WGSL, as it is in Vulkan and Metal, so the reflection carries the pair and this splits the bind across them.

A slot the translator dropped is ignored rather than refused, which is what the WebGL2 backend does for the same reason: the engine gates its call sites on what a material's lighting model declares, and a stage that legitimately optimised a sampler away is not a caller mistake.

A null sampler is SamplerOptions.linearRepeat — the contract's default, not the constructor's, which is nearest and clamp and which cost a third backend two percent of every textured golden before the rule was written down.

Implementation

@override
void bindTexture(
  ShaderHandle shader,
  String slot,
  TextureHandle texture, {
  SamplerOptions? sampler,
}) {
  final stage = shader.backend as WebGpuShader;
  final declared = stage.samplerNamed(slot);
  if (declared == null) return;

  final backend = texture.backend as WebGpuTexture;
  assert(
    backend.sampleable,
    'the "$slot" slot was handed a texture that is multisampled or '
    'deviceTransient, which on this backend is allocated without '
    'TEXTURE_BINDING and can only ever be an attachment',
  );
  _views.putIfAbsent(
    declared.group,
    () => <int, GPUTextureView>{},
  )[declared.textureBinding] = backend.sampledView;
  _samplers.putIfAbsent(
    declared.group,
    () => <int, GPUSampler>{},
  )[declared.samplerBinding] = _device.samplerFor(
    sampler ?? SamplerOptions.linearRepeat,
  );
}