blob: ba0476e55ad1ed27d7df19298ec838877e3101a4 [file] [log] [blame]
/**
* AUTO-GENERATED - DO NOT EDIT. Source: https://github.com/gpuweb/cts
**/ export const description = `
setBindGroup validation tests.
TODO: merge these notes and implement.
> (Note: If there are errors with using certain binding types in certain passes, test those in the file for that pass type, not here.)
>
> All x= {compute pass, render pass, render bundle}
>
> - setBindGroup
> - x= {compute pass, render pass}
> - index {0, max, max+1}
> - GPUBindGroup object {valid, invalid, valid but refers to destroyed {buffer, texture}}
> - bind group {with, without} dynamic offsets with {too few, too many} dynamicOffsets entries
> - x= {sequence, Uint32Array} overload
> - iff minBufferBindingSize is specified, buffer size is correctly validated against it (make sure static offset + dynamic offset are both accounted for)
> - state tracking (probably separate file)
> - x= {compute pass, render pass}
> - {null, compatible, incompatible} current pipeline (should have no effect without draw/dispatch)
> - setBindGroup in different orders (e.g. 0,1,2 vs 2,0,1)
`;
import { poptions, params } from '../../../../../common/framework/params_builder.js';
import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { ValidationTest } from '../../validation_test.js';
class F extends ValidationTest {
makeAttachmentTexture() {
return this.device.createTexture({
format: 'rgba8unorm',
size: { width: 16, height: 16, depth: 1 },
usage: GPUTextureUsage.OUTPUT_ATTACHMENT,
});
}
testComputePass(bindGroup, dynamicOffsets) {
const encoder = this.device.createCommandEncoder();
const computePass = encoder.beginComputePass();
computePass.setBindGroup(0, bindGroup, dynamicOffsets);
computePass.endPass();
encoder.finish();
}
testRenderPass(bindGroup, dynamicOffsets) {
const encoder = this.device.createCommandEncoder();
const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
attachment: this.makeAttachmentTexture().createView(),
loadValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 },
},
],
});
renderPass.setBindGroup(0, bindGroup, dynamicOffsets);
renderPass.endPass();
encoder.finish();
}
testRenderBundle(bindGroup, dynamicOffsets) {
const encoder = this.device.createRenderBundleEncoder({
colorFormats: ['rgba8unorm'],
});
encoder.setBindGroup(0, bindGroup, dynamicOffsets);
encoder.finish();
}
}
export const g = makeTestGroup(F);
g.test('dynamic_offsets_passed_but_not_expected,compute_pass')
.params(poptions('type', ['compute', 'renderpass', 'renderbundle']))
.fn(async t => {
const bindGroupLayout = t.device.createBindGroupLayout({ entries: [] });
const bindGroup = t.device.createBindGroup({ layout: bindGroupLayout, entries: [] });
const { type } = t.params;
const dynamicOffsets = [0];
t.expectValidationError(() => {
if (type === 'compute') {
const encoder = t.device.createCommandEncoder();
const computePass = encoder.beginComputePass();
computePass.setBindGroup(0, bindGroup, dynamicOffsets);
computePass.endPass();
encoder.finish();
} else if (type === 'renderpass') {
const encoder = t.device.createCommandEncoder();
const renderPass = encoder.beginRenderPass({
colorAttachments: [
{
attachment: t.makeAttachmentTexture().createView(),
loadValue: { r: 1.0, g: 0.0, b: 0.0, a: 1.0 },
},
],
});
renderPass.setBindGroup(0, bindGroup, dynamicOffsets);
renderPass.endPass();
encoder.finish();
} else if (type === 'renderbundle') {
const encoder = t.device.createRenderBundleEncoder({
colorFormats: ['rgba8unorm'],
});
encoder.setBindGroup(0, bindGroup, dynamicOffsets);
encoder.finish();
} else {
t.fail();
}
});
});
g.test('dynamic_offsets_match_expectations_in_pass_encoder')
.params(
params()
.combine(poptions('type', ['compute', 'renderpass', 'renderbundle']))
.combine([
{ dynamicOffsets: [256, 0], _success: true }, // Dynamic offsets aligned
{ dynamicOffsets: [1, 2], _success: false }, // Dynamic offsets not aligned
// Wrong number of dynamic offsets
{ dynamicOffsets: [256, 0, 0], _success: false },
{ dynamicOffsets: [256], _success: false },
{ dynamicOffsets: [], _success: false },
// Dynamic uniform buffer out of bounds because of binding size
{ dynamicOffsets: [512, 0], _success: false },
{ dynamicOffsets: [1024, 0], _success: false },
{ dynamicOffsets: [0xffffffff, 0], _success: false },
// Dynamic storage buffer out of bounds because of binding size
{ dynamicOffsets: [0, 512], _success: false },
{ dynamicOffsets: [0, 1024], _success: false },
{ dynamicOffsets: [0, 0xffffffff], _success: false },
])
)
.fn(async t => {
// Dynamic buffer offsets require offset to be divisible by 256
const MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT = 256;
const BINDING_SIZE = 9;
const bindGroupLayout = t.device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
type: 'uniform-buffer',
hasDynamicOffset: true,
},
{
binding: 1,
visibility: GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,
type: 'storage-buffer',
hasDynamicOffset: true,
},
],
});
const uniformBuffer = t.device.createBuffer({
size: 2 * MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT + 8,
usage: GPUBufferUsage.UNIFORM,
});
const storageBuffer = t.device.createBuffer({
size: 2 * MIN_DYNAMIC_BUFFER_OFFSET_ALIGNMENT + 8,
usage: GPUBufferUsage.STORAGE,
});
const bindGroup = t.device.createBindGroup({
layout: bindGroupLayout,
entries: [
{
binding: 0,
resource: {
buffer: uniformBuffer,
size: BINDING_SIZE,
},
},
{
binding: 1,
resource: {
buffer: storageBuffer,
size: BINDING_SIZE,
},
},
],
});
const { type, dynamicOffsets, _success } = t.params;
t.expectValidationError(() => {
if (type === 'compute') {
t.testComputePass(bindGroup, dynamicOffsets);
} else if (type === 'renderpass') {
t.testRenderPass(bindGroup, dynamicOffsets);
} else if (type === 'renderbundle') {
t.testRenderBundle(bindGroup, dynamicOffsets);
} else {
t.fail();
}
t.testComputePass(bindGroup, dynamicOffsets);
}, !_success);
});
// TODO: test error bind group