blob: 53edd63a724d9df69e2852d756aabc4996a2a0ea [file] [log] [blame]
/**
* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
**/ import { assert } from '../../../../../common/framework/util/util.js';
import { GPUTest } from '../../../../gpu_test.js';
const kSize = 4;
export const kAllWriteOps = ['render', 'render-via-bundle', 'compute', 'b2b-copy', 't2b-copy'];
// Note: If it would be useful to have any of these helpers be separate from the fixture,
// they can be refactored into standalone functions.
export class BufferSyncTest extends GPUTest {
// Create a buffer, and initialize it to a specified value for all elements.
async createBufferWithValue(initValue) {
const fence = this.queue.createFence();
const buffer = this.device.createBuffer({
mappedAtCreation: true,
size: kSize,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE,
});
new Uint32Array(buffer.getMappedRange()).fill(initValue);
buffer.unmap();
this.queue.signal(fence, 1);
await fence.onCompletion(1);
return buffer;
}
// Create a texture, and initialize it to a specified value for all elements.
async createTextureWithValue(initValue) {
const fence = this.queue.createFence();
const data = new Uint32Array(kSize / 4).fill(initValue);
const texture = this.device.createTexture({
size: { width: kSize / 4, height: 1, depth: 1 },
format: 'r32uint',
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.COPY_DST,
});
this.device.defaultQueue.writeTexture(
{ texture, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
data,
{ offset: 0, bytesPerRow: kSize, rowsPerImage: 1 },
{ width: kSize / 4, height: 1, depth: 1 }
);
this.queue.signal(fence, 1);
await fence.onCompletion(1);
return texture;
}
createBindGroup(pipeline, buffer) {
return this.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [{ binding: 0, resource: { buffer } }],
});
}
// Create a compute pipeline and write given data into storage buffer.
createStorageWriteComputePipeline(value) {
const wgslCompute = `
[[block]] struct Data {
[[offset(0)]] a : i32;
};
[[set(0), binding(0)]] var<storage_buffer> data : Data;
[[stage(compute)]] fn main() -> void {
data.a = ${value};
return;
}
`;
return this.device.createComputePipeline({
computeStage: {
module: this.device.createShaderModule({
code: wgslCompute,
}),
entryPoint: 'main',
},
});
}
// Create a render pipeline and write given data into storage buffer at fragment stage.
createStorageWriteRenderPipeline(value) {
const wgslShaders = {
vertex: `
[[builtin(position)]] var<out> Position : vec4<f32>;
[[stage(vertex)]] fn vert_main() -> void {
Position = vec4<f32>(0.5, 0.5, 0.0, 1.0);
return;
}
`,
fragment: `
[[location(0)]] var<out> outColor : vec4<f32>;
[[block]] struct Data {
[[offset(0)]] a : i32;
};
[[set(0), binding(0)]] var<storage_buffer> data : Data;
[[stage(fragment)]] fn frag_main() -> void {
data.a = ${value};
outColor = vec4<f32>(1.0, 0.0, 0.0, 1.0);
return;
}
`,
};
return this.device.createRenderPipeline({
vertexStage: {
module: this.device.createShaderModule({
code: wgslShaders.vertex,
}),
entryPoint: 'vert_main',
},
fragmentStage: {
module: this.device.createShaderModule({
code: wgslShaders.fragment,
}),
entryPoint: 'frag_main',
},
primitiveTopology: 'point-list',
colorStates: [{ format: 'rgba8unorm' }],
});
}
beginSimpleRenderPass(encoder) {
const view = this.device
.createTexture({
size: { width: 1, height: 1, depth: 1 },
format: 'rgba8unorm',
usage: GPUTextureUsage.OUTPUT_ATTACHMENT,
})
.createView();
return encoder.beginRenderPass({
colorAttachments: [
{
attachment: view,
loadValue: { r: 0.0, g: 1.0, b: 0.0, a: 1.0 },
},
],
});
}
// Write buffer via draw call in render pass. Use bundle if needed.
encodeWriteAsStorageBufferInRenderPass(encoder, buffer, value, inBundle) {
const pipeline = this.createStorageWriteRenderPipeline(value);
const bindGroup = this.createBindGroup(pipeline, buffer);
const pass = this.beginSimpleRenderPass(encoder);
const renderer = inBundle
? this.device.createRenderBundleEncoder({ colorFormats: ['rgba8unorm'] })
: pass;
renderer.setBindGroup(0, bindGroup);
renderer.setPipeline(pipeline);
renderer.draw(1, 1, 0, 0);
if (inBundle) pass.executeBundles([renderer.finish()]);
pass.endPass();
}
// Write buffer via dispatch call in compute pass.
encodeWriteAsStorageBufferInComputePass(encoder, buffer, value) {
const pipeline = this.createStorageWriteComputePipeline(value);
const bindGroup = this.createBindGroup(pipeline, buffer);
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatch(1);
pass.endPass();
}
/** Write buffer via BufferToBuffer copy. */
async encodeWriteByB2BCopy(encoder, buffer, value) {
const tmpBuffer = await this.createBufferWithValue(value);
// The write operation via b2b copy is just encoded into command encoder, it doesn't write immediately.
encoder.copyBufferToBuffer(tmpBuffer, 0, buffer, 0, kSize);
}
// Write buffer via TextureToBuffer copy.
async encodeWriteByT2BCopy(encoder, buffer, value) {
const tmpTexture = await this.createTextureWithValue(value);
// The write operation via t2b copy is just encoded into command encoder, it doesn't write immediately.
encoder.copyTextureToBuffer(
{ texture: tmpTexture, mipLevel: 0, origin: { x: 0, y: 0, z: 0 } },
{ buffer, bytesPerRow: 256 },
{ width: 1, height: 1, depth: 1 }
);
}
// Write buffer via writeBuffer API on queue
writeByWriteBuffer(buffer, value) {
const data = new Uint32Array(kSize / 4).fill(value);
this.device.defaultQueue.writeBuffer(buffer, 0, data);
}
// Issue write operation via render pass, compute pass, copy, etc.
async encodeWriteOp(encoder, writeOp, buffer, value) {
switch (writeOp) {
case 'render':
this.encodeWriteAsStorageBufferInRenderPass(encoder, buffer, value, false);
break;
case 'render-via-bundle':
this.encodeWriteAsStorageBufferInRenderPass(encoder, buffer, value, true);
break;
case 'compute':
this.encodeWriteAsStorageBufferInComputePass(encoder, buffer, value);
break;
case 'b2b-copy':
await this.encodeWriteByB2BCopy(encoder, buffer, value);
break;
case 't2b-copy':
await this.encodeWriteByT2BCopy(encoder, buffer, value);
break;
default:
assert(false);
}
}
async createCommandBufferWithWriteOp(writeOp, buffer, value) {
const encoder = this.device.createCommandEncoder();
await this.encodeWriteOp(encoder, writeOp, buffer, value);
return encoder.finish();
}
async submitWriteOp(writeOp, buffer, value) {
if (writeOp === 'write-buffer') {
this.writeByWriteBuffer(buffer, value);
} else {
const encoder = this.device.createCommandEncoder();
await this.encodeWriteOp(encoder, writeOp, buffer, value);
this.device.defaultQueue.submit([encoder.finish()]);
}
}
verifyData(buffer, expectedValue) {
const bufferData = new Uint32Array(1);
bufferData[0] = expectedValue;
this.expectContents(buffer, bufferData);
}
verifyDataTwoValidValues(buffer, expectedValue1, expectedValue2) {
const bufferData1 = new Uint32Array(1);
bufferData1[0] = expectedValue1;
const bufferData2 = new Uint32Array(1);
bufferData2[0] = expectedValue2;
this.expectContentsTwoValidValues(buffer, bufferData1, bufferData2);
}
}