in src/webgpu/shader/execution/expression/call/builtin/atomics/harness.ts [114:206]
export function runWorkgroupVariableTest({
t,
workgroupSize, // Workgroup X-size
dispatchSize, // Dispatch X-size
wgNumElements, // Number of 32-bit elements in 'wg' array. Output buffer is sized to wgNumElements * dispatchSize.
initValue, // 32-bit initial value used to fill 'wg' array
// Atomic op source executed by the compute shader, NOTE: 'id' is local_invocation_index,
// `wg` is a workgroup array of atomics of size `workgroupSize`, `output` is a storage array of non-atomics of size
// `workgroupSize * dispatcSize` to which each dispatch of `wg` gets copied to (dispatch 0 to first workgroupSize elements,
// dispatch 1 to second workgroupSize elements, etc.).
op,
expected, // Expected values array to compare against output buffer
extra, // Optional extra WGSL source
}: {
t: GPUTest;
workgroupSize: number;
dispatchSize: number;
wgNumElements: number;
initValue: number;
op: string;
expected: TypedArrayBufferView;
extra?: string;
}) {
assert(expected.length === wgNumElements * dispatchSize, "'expected' buffer size is incorrect");
const scalarType = expected instanceof Uint32Array ? 'u32' : 'i32';
const arrayType = typedArrayCtor(scalarType);
const wgsl = `
var<workgroup> wg: array<atomic<${scalarType}>, ${wgNumElements}>;
// Result of each workgroup is written to output[workgroup_id.x]
@group(0) @binding(0)
var<storage, read_write> output: array<${scalarType}, ${wgNumElements * dispatchSize}>;
@compute @workgroup_size(${workgroupSize})
fn main(
@builtin(local_invocation_index) local_invocation_index: u32,
@builtin(workgroup_id) workgroup_id : vec3<u32>
) {
let id = ${scalarType}(local_invocation_index);
let global_id = ${scalarType}(workgroup_id.x * ${wgNumElements} + local_invocation_index);
// Initialize workgroup array
if (local_invocation_index == 0) {
for (var i = 0u; i < ${wgNumElements}; i++) {
atomicStore(&wg[i], bitcast<${scalarType}>(${initValue}u));
}
}
workgroupBarrier();
${op};
// Copy results to output buffer
workgroupBarrier();
if (local_invocation_index == 0) {
for (var i = 0u; i < ${wgNumElements}; i++) {
output[(workgroup_id.x * ${wgNumElements}) + i] = atomicLoad(&wg[i]);
}
}
}
${extra || ''}
`;
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({ code: wgsl }),
entryPoint: 'main',
},
});
const outputBuffer = t.createBufferTracked({
size: wgNumElements * dispatchSize * arrayType.BYTES_PER_ELEMENT,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
});
const bindGroup = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer: outputBuffer } }],
});
// Run the shader.
const encoder = t.device.createCommandEncoder({ label: 'runWorkgroupVariableTest' });
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(dispatchSize);
pass.end();
t.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(outputBuffer, expected);
}