summaryrefslogtreecommitdiffstats
path: root/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts')
-rw-r--r--dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts162
1 files changed, 162 insertions, 0 deletions
diff --git a/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts
new file mode 100644
index 0000000000..cdd7159d15
--- /dev/null
+++ b/dom/webgpu/tests/cts/checkout/src/webgpu/api/validation/encoding/cmds/index_access.spec.ts
@@ -0,0 +1,162 @@
+export const description = `
+Validation tests for indexed draws accessing the index buffer.
+`;
+
+import { makeTestGroup } from '../../../../../common/framework/test_group.js';
+import { ValidationTest } from '../../validation_test.js';
+
+class F extends ValidationTest {
+ createIndexBuffer(indexData: Iterable<number>): GPUBuffer {
+ return this.makeBufferWithContents(new Uint32Array(indexData), GPUBufferUsage.INDEX);
+ }
+
+ createRenderPipeline(): GPURenderPipeline {
+ return this.device.createRenderPipeline({
+ layout: 'auto',
+ vertex: {
+ module: this.device.createShaderModule({
+ code: `
+ @vertex fn main() -> @builtin(position) vec4<f32> {
+ return vec4<f32>(0.0, 0.0, 0.0, 1.0);
+ }`,
+ }),
+ entryPoint: 'main',
+ },
+ fragment: {
+ module: this.device.createShaderModule({
+ code: `
+ @fragment fn main() -> @location(0) vec4<f32> {
+ return vec4<f32>(0.0, 1.0, 0.0, 1.0);
+ }`,
+ }),
+ entryPoint: 'main',
+ targets: [{ format: 'rgba8unorm' }],
+ },
+ primitive: {
+ topology: 'triangle-strip',
+ stripIndexFormat: 'uint32',
+ },
+ });
+ }
+
+ beginRenderPass(encoder: GPUCommandEncoder) {
+ const colorAttachment = this.device.createTexture({
+ format: 'rgba8unorm',
+ size: { width: 1, height: 1, depthOrArrayLayers: 1 },
+ usage: GPUTextureUsage.RENDER_ATTACHMENT,
+ });
+
+ return encoder.beginRenderPass({
+ colorAttachments: [
+ {
+ view: colorAttachment.createView(),
+ clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
+ loadOp: 'clear',
+ storeOp: 'store',
+ },
+ ],
+ });
+ }
+
+ drawIndexed(
+ indexBuffer: GPUBuffer,
+ indexCount: number,
+ instanceCount: number,
+ firstIndex: number,
+ baseVertex: number,
+ firstInstance: number,
+ isSuccess: boolean
+ ) {
+ const pipeline = this.createRenderPipeline();
+
+ const encoder = this.device.createCommandEncoder();
+ const pass = this.beginRenderPass(encoder);
+ pass.setPipeline(pipeline);
+ pass.setIndexBuffer(indexBuffer, 'uint32');
+ pass.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
+ pass.end();
+
+ if (isSuccess) {
+ this.device.queue.submit([encoder.finish()]);
+ } else {
+ this.expectValidationError(() => {
+ encoder.finish();
+ });
+ }
+ }
+}
+
+export const g = makeTestGroup(F);
+
+g.test('out_of_bounds')
+ .desc(
+ `Test drawing with out of bound index access to make sure encoder validation catch the
+ following indexCount and firstIndex OOB conditions
+ - either is within bound but indexCount + firstIndex is out of bound
+ - only firstIndex is out of bound
+ - only indexCount is out of bound
+ - firstIndex much larger than indexCount
+ - indexCount much larger than firstIndex
+ - max uint32 value for both to make sure the sum doesn't overflow
+ - max uint32 indexCount and small firstIndex
+ - max uint32 firstIndex and small indexCount
+ Together with normal and large instanceCount`
+ )
+ .params(
+ u =>
+ u
+ .combineWithParams([
+ { indexCount: 6, firstIndex: 0 }, // draw all 6 out of 6 index
+ { indexCount: 5, firstIndex: 1 }, // draw the last 5 out of 6 index
+ { indexCount: 1, firstIndex: 5 }, // draw the last 1 out of 6 index
+ { indexCount: 0, firstIndex: 6 }, // firstIndex point to the one after last, but (indexCount + firstIndex) * stride <= bufferSize, valid
+ { indexCount: 0, firstIndex: 7 }, // (indexCount + firstIndex) * stride > bufferSize, invalid
+ { indexCount: 7, firstIndex: 0 }, // only indexCount out of bound
+ { indexCount: 6, firstIndex: 1 }, // indexCount + firstIndex out of bound
+ { indexCount: 1, firstIndex: 6 }, // indexCount valid, but (indexCount + firstIndex) out of bound
+ { indexCount: 6, firstIndex: 10000 }, // firstIndex much larger than the bound
+ { indexCount: 10000, firstIndex: 0 }, // indexCount much larger than the bound
+ { indexCount: 0xffffffff, firstIndex: 0xffffffff }, // max uint32 value
+ { indexCount: 0xffffffff, firstIndex: 2 }, // max uint32 indexCount and small firstIndex
+ { indexCount: 2, firstIndex: 0xffffffff }, // small indexCount and max uint32 firstIndex
+ ] as const)
+ .combine('instanceCount', [1, 10000]) // normal and large instanceCount
+ )
+ .fn(t => {
+ const { indexCount, firstIndex, instanceCount } = t.params;
+
+ const indexBuffer = t.createIndexBuffer([0, 1, 2, 3, 1, 2]);
+ const isSuccess: boolean = indexCount + firstIndex <= 6;
+
+ t.drawIndexed(indexBuffer, indexCount, instanceCount, firstIndex, 0, 0, isSuccess);
+ });
+
+g.test('out_of_bounds_zero_sized_index_buffer')
+ .desc(
+ `Test drawing with an empty index buffer to make sure the encoder validation catch the
+ following indexCount and firstIndex conditions
+ - indexCount + firstIndex is out of bound
+ - indexCount is 0 but firstIndex is out of bound
+ - only indexCount is out of bound
+ - both are 0s (not out of bound) but index buffer size is 0
+ Together with normal and large instanceCount`
+ )
+ .params(
+ u =>
+ u
+ .combineWithParams([
+ { indexCount: 3, firstIndex: 1 }, // indexCount + firstIndex out of bound
+ { indexCount: 0, firstIndex: 1 }, // indexCount is 0 but firstIndex out of bound
+ { indexCount: 3, firstIndex: 0 }, // only indexCount out of bound
+ { indexCount: 0, firstIndex: 0 }, // just zeros, valid
+ ] as const)
+ .combine('instanceCount', [1, 10000]) // normal and large instanceCount
+ )
+ .fn(t => {
+ const { indexCount, firstIndex, instanceCount } = t.params;
+
+ const indexBuffer = t.createIndexBuffer([]);
+ const isSuccess: boolean = indexCount + firstIndex <= 0;
+
+ t.drawIndexed(indexBuffer, indexCount, instanceCount, firstIndex, 0, 0, isSuccess);
+ });