Skip to content

Bounds Guards

Volten normally protects kernels from extra invocations at the edge of a dispatch.

const kernel = new Kernel(`
fn main(gid: vec3u) {
data[gid.x] = data[gid.x] * 2.0;
}
`);

If data.count is 100 and the workgroup size is 64, WebGPU must dispatch two workgroups. That creates up to 128 invocations. Volten injects a guard so invocations 100..127 return before calling your code.

Most kernels are written against the logical data size, not the rounded-up dispatch size.

fn main(gid: vec3u) {
data[gid.x] = data[gid.x] * 2.0;
}

Without a guard, gid.x could go past the end of data.

Advanced kernels can opt out.

const kernel = new Kernel(
`
fn main(gid: vec3u) {
if (gid.x >= count) {
return;
}
data[gid.x] = data[gid.x] * 2.0;
}
`,
{
unsafeManualBounds: true,
threads: 128
}
);
const count = new Uniform(data.count, 'u32');
const node = v.pass(kernel, { data, count });

Use this only when your WGSL handles every out-of-range invocation correctly.

Kernels that use workgroupBarrier() or storageBarrier() need special care.

fn main(gid: vec3u, lid: u32) {
workgroupBarrier();
}

Volten’s automatic guard returns early before your code runs. That is unsafe for some barrier patterns, because not every invocation reaches the same barrier.

If a barrier kernel would need a partial guarded workgroup, Volten throws. Fix it by making the thread count an exact multiple of workgroupSize, or by using unsafeManualBounds: true and handling bounds manually.