• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 The Dawn Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "tests/unittests/validation/ValidationTest.h"
16 
17 #include "common/Constants.h"
18 #include "utils/ComboRenderPipelineDescriptor.h"
19 #include "utils/WGPUHelpers.h"
20 
21 namespace {
22     constexpr uint32_t kRTSize = 4;
23     constexpr uint32_t kFloat32x2Stride = 2 * sizeof(float);
24     constexpr uint32_t kFloat32x4Stride = 4 * sizeof(float);
25 
26     class DrawVertexAndIndexBufferOOBValidationTests : public ValidationTest {
27       public:
28         // Parameters for testing index buffer
29         struct IndexBufferParams {
30             wgpu::IndexFormat indexFormat;
31             uint64_t indexBufferSize;              // Size for creating index buffer
32             uint64_t indexBufferOffsetForEncoder;  // Offset for SetIndexBuffer in encoder
33             uint64_t indexBufferSizeForEncoder;    // Size for SetIndexBuffer in encoder
34             uint32_t maxValidIndexNumber;  // max number of {indexCount + firstIndex} for this set
35                                            // of parameters
36         };
37 
38         // Parameters for testing vertex-step-mode and instance-step-mode vertex buffer
39         struct VertexBufferParams {
40             uint32_t bufferStride;
41             uint64_t bufferSize;              // Size for creating vertex buffer
42             uint64_t bufferOffsetForEncoder;  // Offset for SetVertexBuffer in encoder
43             uint64_t bufferSizeForEncoder;    // Size for SetVertexBuffer in encoder
44             uint32_t maxValidAccessNumber;    // max number of valid access time for this set of
45                                               // parameters, i.e. {vertexCount + firstVertex} for
46             // vertex-step-mode, and {instanceCount + firstInstance}
47             // for instance-step-mode
48         };
49 
50         // Parameters for setIndexBuffer
51         struct IndexBufferDesc {
52             const wgpu::Buffer buffer;
53             wgpu::IndexFormat indexFormat;
54             uint64_t offset = 0;
55             uint64_t size = wgpu::kWholeSize;
56         };
57 
58         // Parameters for setVertexBuffer
59         struct VertexBufferSpec {
60             uint32_t slot;
61             const wgpu::Buffer buffer;
62             uint64_t offset = 0;
63             uint64_t size = wgpu::kWholeSize;
64         };
65         using VertexBufferList = std::vector<VertexBufferSpec>;
66 
67         // Buffer layout parameters for creating pipeline
68         struct PipelineVertexBufferAttributeDesc {
69             uint32_t shaderLocation;
70             wgpu::VertexFormat format;
71             uint64_t offset = 0;
72         };
73         struct PipelineVertexBufferDesc {
74             uint64_t arrayStride;
75             wgpu::VertexStepMode stepMode;
76             std::vector<PipelineVertexBufferAttributeDesc> attributes = {};
77         };
78 
SetUp()79         void SetUp() override {
80             ValidationTest::SetUp();
81 
82             renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
83 
84             fsModule = utils::CreateShaderModule(device, R"(
85             [[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
86                 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
87             })");
88         }
89 
GetBasicRenderPassDescriptor() const90         const wgpu::RenderPassDescriptor* GetBasicRenderPassDescriptor() const {
91             return &renderPass.renderPassInfo;
92         }
93 
CreateBuffer(uint64_t size,wgpu::BufferUsage usage=wgpu::BufferUsage::Vertex)94         wgpu::Buffer CreateBuffer(uint64_t size,
95                                   wgpu::BufferUsage usage = wgpu::BufferUsage::Vertex) {
96             wgpu::BufferDescriptor descriptor;
97             descriptor.size = size;
98             descriptor.usage = usage;
99 
100             return device.CreateBuffer(&descriptor);
101         }
102 
CreateVertexShaderModuleWithBuffer(std::vector<PipelineVertexBufferDesc> bufferDescList)103         wgpu::ShaderModule CreateVertexShaderModuleWithBuffer(
104             std::vector<PipelineVertexBufferDesc> bufferDescList) {
105             uint32_t attributeCount = 0;
106             std::stringstream inputStringStream;
107 
108             for (auto buffer : bufferDescList) {
109                 for (auto attr : buffer.attributes) {
110                     // [[location({shaderLocation})]] var_{id} : {typeString},
111                     inputStringStream << "[[location(" << attr.shaderLocation << ")]] var_"
112                                       << attributeCount << " : vec4<f32>,";
113                     attributeCount++;
114                 }
115             }
116 
117             std::stringstream shaderStringStream;
118 
119             shaderStringStream << R"(
120             [[stage(vertex)]]
121             fn main()" << inputStringStream.str()
122                                << R"() -> [[builtin(position)]] vec4<f32> {
123                 return vec4<f32>(0.0, 1.0, 0.0, 1.0);
124             })";
125 
126             return utils::CreateShaderModule(device, shaderStringStream.str().c_str());
127         }
128 
129         // Create a render pipeline with given buffer layout description, using a vertex shader
130         // module automatically generated from the buffer description.
CreateRenderPipelineWithBufferDesc(std::vector<PipelineVertexBufferDesc> bufferDescList)131         wgpu::RenderPipeline CreateRenderPipelineWithBufferDesc(
132             std::vector<PipelineVertexBufferDesc> bufferDescList) {
133             utils::ComboRenderPipelineDescriptor descriptor;
134 
135             descriptor.vertex.module = CreateVertexShaderModuleWithBuffer(bufferDescList);
136             descriptor.cFragment.module = fsModule;
137             descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
138 
139             descriptor.vertex.bufferCount = bufferDescList.size();
140 
141             size_t attributeCount = 0;
142 
143             for (size_t bufferCount = 0; bufferCount < bufferDescList.size(); bufferCount++) {
144                 auto bufferDesc = bufferDescList[bufferCount];
145                 descriptor.cBuffers[bufferCount].arrayStride = bufferDesc.arrayStride;
146                 descriptor.cBuffers[bufferCount].stepMode = bufferDesc.stepMode;
147                 if (bufferDesc.attributes.size() > 0) {
148                     descriptor.cBuffers[bufferCount].attributeCount = bufferDesc.attributes.size();
149                     descriptor.cBuffers[bufferCount].attributes =
150                         &descriptor.cAttributes[attributeCount];
151                     for (auto attribute : bufferDesc.attributes) {
152                         descriptor.cAttributes[attributeCount].shaderLocation =
153                             attribute.shaderLocation;
154                         descriptor.cAttributes[attributeCount].format = attribute.format;
155                         descriptor.cAttributes[attributeCount].offset = attribute.offset;
156                         attributeCount++;
157                     }
158                 } else {
159                     descriptor.cBuffers[bufferCount].attributeCount = 0;
160                     descriptor.cBuffers[bufferCount].attributes = nullptr;
161                 }
162             }
163 
164             descriptor.cTargets[0].format = renderPass.colorFormat;
165 
166             return device.CreateRenderPipeline(&descriptor);
167         }
168 
169         // Create a render pipeline using only one vertex-step-mode Float32x4 buffer
CreateBasicRenderPipeline(uint32_t bufferStride=kFloat32x4Stride)170         wgpu::RenderPipeline CreateBasicRenderPipeline(uint32_t bufferStride = kFloat32x4Stride) {
171             DAWN_ASSERT(bufferStride >= kFloat32x4Stride);
172 
173             std::vector<PipelineVertexBufferDesc> bufferDescList = {
174                 {bufferStride, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
175             };
176 
177             return CreateRenderPipelineWithBufferDesc(bufferDescList);
178         }
179 
180         // Create a render pipeline using one vertex-step-mode Float32x4 buffer and one
181         // instance-step-mode Float32x2 buffer
CreateBasicRenderPipelineWithInstance(uint32_t bufferStride1=kFloat32x4Stride,uint32_t bufferStride2=kFloat32x2Stride)182         wgpu::RenderPipeline CreateBasicRenderPipelineWithInstance(
183             uint32_t bufferStride1 = kFloat32x4Stride,
184             uint32_t bufferStride2 = kFloat32x2Stride) {
185             DAWN_ASSERT(bufferStride1 >= kFloat32x4Stride);
186             DAWN_ASSERT(bufferStride2 >= kFloat32x2Stride);
187 
188             std::vector<PipelineVertexBufferDesc> bufferDescList = {
189                 {bufferStride1, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
190                 {bufferStride2,
191                  wgpu::VertexStepMode::Instance,
192                  {{3, wgpu::VertexFormat::Float32x2}}},
193             };
194 
195             return CreateRenderPipelineWithBufferDesc(bufferDescList);
196         }
197 
198         // Create a render pipeline using one vertex-step-mode and one instance-step-mode buffer,
199         // both with a zero array stride. The minimal size of vertex step mode buffer should be 28,
200         // and the minimal size of instance step mode buffer should be 20.
CreateBasicRenderPipelineWithZeroArrayStride()201         wgpu::RenderPipeline CreateBasicRenderPipelineWithZeroArrayStride() {
202             std::vector<PipelineVertexBufferDesc> bufferDescList = {
203                 {0,
204                  wgpu::VertexStepMode::Vertex,
205                  {{0, wgpu::VertexFormat::Float32x4, 0}, {1, wgpu::VertexFormat::Float32x2, 20}}},
206                 {0,
207                  wgpu::VertexStepMode::Instance,
208                  // Two attributes are overlapped within this instance step mode vertex buffer
209                  {{3, wgpu::VertexFormat::Float32x4, 4}, {7, wgpu::VertexFormat::Float32x3, 0}}},
210             };
211 
212             return CreateRenderPipelineWithBufferDesc(bufferDescList);
213         }
214 
TestRenderPassDraw(const wgpu::RenderPipeline & pipeline,VertexBufferList vertexBufferList,uint32_t vertexCount,uint32_t instanceCount,uint32_t firstVertex,uint32_t firstInstance,bool isSuccess)215         void TestRenderPassDraw(const wgpu::RenderPipeline& pipeline,
216                                 VertexBufferList vertexBufferList,
217                                 uint32_t vertexCount,
218                                 uint32_t instanceCount,
219                                 uint32_t firstVertex,
220                                 uint32_t firstInstance,
221                                 bool isSuccess) {
222             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
223             wgpu::RenderPassEncoder renderPassEncoder =
224                 encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
225             renderPassEncoder.SetPipeline(pipeline);
226 
227             for (auto vertexBufferParam : vertexBufferList) {
228                 renderPassEncoder.SetVertexBuffer(vertexBufferParam.slot, vertexBufferParam.buffer,
229                                                   vertexBufferParam.offset, vertexBufferParam.size);
230             }
231             renderPassEncoder.Draw(vertexCount, instanceCount, firstVertex, firstInstance);
232             renderPassEncoder.EndPass();
233 
234             if (isSuccess) {
235                 encoder.Finish();
236             } else {
237                 ASSERT_DEVICE_ERROR(encoder.Finish());
238             }
239         }
240 
TestRenderPassDrawIndexed(const wgpu::RenderPipeline & pipeline,IndexBufferDesc indexBuffer,VertexBufferList vertexBufferList,uint32_t indexCount,uint32_t instanceCount,uint32_t firstIndex,int32_t baseVertex,uint32_t firstInstance,bool isSuccess)241         void TestRenderPassDrawIndexed(const wgpu::RenderPipeline& pipeline,
242                                        IndexBufferDesc indexBuffer,
243                                        VertexBufferList vertexBufferList,
244                                        uint32_t indexCount,
245                                        uint32_t instanceCount,
246                                        uint32_t firstIndex,
247                                        int32_t baseVertex,
248                                        uint32_t firstInstance,
249                                        bool isSuccess) {
250             wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
251             wgpu::RenderPassEncoder renderPassEncoder =
252                 encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
253             renderPassEncoder.SetPipeline(pipeline);
254 
255             renderPassEncoder.SetIndexBuffer(indexBuffer.buffer, indexBuffer.indexFormat,
256                                              indexBuffer.offset, indexBuffer.size);
257 
258             for (auto vertexBufferParam : vertexBufferList) {
259                 renderPassEncoder.SetVertexBuffer(vertexBufferParam.slot, vertexBufferParam.buffer,
260                                                   vertexBufferParam.offset, vertexBufferParam.size);
261             }
262             renderPassEncoder.DrawIndexed(indexCount, instanceCount, firstIndex, baseVertex,
263                                           firstInstance);
264             renderPassEncoder.EndPass();
265 
266             if (isSuccess) {
267                 encoder.Finish();
268             } else {
269                 ASSERT_DEVICE_ERROR(encoder.Finish());
270             }
271         }
272 
273         // Parameters list for index buffer. Should cover all IndexFormat, and the zero/non-zero
274         // offset and size case in SetIndexBuffer
275         const std::vector<IndexBufferParams> kIndexParamsList = {
276             {wgpu::IndexFormat::Uint32, 12 * sizeof(uint32_t), 0, wgpu::kWholeSize, 12},
277             {wgpu::IndexFormat::Uint32, 13 * sizeof(uint32_t), sizeof(uint32_t), wgpu::kWholeSize,
278              12},
279             {wgpu::IndexFormat::Uint32, 13 * sizeof(uint32_t), 0, 12 * sizeof(uint32_t), 12},
280             {wgpu::IndexFormat::Uint32, 14 * sizeof(uint32_t), sizeof(uint32_t),
281              12 * sizeof(uint32_t), 12},
282 
283             {wgpu::IndexFormat::Uint16, 12 * sizeof(uint16_t), 0, wgpu::kWholeSize, 12},
284             {wgpu::IndexFormat::Uint16, 13 * sizeof(uint16_t), sizeof(uint16_t), wgpu::kWholeSize,
285              12},
286             {wgpu::IndexFormat::Uint16, 13 * sizeof(uint16_t), 0, 12 * sizeof(uint16_t), 12},
287             {wgpu::IndexFormat::Uint16, 14 * sizeof(uint16_t), sizeof(uint16_t),
288              12 * sizeof(uint16_t), 12},
289         };
290         // Parameters list for vertex-step-mode buffer. These parameters should cover different
291         // stride, buffer size, SetVertexBuffer size and offset.
292         const std::vector<VertexBufferParams> kVertexParamsList = {
293             // For stride = kFloat32x4Stride
294             {kFloat32x4Stride, 3 * kFloat32x4Stride, 0, wgpu::kWholeSize, 3},
295             // Non-zero offset
296             {kFloat32x4Stride, 4 * kFloat32x4Stride, kFloat32x4Stride, wgpu::kWholeSize, 3},
297             // Non-default size
298             {kFloat32x4Stride, 4 * kFloat32x4Stride, 0, 3 * kFloat32x4Stride, 3},
299             // Non-zero offset and size
300             {kFloat32x4Stride, 5 * kFloat32x4Stride, kFloat32x4Stride, 3 * kFloat32x4Stride, 3},
301             // For stride = 2 * kFloat32x4Stride
302             {(2 * kFloat32x4Stride), 3 * (2 * kFloat32x4Stride), 0, wgpu::kWholeSize, 3},
303             // Non-zero offset
304             {(2 * kFloat32x4Stride), 4 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
305              wgpu::kWholeSize, 3},
306             // Non-default size
307             {(2 * kFloat32x4Stride), 4 * (2 * kFloat32x4Stride), 0, 3 * (2 * kFloat32x4Stride), 3},
308             // Non-zero offset and size
309             {(2 * kFloat32x4Stride), 5 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
310              3 * (2 * kFloat32x4Stride), 3},
311         };
312         // Parameters list for instance-step-mode buffer.
313         const std::vector<VertexBufferParams> kInstanceParamsList = {
314             // For stride = kFloat32x2Stride
315             {kFloat32x2Stride, 5 * kFloat32x2Stride, 0, wgpu::kWholeSize, 5},
316             // Non-zero offset
317             {kFloat32x2Stride, 6 * kFloat32x2Stride, kFloat32x2Stride, wgpu::kWholeSize, 5},
318             // Non-default size
319             {kFloat32x2Stride, 6 * kFloat32x2Stride, 0, 5 * kFloat32x2Stride, 5},
320             // Non-zero offset and size
321             {kFloat32x2Stride, 7 * kFloat32x2Stride, kFloat32x2Stride, 5 * kFloat32x2Stride, 5},
322             // For stride = 3 * kFloat32x2Stride
323             {(3 * kFloat32x2Stride), 5 * (3 * kFloat32x2Stride), 0, wgpu::kWholeSize, 5},
324             // Non-zero offset
325             {(3 * kFloat32x2Stride), 6 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
326              wgpu::kWholeSize, 5},
327             // Non-default size
328             {(3 * kFloat32x2Stride), 6 * (3 * kFloat32x2Stride), 0, 5 * (3 * kFloat32x2Stride), 5},
329             // Non-zero offset and size
330             {(3 * kFloat32x2Stride), 7 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
331              5 * (3 * kFloat32x2Stride), 5},
332         };
333 
334       private:
335         wgpu::ShaderModule fsModule;
336         utils::BasicRenderPass renderPass;
337     };
338 
339     // Control case for Draw
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawBasic)340     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawBasic) {
341         wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
342 
343         wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
344 
345         {
346             // Implicit size
347             VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
348             TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, true);
349         }
350 
351         {
352             // Explicit zero size
353             VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, 0}};
354             TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, false);
355         }
356     }
357 
358     // Verify vertex buffer OOB for non-instanced Draw are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawVertexBufferOutOfBoundWithoutInstance)359     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWithoutInstance) {
360         for (VertexBufferParams params : kVertexParamsList) {
361             // Create a render pipeline without instance step mode buffer
362             wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline(params.bufferStride);
363 
364             // Build vertex buffer for 3 vertices
365             wgpu::Buffer vertexBuffer = CreateBuffer(params.bufferSize);
366             VertexBufferList vertexBufferList = {
367                 {0, vertexBuffer, params.bufferOffsetForEncoder, params.bufferSizeForEncoder}};
368 
369             uint32_t n = params.maxValidAccessNumber;
370             // It is ok to draw n vertices with vertex buffer
371             TestRenderPassDraw(pipeline, vertexBufferList, n, 1, 0, 0, true);
372             // It is ok to draw n-1 vertices with offset 1
373             TestRenderPassDraw(pipeline, vertexBufferList, n - 1, 1, 1, 0, true);
374             // Drawing more vertices will cause OOB, even if not enough for another primitive
375             TestRenderPassDraw(pipeline, vertexBufferList, n + 1, 1, 0, 0, false);
376             // Drawing n vertices will non-zero offset will cause OOB
377             TestRenderPassDraw(pipeline, vertexBufferList, n, 1, 1, 0, false);
378             // It is ok to draw any number of instances, as we have no instance-mode buffer
379             TestRenderPassDraw(pipeline, vertexBufferList, n, 5, 0, 0, true);
380             TestRenderPassDraw(pipeline, vertexBufferList, n, 5, 0, 5, true);
381         }
382     }
383 
384     // Verify vertex buffer OOB for instanced Draw are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawVertexBufferOutOfBoundWithInstance)385     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWithInstance) {
386         for (VertexBufferParams vertexParams : kVertexParamsList) {
387             for (VertexBufferParams instanceParams : kInstanceParamsList) {
388                 // Create pipeline with given buffer stride
389                 wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
390                     vertexParams.bufferStride, instanceParams.bufferStride);
391 
392                 // Build vertex buffer
393                 wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
394                 wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
395 
396                 VertexBufferList vertexBufferList = {
397                     {0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
398                      vertexParams.bufferSizeForEncoder},
399                     {1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
400                      instanceParams.bufferSizeForEncoder},
401                 };
402 
403                 uint32_t vert = vertexParams.maxValidAccessNumber;
404                 uint32_t inst = instanceParams.maxValidAccessNumber;
405                 // It is ok to draw vert vertices
406                 TestRenderPassDraw(pipeline, vertexBufferList, vert, 1, 0, 0, true);
407                 TestRenderPassDraw(pipeline, vertexBufferList, vert - 1, 1, 1, 0, true);
408                 // It is ok to draw vert vertices and inst instences
409                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 0, true);
410                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst - 1, 0, 1, true);
411                 // more vertices causing OOB
412                 TestRenderPassDraw(pipeline, vertexBufferList, vert + 1, 1, 0, 0, false);
413                 TestRenderPassDraw(pipeline, vertexBufferList, vert, 1, 1, 0, false);
414                 TestRenderPassDraw(pipeline, vertexBufferList, vert + 1, inst, 0, 0, false);
415                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 1, 0, false);
416                 // more instances causing OOB
417                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst + 1, 0, 0, false);
418                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 1, false);
419                 // Both OOB
420                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst + 1, 0, 0, false);
421                 TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 1, 1, false);
422             }
423         }
424     }
425 
426     // Control case for DrawIndexed
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawIndexedBasic)427     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedBasic) {
428         wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
429 
430         // Build index buffer for 12 indexes
431         wgpu::Buffer indexBuffer = CreateBuffer(12 * sizeof(uint32_t), wgpu::BufferUsage::Index);
432 
433         // Build vertex buffer for 3 vertices
434         wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
435         VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
436 
437         IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
438 
439         TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 1, 0, 0, 0,
440                                   true);
441     }
442 
443     // Verify index buffer OOB for DrawIndexed are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawIndexedIndexBufferOOB)444     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedIndexBufferOOB) {
445         wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance();
446 
447         for (IndexBufferParams params : kIndexParamsList) {
448             // Build index buffer use given params
449             wgpu::Buffer indexBuffer =
450                 CreateBuffer(params.indexBufferSize, wgpu::BufferUsage::Index);
451             // Build vertex buffer for 3 vertices
452             wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
453             // Build vertex buffer for 5 instances
454             wgpu::Buffer instanceBuffer = CreateBuffer(5 * kFloat32x2Stride);
455 
456             VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize},
457                                                  {1, instanceBuffer, 0, wgpu::kWholeSize}};
458 
459             IndexBufferDesc indexBufferDesc = {indexBuffer, params.indexFormat,
460                                                params.indexBufferOffsetForEncoder,
461                                                params.indexBufferSizeForEncoder};
462 
463             uint32_t n = params.maxValidIndexNumber;
464 
465             // Control case
466             TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 0, 0, 0,
467                                       true);
468             TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n - 1, 5, 1, 0,
469                                       0, true);
470             // Index buffer OOB, indexCount too large
471             TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n + 1, 5, 0, 0,
472                                       0, false);
473             // Index buffer OOB, indexCount + firstIndex too large
474             TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 1, 0, 0,
475                                       false);
476 
477             if (!HasToggleEnabled("disable_base_vertex")) {
478                 // baseVertex is not considered in CPU validation and has no effect on validation
479                 // Although baseVertex is too large, it will still pass
480                 TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 0, 100,
481                                           0, true);
482                 // Index buffer OOB, indexCount too large
483                 TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n + 1, 5, 0,
484                                           100, 0, false);
485             }
486         }
487     }
488 
489     // Verify instance mode vertex buffer OOB for DrawIndexed are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,DrawIndexedVertexBufferOOB)490     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedVertexBufferOOB) {
491         for (VertexBufferParams vertexParams : kVertexParamsList) {
492             for (VertexBufferParams instanceParams : kInstanceParamsList) {
493                 // Create pipeline with given buffer stride
494                 wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
495                     vertexParams.bufferStride, instanceParams.bufferStride);
496 
497                 auto indexFormat = wgpu::IndexFormat::Uint32;
498                 auto indexStride = sizeof(uint32_t);
499 
500                 // Build index buffer for 12 indexes
501                 wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
502                 // Build vertex buffer for vertices
503                 wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
504                 // Build vertex buffer for instances
505                 wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
506 
507                 VertexBufferList vertexBufferList = {
508                     {0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
509                      vertexParams.bufferSizeForEncoder},
510                     {1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
511                      instanceParams.bufferSizeForEncoder}};
512 
513                 IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
514 
515                 uint32_t inst = instanceParams.maxValidAccessNumber;
516                 // Control case
517                 TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst, 0,
518                                           0, 0, true);
519                 // Vertex buffer (stepMode = instance) OOB, instanceCount too large
520                 TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst + 1,
521                                           0, 0, 0, false);
522 
523                 if (!HasToggleEnabled("disable_base_instance")) {
524                     // firstInstance is considered in CPU validation
525                     // Vertex buffer (stepMode = instance) in bound
526                     TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12,
527                                               inst - 1, 0, 0, 1, true);
528                     // Vertex buffer (stepMode = instance) OOB, instanceCount + firstInstance too
529                     // large
530                     TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst,
531                                               0, 0, 1, false);
532                 }
533             }
534         }
535     }
536 
537     // Verify instance mode vertex buffer OOB for DrawIndexed are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,ZeroArrayStrideVertexBufferOOB)538     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBufferOOB) {
539         // In this test, we use VertexBufferParams.maxValidAccessNumber > 0 to indicate that such
540         // buffer parameter meet the requirement of pipeline, and maxValidAccessNumber == 0 to
541         // indicate that such buffer parameter will cause OOB.
542         const std::vector<VertexBufferParams> kVertexParamsListForZeroStride = {
543             // Control case
544             {0, 28, 0, wgpu::kWholeSize, 1},
545             // Non-zero offset
546             {0, 28, 4, wgpu::kWholeSize, 0},
547             {0, 28, 28, wgpu::kWholeSize, 0},
548             // Non-default size
549             {0, 28, 0, 28, 1},
550             {0, 28, 0, 27, 0},
551             // Non-zero offset and size
552             {0, 32, 4, 28, 1},
553             {0, 31, 4, 27, 0},
554             {0, 31, 4, wgpu::kWholeSize, 0},
555         };
556 
557         const std::vector<VertexBufferParams> kInstanceParamsListForZeroStride = {
558             // Control case
559             {0, 20, 0, wgpu::kWholeSize, 1},
560             // Non-zero offset
561             {0, 24, 4, wgpu::kWholeSize, 1},
562             {0, 23, 4, wgpu::kWholeSize, 0},
563             {0, 20, 4, wgpu::kWholeSize, 0},
564             {0, 20, 20, wgpu::kWholeSize, 0},
565             // Non-default size
566             {0, 21, 0, 20, 1},
567             {0, 20, 0, 19, 0},
568             // Non-zero offset and size
569             {0, 30, 4, 20, 1},
570             {0, 30, 4, 19, 0},
571         };
572 
573         // Build a pipeline that require a vertex step mode vertex buffer no smaller than 28 bytes
574         // and an instance step mode buffer no smaller than 20 bytes
575         wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithZeroArrayStride();
576 
577         for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
578             for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
579                 auto indexFormat = wgpu::IndexFormat::Uint32;
580                 auto indexStride = sizeof(uint32_t);
581 
582                 // Build index buffer for 12 indexes
583                 wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
584                 // Build vertex buffer for vertices
585                 wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
586                 // Build vertex buffer for instances
587                 wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
588 
589                 VertexBufferList vertexBufferList = {
590                     {0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
591                      vertexParams.bufferSizeForEncoder},
592                     {1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
593                      instanceParams.bufferSizeForEncoder}};
594 
595                 IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
596 
597                 const bool isSuccess = (vertexParams.maxValidAccessNumber > 0) &&
598                                        (instanceParams.maxValidAccessNumber > 0);
599                 // vertexCount and instanceCount doesn't matter, as array stride is zero and all
600                 // vertex/instance access the same space of buffer
601                 TestRenderPassDraw(pipeline, vertexBufferList, 100, 100, 0, 0, isSuccess);
602                 // indexCount doesn't matter as long as no index buffer OOB happened
603                 TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 100, 0,
604                                           0, 0, isSuccess);
605             }
606         }
607     }
608 
609     // Verify that if setVertexBuffer and/or setIndexBuffer for multiple times, only the last one is
610     // taken into account
TEST_F(DrawVertexAndIndexBufferOOBValidationTests,SetBufferMultipleTime)611     TEST_F(DrawVertexAndIndexBufferOOBValidationTests, SetBufferMultipleTime) {
612         wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
613         uint32_t indexStride = sizeof(uint32_t);
614 
615         // Build index buffer for 11 indexes
616         wgpu::Buffer indexBuffer11 = CreateBuffer(11 * indexStride, wgpu::BufferUsage::Index);
617         // Build index buffer for 12 indexes
618         wgpu::Buffer indexBuffer12 = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
619         // Build vertex buffer for 2 vertices
620         wgpu::Buffer vertexBuffer2 = CreateBuffer(2 * kFloat32x4Stride);
621         // Build vertex buffer for 3 vertices
622         wgpu::Buffer vertexBuffer3 = CreateBuffer(3 * kFloat32x4Stride);
623         // Build vertex buffer for 4 instances
624         wgpu::Buffer instanceBuffer4 = CreateBuffer(4 * kFloat32x2Stride);
625         // Build vertex buffer for 5 instances
626         wgpu::Buffer instanceBuffer5 = CreateBuffer(5 * kFloat32x2Stride);
627 
628         // Test for setting vertex buffer for multiple times
629         {
630             wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance();
631 
632             // Set to vertexBuffer3 and instanceBuffer5 at last
633             VertexBufferList vertexBufferList = {{0, vertexBuffer2, 0, wgpu::kWholeSize},
634                                                  {1, instanceBuffer4, 0, wgpu::kWholeSize},
635                                                  {1, instanceBuffer5, 0, wgpu::kWholeSize},
636                                                  {0, vertexBuffer3, 0, wgpu::kWholeSize}};
637 
638             // For Draw, the max vertexCount is 3 and the max instanceCount is 5
639             TestRenderPassDraw(pipeline, vertexBufferList, 3, 5, 0, 0, true);
640             TestRenderPassDraw(pipeline, vertexBufferList, 4, 5, 0, 0, false);
641             TestRenderPassDraw(pipeline, vertexBufferList, 3, 6, 0, 0, false);
642             // For DrawIndex, the max instanceCount is 5
643             TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12,
644                                       5, 0, 0, 0, true);
645             TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12,
646                                       6, 0, 0, 0, false);
647 
648             // Set to vertexBuffer2 and instanceBuffer4 at last
649             vertexBufferList = VertexBufferList{{0, vertexBuffer3, 0, wgpu::kWholeSize},
650                                                 {1, instanceBuffer5, 0, wgpu::kWholeSize},
651                                                 {0, vertexBuffer2, 0, wgpu::kWholeSize},
652                                                 {1, instanceBuffer4, 0, wgpu::kWholeSize}};
653 
654             // For Draw, the max vertexCount is 2 and the max instanceCount is 4
655             TestRenderPassDraw(pipeline, vertexBufferList, 2, 4, 0, 0, true);
656             TestRenderPassDraw(pipeline, vertexBufferList, 3, 4, 0, 0, false);
657             TestRenderPassDraw(pipeline, vertexBufferList, 2, 5, 0, 0, false);
658             // For DrawIndex, the max instanceCount is 4
659             TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12,
660                                       4, 0, 0, 0, true);
661             TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12,
662                                       5, 0, 0, 0, false);
663         }
664 
665         // Test for setIndexBuffer multiple times
666         {
667             wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
668 
669             {
670                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
671                 wgpu::RenderPassEncoder renderPassEncoder =
672                     encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
673                 renderPassEncoder.SetPipeline(pipeline);
674 
675                 // Index buffer is set to indexBuffer12 at last
676                 renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
677                 renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
678 
679                 renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
680                 // It should be ok to draw 12 index
681                 renderPassEncoder.DrawIndexed(12, 1, 0, 0, 0);
682                 renderPassEncoder.EndPass();
683 
684                 // Expect success
685                 encoder.Finish();
686             }
687 
688             {
689                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
690                 wgpu::RenderPassEncoder renderPassEncoder =
691                     encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
692                 renderPassEncoder.SetPipeline(pipeline);
693 
694                 // Index buffer is set to indexBuffer12 at last
695                 renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
696                 renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
697 
698                 renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
699                 // It should be index buffer OOB to draw 13 index
700                 renderPassEncoder.DrawIndexed(13, 1, 0, 0, 0);
701                 renderPassEncoder.EndPass();
702 
703                 // Expect failure
704                 ASSERT_DEVICE_ERROR(encoder.Finish());
705             }
706 
707             {
708                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
709                 wgpu::RenderPassEncoder renderPassEncoder =
710                     encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
711                 renderPassEncoder.SetPipeline(pipeline);
712 
713                 // Index buffer is set to indexBuffer11 at last
714                 renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
715                 renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
716 
717                 renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
718                 // It should be ok to draw 11 index
719                 renderPassEncoder.DrawIndexed(11, 1, 0, 0, 0);
720                 renderPassEncoder.EndPass();
721 
722                 // Expect success
723                 encoder.Finish();
724             }
725 
726             {
727                 wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
728                 wgpu::RenderPassEncoder renderPassEncoder =
729                     encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
730                 renderPassEncoder.SetPipeline(pipeline);
731 
732                 // Index buffer is set to indexBuffer11 at last
733                 renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
734                 renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
735 
736                 renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
737                 // It should be index buffer OOB to draw 12 index
738                 renderPassEncoder.DrawIndexed(12, 1, 0, 0, 0);
739                 renderPassEncoder.EndPass();
740 
741                 // Expect failure
742                 ASSERT_DEVICE_ERROR(encoder.Finish());
743             }
744         }
745     }
746 
747 }  // anonymous namespace
748