blob: bfa8153e9880b822876a2e6bda6ebfa3ce6349e4 [file] [log] [blame]
export const description = `
Basic command buffer compute tests.
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import { checkElementsEqualGenerated } from '../../../util/check_contents.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
g.test('memcpy').fn(t => {
const data = new Uint32Array([0x01020304]);
const src = t.makeBufferWithContents(data, GPUBufferUsage.COPY_DST | GPUBufferUsage.STORAGE);
const dst = t.createBufferTracked({
size: 4,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE,
});
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: `
struct Data {
value : u32
};
@group(0) @binding(0) var<storage, read> src : Data;
@group(0) @binding(1) var<storage, read_write> dst : Data;
@compute @workgroup_size(1) fn main() {
dst.value = src.value;
return;
}
`,
}),
entryPoint: 'main',
},
});
const bg = t.device.createBindGroup({
entries: [
{ binding: 0, resource: { buffer: src, offset: 0, size: 4 } },
{ binding: 1, resource: { buffer: dst, offset: 0, size: 4 } },
],
layout: pipeline.getBindGroupLayout(0),
});
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bg);
pass.dispatchWorkgroups(1);
pass.end();
t.device.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesEqual(dst, data);
});
g.test('large_dispatch')
.desc(`Test reasonably-sized large dispatches (see also: stress tests).`)
.params(u =>
u
// Reasonably-sized powers of two, and some stranger larger sizes.
.combine('dispatchSize', [256, 2048, 315, 628, 2179, 'maximum'] as const)
// Test some reasonable workgroup sizes.
.beginSubcases()
// 0 == x axis; 1 == y axis; 2 == z axis.
.combine('largeDimension', [0, 1, 2] as const)
.expand('workgroupSize', () => [1, 2, 8, 32, 'maximum'] as const)
)
.fn(t => {
// The output storage buffer is filled with this value.
const val = 0x01020304;
const badVal = 0xbaadf00d;
const kMaxComputeWorkgroupSize = [
t.device.limits.maxComputeWorkgroupSizeX,
t.device.limits.maxComputeWorkgroupSizeY,
t.device.limits.maxComputeWorkgroupSizeZ,
];
const wgSize =
t.params.workgroupSize === 'maximum'
? kMaxComputeWorkgroupSize[t.params.largeDimension]
: t.params.workgroupSize;
const dispatchSize =
t.params.dispatchSize === 'maximum'
? t.device.limits.maxComputeWorkgroupsPerDimension
: t.params.dispatchSize;
const bufferLength = dispatchSize * wgSize;
const bufferByteSize = Uint32Array.BYTES_PER_ELEMENT * bufferLength;
const dst = t.createBufferTracked({
size: bufferByteSize,
usage: GPUBufferUsage.COPY_SRC | GPUBufferUsage.STORAGE,
});
// Only use one large dimension and workgroup size in the dispatch
// call to keep the size of the test reasonable.
const dims = [1, 1, 1];
dims[t.params.largeDimension] = dispatchSize;
const wgSizes = [1, 1, 1];
wgSizes[t.params.largeDimension] = wgSize;
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: `
struct OutputBuffer {
value : array<u32>
};
@group(0) @binding(0) var<storage, read_write> dst : OutputBuffer;
@compute @workgroup_size(${wgSizes[0]}, ${wgSizes[1]}, ${wgSizes[2]})
fn main(
@builtin(global_invocation_id) GlobalInvocationID : vec3<u32>
) {
var xExtent : u32 = ${dims[0]}u * ${wgSizes[0]}u;
var yExtent : u32 = ${dims[1]}u * ${wgSizes[1]}u;
var zExtent : u32 = ${dims[2]}u * ${wgSizes[2]}u;
var index : u32 = (
GlobalInvocationID.z * xExtent * yExtent +
GlobalInvocationID.y * xExtent +
GlobalInvocationID.x);
var val : u32 = ${val}u;
// Trivial error checking in the indexing and invocation.
if (GlobalInvocationID.x > xExtent ||
GlobalInvocationID.y > yExtent ||
GlobalInvocationID.z > zExtent) {
val = ${badVal}u;
}
dst.value[index] = val;
}
`,
}),
entryPoint: 'main',
},
});
const bg = t.device.createBindGroup({
entries: [{ binding: 0, resource: { buffer: dst, offset: 0, size: bufferByteSize } }],
layout: pipeline.getBindGroupLayout(0),
});
const encoder = t.device.createCommandEncoder();
const pass = encoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(0, bg);
pass.dispatchWorkgroups(dims[0], dims[1], dims[2]);
pass.end();
t.device.queue.submit([encoder.finish()]);
t.expectGPUBufferValuesPassCheck(dst, a => checkElementsEqualGenerated(a, _i => val), {
type: Uint32Array,
typedLength: bufferLength,
});
dst.destroy();
});