override Constants

Pipeline-Overridable Constants with override

An override is a module-scope constant fixed when a pipeline is created. The stage's constants record sets it by name or, with @id(n), by number, and a default makes it optional. One module yields several specialized pipelines, and overrides can even size workgroups:

One module, several pipelines specialized with override constantsHTMLLive
<script type="module">
const device = await (await navigator.gpu.requestAdapter()).requestDevice();
const module = device.createShaderModule({ code: /* wgsl */ `
  override percentOff: f32 = 0;                   // set per pipeline, by name
  @id(7) override size: u32 = 64;                 // or by numeric id
  const prices = array(14.99, 39.50, 24.00, 18.75, 16.20, 21.30);
  @group(0) @binding(0) var<storage, read_write> out: array<f32, 6>;
  @compute @workgroup_size(size) fn main(@builtin(local_invocation_index) i: u32) {
    if (i < 6) { out[i] = round(prices[i] * (100 - percentOff)) / 100; }
  }` });
const { STORAGE, COPY_SRC, COPY_DST, MAP_READ } = GPUBufferUsage;
const out = device.createBuffer({ size: 24, usage: STORAGE | COPY_SRC });
async function run(constants) {
  device.pushErrorScope('validation');
  const pipeline = device.createComputePipeline({ layout: 'auto',
    compute: { module, constants } });
  const error = await device.popErrorScope(), label = JSON.stringify(constants);
  if (error) return console.log(`${label}: ${error.message.split('\n')[0].split('. ')[0]}`);
  const read = device.createBuffer({ size: 24, usage: COPY_DST | MAP_READ });
  const encoder = device.createCommandEncoder(), pass = encoder.beginComputePass();
  pass.setPipeline(pipeline);
  pass.setBindGroup(0, device.createBindGroup({ layout: pipeline.getBindGroupLayout(0),
    entries: [{ binding: 0, resource: { buffer: out } }] }));
  pass.dispatchWorkgroups(1), pass.end();
  encoder.copyBufferToBuffer(out, 0, read, 0, 24);
  device.queue.submit([encoder.finish()]), await read.mapAsync(GPUMapMode.READ);
  const prices = [...new Float32Array(read.getMappedRange())].map((p) => p.toFixed(2));
  console.log(`${label}: ${prices}`);
}
await run({});
await run({ percentOff: 15, 7: 8 });
await run({ 7: 512 });
</script>

15% off with 8-wide workgroups gave Shared Book Struct's sale prices. A size of 512 failed only at pipeline creation, against the default limit of 256; { size: 8 } failed too, since an @id override answers only to its number. Use overrides for per-pipeline choices (quality, workgroup size, features) and uniforms for per-frame values.