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