1export const description = `
2indexed draws validation tests.
3`;
4
5import { params, poptions, pbool } from '../../../../../common/framework/params_builder.js';
6import { makeTestGroup } from '../../../../../common/framework/test_group.js';
7
8import { ValidationTest } from './../../validation_test.js';
9
10class F extends ValidationTest {
11  createIndexBuffer(): GPUBuffer {
12    const indexArray = new Uint32Array([0, 1, 2, 3, 1, 2]);
13
14    const indexBuffer = this.device.createBuffer({
15      mappedAtCreation: true,
16      size: indexArray.byteLength,
17      usage: GPUBufferUsage.INDEX,
18    });
19    new Uint32Array(indexBuffer.getMappedRange()).set(indexArray);
20    indexBuffer.unmap();
21
22    return indexBuffer;
23  }
24
25  createRenderPipeline(): GPURenderPipeline {
26    const vertexModule = this.makeShaderModule('vertex', {
27      glsl: `
28        #version 450
29        void main() {
30          gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
31        }
32      `,
33    });
34
35    const fragmentModule = this.makeShaderModule('fragment', {
36      glsl: `
37        #version 450
38        layout(location = 0) out vec4 fragColor;
39        void main() {
40            fragColor = vec4(0.0, 1.0, 0.0, 1.0);
41        }
42      `,
43    });
44
45    return this.device.createRenderPipeline({
46      layout: this.device.createPipelineLayout({ bindGroupLayouts: [] }),
47      vertexStage: { module: vertexModule, entryPoint: 'main' },
48      fragmentStage: { module: fragmentModule, entryPoint: 'main' },
49      primitiveTopology: 'triangle-strip',
50      colorStates: [{ format: 'rgba8unorm' }],
51    });
52  }
53
54  beginRenderPass(encoder: GPUCommandEncoder) {
55    const colorAttachment = this.device.createTexture({
56      format: 'rgba8unorm',
57      size: { width: 1, height: 1, depth: 1 },
58      usage: GPUTextureUsage.OUTPUT_ATTACHMENT,
59    });
60
61    return encoder.beginRenderPass({
62      colorAttachments: [
63        {
64          attachment: colorAttachment.createView(),
65          loadValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },
66          storeOp: 'store',
67        },
68      ],
69    });
70  }
71
72  drawIndexed(
73    indexCount: number,
74    instanceCount: number,
75    firstIndex: number,
76    baseVertex: number,
77    firstInstance: number
78  ) {
79    const indexBuffer = this.createIndexBuffer();
80
81    const pipeline = this.createRenderPipeline();
82
83    const encoder = this.device.createCommandEncoder();
84    const pass = this.beginRenderPass(encoder);
85    pass.setPipeline(pipeline);
86    pass.setIndexBuffer(indexBuffer);
87    pass.drawIndexed(indexCount, instanceCount, firstIndex, baseVertex, firstInstance);
88    pass.endPass();
89
90    this.device.defaultQueue.submit([encoder.finish()]);
91  }
92
93  drawIndexedIndirect(bufferArray: Uint32Array, indirectOffset: number) {
94    const indirectBuffer = this.device.createBuffer({
95      mappedAtCreation: true,
96      size: bufferArray.byteLength,
97      usage: GPUBufferUsage.INDIRECT,
98    });
99    new Uint32Array(indirectBuffer.getMappedRange()).set(bufferArray);
100    indirectBuffer.unmap();
101
102    const indexBuffer = this.createIndexBuffer();
103
104    const pipeline = this.createRenderPipeline();
105
106    const encoder = this.device.createCommandEncoder();
107    const pass = this.beginRenderPass(encoder);
108    pass.setPipeline(pipeline);
109    pass.setIndexBuffer(indexBuffer, 0);
110    pass.drawIndexedIndirect(indirectBuffer, indirectOffset);
111    pass.endPass();
112
113    this.device.defaultQueue.submit([encoder.finish()]);
114  }
115}
116
117export const g = makeTestGroup(F);
118
119g.test('out_of_bounds')
120  .params(
121    params()
122      .combine(pbool('indirect')) // indirect drawIndexed
123      .combine([
124        { indexCount: 6, firstIndex: 1 }, // indexCount + firstIndex out of bound
125        { indexCount: 6, firstIndex: 6 }, // only firstIndex out of bound
126        { indexCount: 6, firstIndex: 10000 }, // firstIndex much larger than the bound
127        { indexCount: 7, firstIndex: 0 }, // only indexCount out of bound
128        { indexCount: 10000, firstIndex: 0 }, // indexCount much larger than the bound
129      ] as const)
130      .combine(poptions('instanceCount', [1, 10000])) // normal and large instanceCount
131  )
132  .fn(t => {
133    const { indirect, indexCount, firstIndex, instanceCount } = t.params;
134
135    if (indirect) {
136      t.drawIndexedIndirect(new Uint32Array([indexCount, instanceCount, firstIndex, 0, 0]), 0);
137    } else {
138      t.drawIndexed(indexCount, instanceCount, firstIndex, 0, 0);
139    }
140  });
141