in src/webgpu/shader/execution/expression/call/builtin/subgroup_util.ts [480:587]
export async function runFragmentTest(
t: SubgroupTest,
format: GPUTextureFormat,
fsShader: string,
width: number,
height: number,
inputData: Uint32Array | Float32Array | Float16Array,
checker: (data: Uint32Array) => Error | undefined
) {
const vsShader = `
@vertex
fn vsMain(@builtin(vertex_index) index : u32) -> @builtin(position) vec4f {
const vertices = array(
vec2(-2, 4), vec2(-2, -4), vec2(2, 0),
);
return vec4f(vec2f(vertices[index]), 0, 1);
}`;
assert(width >= 3, 'Minimum width is 3');
assert(height >= 3, 'Minimum height is 3');
const pipeline = t.device.createRenderPipeline({
layout: 'auto',
vertex: {
module: t.device.createShaderModule({ code: vsShader }),
},
fragment: {
module: t.device.createShaderModule({ code: fsShader }),
targets: [{ format }],
},
primitive: {
topology: 'triangle-list',
},
});
const { blockWidth, blockHeight, bytesPerBlock } = getBlockInfoForTextureFormat(format);
assert(bytesPerBlock !== undefined);
const blocksPerRow = width / blockWidth;
const blocksPerColumn = height / blockHeight;
// 256 minimum arises from image copy requirements.
const bytesPerRow = align(blocksPerRow * (bytesPerBlock ?? 1), 256);
const byteLength = bytesPerRow * blocksPerColumn;
const uintLength = byteLength / 4;
const expandedInputData = new (
inputData instanceof Uint32Array
? Uint32Array
: inputData instanceof Float32Array
? Float32Array
: Float16Array
)(inputData.length * 4);
for (let i = 0; i < inputData.length; ++i) {
expandedInputData[i * 4] = inputData[i];
}
const buffer = t.makeBufferWithContents(
expandedInputData,
GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST
);
const bg = t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: {
buffer,
},
},
],
});
const framebuffer = t.createTextureTracked({
size: [width, height],
usage:
GPUTextureUsage.COPY_SRC |
GPUTextureUsage.COPY_DST |
GPUTextureUsage.RENDER_ATTACHMENT |
GPUTextureUsage.TEXTURE_BINDING,
format,
});
const encoder = t.device.createCommandEncoder({ label: 'runFragmentTest' });
const pass = encoder.beginRenderPass({
colorAttachments: [
{
view: framebuffer.createView(),
loadOp: 'clear',
storeOp: 'store',
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(0, bg);
pass.draw(3);
pass.end();
t.queue.submit([encoder.finish()]);
const copyBuffer = t.copyWholeTextureToNewBufferSimple(framebuffer, 0);
const readback = await t.readGPUBufferRangeTyped(copyBuffer, {
srcByteOffset: 0,
type: Uint32Array,
typedLength: uintLength,
method: 'copy',
});
const data: Uint32Array = readback.data;
t.expectOK(checker(data));
}