blob: a6c312415ff0dc8843af318ccf589c1c6dee9008 [file] [log] [blame] [edit]
// Copyright 2021 The Dawn Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include "dawn/common/Constants.h"
#include "dawn/tests/unittests/validation/ValidationTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
#include "dawn/utils/WGPUHelpers.h"
namespace dawn {
namespace {
constexpr uint32_t kRTSize = 4;
constexpr uint32_t kFloat32x2Stride = 2 * sizeof(float);
constexpr uint32_t kFloat32x4Stride = 4 * sizeof(float);
class DrawVertexAndIndexBufferOOBValidationTests : public ValidationTest {
public:
// Parameters for testing index buffer
struct IndexBufferParams {
wgpu::IndexFormat indexFormat;
uint64_t indexBufferSize; // Size for creating index buffer
uint64_t indexBufferOffsetForEncoder; // Offset for SetIndexBuffer in encoder
uint64_t indexBufferSizeForEncoder; // Size for SetIndexBuffer in encoder
uint32_t maxValidIndexNumber; // max number of {indexCount + firstIndex} for this set
// of parameters
};
// Parameters for testing vertex-step-mode and instance-step-mode vertex buffer
struct VertexBufferParams {
uint32_t bufferStride;
uint64_t bufferSize; // Size for creating vertex buffer
uint64_t bufferOffsetForEncoder; // Offset for SetVertexBuffer in encoder
uint64_t bufferSizeForEncoder; // Size for SetVertexBuffer in encoder
uint32_t maxValidAccessNumber; // max number of valid access time for this set of
// parameters, i.e. {vertexCount + firstVertex} for
// vertex-step-mode, and {instanceCount + firstInstance}
// for instance-step-mode
};
// Parameters for setIndexBuffer
struct IndexBufferDesc {
const wgpu::Buffer buffer;
wgpu::IndexFormat indexFormat;
uint64_t offset = 0;
uint64_t size = wgpu::kWholeSize;
};
// Parameters for setVertexBuffer
struct VertexBufferSpec {
uint32_t slot;
const wgpu::Buffer buffer;
uint64_t offset = 0;
uint64_t size = wgpu::kWholeSize;
};
using VertexBufferList = std::vector<VertexBufferSpec>;
// Buffer layout parameters for creating pipeline
struct PipelineVertexBufferAttributeDesc {
uint32_t shaderLocation;
wgpu::VertexFormat format;
uint64_t offset = 0;
};
struct PipelineVertexBufferDesc {
uint64_t arrayStride;
wgpu::VertexStepMode stepMode;
std::vector<PipelineVertexBufferAttributeDesc> attributes = {};
};
void SetUp() override {
ValidationTest::SetUp();
renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
fsModule = utils::CreateShaderModule(device, R"(
@fragment fn main() -> @location(0) vec4f {
return vec4f(0.0, 1.0, 0.0, 1.0);
})");
}
const wgpu::RenderPassDescriptor* GetBasicRenderPassDescriptor() const {
return &renderPass.renderPassInfo;
}
wgpu::Buffer CreateBuffer(uint64_t size, wgpu::BufferUsage usage = wgpu::BufferUsage::Vertex) {
wgpu::BufferDescriptor descriptor;
descriptor.size = size;
descriptor.usage = usage;
return device.CreateBuffer(&descriptor);
}
wgpu::ShaderModule CreateVertexShaderModuleWithBuffer(
std::vector<PipelineVertexBufferDesc> bufferDescList) {
uint32_t attributeCount = 0;
std::stringstream inputStringStream;
for (auto buffer : bufferDescList) {
for (auto attr : buffer.attributes) {
// @location({shaderLocation}) var_{id} : {typeString},
inputStringStream << "@location(" << attr.shaderLocation << ") var_"
<< attributeCount << " : vec4f,";
attributeCount++;
}
}
std::stringstream shaderStringStream;
shaderStringStream << R"(
@vertex
fn main()" << inputStringStream.str()
<< R"() -> @builtin(position) vec4f {
return vec4f(0.0, 1.0, 0.0, 1.0);
})";
return utils::CreateShaderModule(device, shaderStringStream.str().c_str());
}
// Create a render pipeline with given buffer layout description, using a vertex shader
// module automatically generated from the buffer description.
wgpu::RenderPipeline CreateRenderPipelineWithBufferDesc(
std::vector<PipelineVertexBufferDesc> bufferDescList) {
utils::ComboRenderPipelineDescriptor descriptor;
descriptor.vertex.module = CreateVertexShaderModuleWithBuffer(bufferDescList);
descriptor.cFragment.module = fsModule;
descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleList;
descriptor.vertex.bufferCount = bufferDescList.size();
size_t attributeCount = 0;
for (size_t bufferCount = 0; bufferCount < bufferDescList.size(); bufferCount++) {
auto bufferDesc = bufferDescList[bufferCount];
descriptor.cBuffers[bufferCount].arrayStride = bufferDesc.arrayStride;
descriptor.cBuffers[bufferCount].stepMode = bufferDesc.stepMode;
if (bufferDesc.attributes.size() > 0) {
descriptor.cBuffers[bufferCount].attributeCount = bufferDesc.attributes.size();
descriptor.cBuffers[bufferCount].attributes =
&descriptor.cAttributes[attributeCount];
for (auto attribute : bufferDesc.attributes) {
descriptor.cAttributes[attributeCount].shaderLocation =
attribute.shaderLocation;
descriptor.cAttributes[attributeCount].format = attribute.format;
descriptor.cAttributes[attributeCount].offset = attribute.offset;
attributeCount++;
}
} else {
descriptor.cBuffers[bufferCount].attributeCount = 0;
descriptor.cBuffers[bufferCount].attributes = nullptr;
}
}
descriptor.cTargets[0].format = renderPass.colorFormat;
return device.CreateRenderPipeline(&descriptor);
}
// Create a render pipeline using only one vertex-step-mode Float32x4 buffer
wgpu::RenderPipeline CreateBasicRenderPipeline(uint32_t bufferStride = kFloat32x4Stride) {
DAWN_ASSERT(bufferStride >= kFloat32x4Stride);
std::vector<PipelineVertexBufferDesc> bufferDescList = {
{bufferStride, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
};
return CreateRenderPipelineWithBufferDesc(bufferDescList);
}
// Create a render pipeline using one vertex-step-mode Float32x4 buffer and one
// instance-step-mode Float32x2 buffer
wgpu::RenderPipeline CreateBasicRenderPipelineWithInstance(
uint32_t bufferStride1 = kFloat32x4Stride,
uint32_t bufferStride2 = kFloat32x2Stride) {
DAWN_ASSERT(bufferStride1 >= kFloat32x4Stride);
DAWN_ASSERT(bufferStride2 >= kFloat32x2Stride);
std::vector<PipelineVertexBufferDesc> bufferDescList = {
{bufferStride1, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
{bufferStride2, wgpu::VertexStepMode::Instance, {{3, wgpu::VertexFormat::Float32x2}}},
};
return CreateRenderPipelineWithBufferDesc(bufferDescList);
}
// Create a render pipeline using one vertex-step-mode and one instance-step-mode buffer,
// both with a zero array stride. The minimal size of vertex step mode buffer should be 28,
// and the minimal size of instance step mode buffer should be 20.
wgpu::RenderPipeline CreateBasicRenderPipelineWithZeroArrayStride() {
std::vector<PipelineVertexBufferDesc> bufferDescList = {
{0,
wgpu::VertexStepMode::Vertex,
{{0, wgpu::VertexFormat::Float32x4, 0}, {1, wgpu::VertexFormat::Float32x2, 20}}},
{0,
wgpu::VertexStepMode::Instance,
// Two attributes are overlapped within this instance step mode vertex buffer
{{3, wgpu::VertexFormat::Float32x4, 4}, {7, wgpu::VertexFormat::Float32x3, 0}}},
};
return CreateRenderPipelineWithBufferDesc(bufferDescList);
}
void TestRenderPassDraw(const wgpu::RenderPipeline& pipeline,
VertexBufferList vertexBufferList,
uint32_t vertexCount,
uint32_t instanceCount,
uint32_t firstVertex,
uint32_t firstInstance,
bool isSuccess) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
for (auto vertexBufferParam : vertexBufferList) {
renderPassEncoder.SetVertexBuffer(vertexBufferParam.slot, vertexBufferParam.buffer,
vertexBufferParam.offset, vertexBufferParam.size);
}
renderPassEncoder.Draw(vertexCount, instanceCount, firstVertex, firstInstance);
renderPassEncoder.End();
if (isSuccess) {
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
void 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) {
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
renderPassEncoder.SetIndexBuffer(indexBuffer.buffer, indexBuffer.indexFormat,
indexBuffer.offset, indexBuffer.size);
for (auto vertexBufferParam : vertexBufferList) {
renderPassEncoder.SetVertexBuffer(vertexBufferParam.slot, vertexBufferParam.buffer,
vertexBufferParam.offset, vertexBufferParam.size);
}
renderPassEncoder.DrawIndexed(indexCount, instanceCount, firstIndex, baseVertex,
firstInstance);
renderPassEncoder.End();
if (isSuccess) {
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
// Parameters list for index buffer. Should cover all IndexFormat, and the zero/non-zero
// offset and size case in SetIndexBuffer
const std::vector<IndexBufferParams> kIndexParamsList = {
{wgpu::IndexFormat::Uint32, 12 * sizeof(uint32_t), 0, wgpu::kWholeSize, 12},
{wgpu::IndexFormat::Uint32, 13 * sizeof(uint32_t), sizeof(uint32_t), wgpu::kWholeSize, 12},
{wgpu::IndexFormat::Uint32, 13 * sizeof(uint32_t), 0, 12 * sizeof(uint32_t), 12},
{wgpu::IndexFormat::Uint32, 14 * sizeof(uint32_t), sizeof(uint32_t), 12 * sizeof(uint32_t),
12},
{wgpu::IndexFormat::Uint16, 12 * sizeof(uint16_t), 0, wgpu::kWholeSize, 12},
{wgpu::IndexFormat::Uint16, 13 * sizeof(uint16_t), sizeof(uint16_t), wgpu::kWholeSize, 12},
{wgpu::IndexFormat::Uint16, 13 * sizeof(uint16_t), 0, 12 * sizeof(uint16_t), 12},
{wgpu::IndexFormat::Uint16, 14 * sizeof(uint16_t), sizeof(uint16_t), 12 * sizeof(uint16_t),
12},
};
// Parameters list for vertex-step-mode buffer. These parameters should cover different
// stride, buffer size, SetVertexBuffer size and offset.
const std::vector<VertexBufferParams> kVertexParamsList = {
// For stride = kFloat32x4Stride
{kFloat32x4Stride, 3 * kFloat32x4Stride, 0, wgpu::kWholeSize, 3},
// Non-zero offset
{kFloat32x4Stride, 4 * kFloat32x4Stride, kFloat32x4Stride, wgpu::kWholeSize, 3},
// Non-default size
{kFloat32x4Stride, 4 * kFloat32x4Stride, 0, 3 * kFloat32x4Stride, 3},
// Non-zero offset and size
{kFloat32x4Stride, 5 * kFloat32x4Stride, kFloat32x4Stride, 3 * kFloat32x4Stride, 3},
// For stride = 2 * kFloat32x4Stride
{(2 * kFloat32x4Stride), 3 * (2 * kFloat32x4Stride), 0, wgpu::kWholeSize, 3},
// Non-zero offset
{(2 * kFloat32x4Stride), 4 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
wgpu::kWholeSize, 3},
// Non-default size
{(2 * kFloat32x4Stride), 4 * (2 * kFloat32x4Stride), 0, 3 * (2 * kFloat32x4Stride), 3},
// Non-zero offset and size
{(2 * kFloat32x4Stride), 5 * (2 * kFloat32x4Stride), (2 * kFloat32x4Stride),
3 * (2 * kFloat32x4Stride), 3},
// For (strideCount - 1) * arrayStride + lastStride <= bound buffer size < strideCount *
// arrayStride
{(kFloat32x4Stride + 4), 2 * (kFloat32x4Stride + 4) + kFloat32x4Stride, 0, wgpu::kWholeSize,
3},
{(kFloat32x4Stride + 4), 2 * (kFloat32x4Stride + 4) + kFloat32x4Stride - 1, 0,
wgpu::kWholeSize, 2},
};
// Parameters list for instance-step-mode buffer.
const std::vector<VertexBufferParams> kInstanceParamsList = {
// For stride = kFloat32x2Stride
{kFloat32x2Stride, 5 * kFloat32x2Stride, 0, wgpu::kWholeSize, 5},
// Non-zero offset
{kFloat32x2Stride, 6 * kFloat32x2Stride, kFloat32x2Stride, wgpu::kWholeSize, 5},
// Non-default size
{kFloat32x2Stride, 6 * kFloat32x2Stride, 0, 5 * kFloat32x2Stride, 5},
// Non-zero offset and size
{kFloat32x2Stride, 7 * kFloat32x2Stride, kFloat32x2Stride, 5 * kFloat32x2Stride, 5},
// For stride = 3 * kFloat32x2Stride
{(3 * kFloat32x2Stride), 5 * (3 * kFloat32x2Stride), 0, wgpu::kWholeSize, 5},
// Non-zero offset
{(3 * kFloat32x2Stride), 6 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
wgpu::kWholeSize, 5},
// Non-default size
{(3 * kFloat32x2Stride), 6 * (3 * kFloat32x2Stride), 0, 5 * (3 * kFloat32x2Stride), 5},
// Non-zero offset and size
{(3 * kFloat32x2Stride), 7 * (3 * kFloat32x2Stride), (3 * kFloat32x2Stride),
5 * (3 * kFloat32x2Stride), 5},
// For (strideCount - 1) * arrayStride + lastStride <= bound buffer size < strideCount *
// arrayStride
{(kFloat32x2Stride + 4), 2 * (kFloat32x2Stride + 4) + kFloat32x2Stride, 0, wgpu::kWholeSize,
3},
{(kFloat32x2Stride + 4), 2 * (kFloat32x2Stride + 4) + kFloat32x2Stride - 1, 0,
wgpu::kWholeSize, 2},
};
private:
wgpu::ShaderModule fsModule;
utils::BasicRenderPass renderPass;
};
// Control case for Draw
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawBasic) {
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
{
// Implicit size
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 3,
/* instanceCount */ 1, /* firstVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
}
{
// Explicit zero size
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, 0}};
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, false);
}
}
// Verify vertex buffer OOB for non-instanced Draw are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWithoutInstance) {
for (VertexBufferParams params : kVertexParamsList) {
// Create a render pipeline without instance step mode buffer
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline(params.bufferStride);
// Build vertex buffer for 3 vertices
wgpu::Buffer vertexBuffer = CreateBuffer(params.bufferSize);
VertexBufferList vertexBufferList = {
{0, vertexBuffer, params.bufferOffsetForEncoder, params.bufferSizeForEncoder}};
DAWN_ASSERT(params.maxValidAccessNumber > 0);
uint32_t n = params.maxValidAccessNumber;
// It is ok to draw n vertices with vertex buffer
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ n,
/* instanceCount */ 1, /* firstVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
// It is ok to draw n-1 vertices with offset 1
TestRenderPassDraw(pipeline, vertexBufferList, n - 1, 1, 1, 0, true);
// Drawing more vertices will cause OOB, even if not enough for another primitive
TestRenderPassDraw(pipeline, vertexBufferList, n + 1, 1, 0, 0, false);
// Drawing n vertices will non-zero offset will cause OOB
TestRenderPassDraw(pipeline, vertexBufferList, n, 1, 1, 0, false);
// It is ok to draw any number of instances, as we have no instance-mode buffer
TestRenderPassDraw(pipeline, vertexBufferList, n, 5, 0, 0, true);
TestRenderPassDraw(pipeline, vertexBufferList, n, 5, 0, 5, true);
}
}
// Verify vertex buffer OOB for instanced Draw are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawVertexBufferOutOfBoundWithInstance) {
for (VertexBufferParams vertexParams : kVertexParamsList) {
for (VertexBufferParams instanceParams : kInstanceParamsList) {
// Create pipeline with given buffer stride
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
vertexParams.bufferStride, instanceParams.bufferStride);
// Build vertex buffer
wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
VertexBufferList vertexBufferList = {
{0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
vertexParams.bufferSizeForEncoder},
{1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
instanceParams.bufferSizeForEncoder},
};
DAWN_ASSERT(vertexParams.maxValidAccessNumber > 0);
DAWN_ASSERT(instanceParams.maxValidAccessNumber > 0);
uint32_t vert = vertexParams.maxValidAccessNumber;
uint32_t inst = instanceParams.maxValidAccessNumber;
// It is ok to draw vert vertices
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ vert,
/* instanceCount */ 1, /* firstVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
TestRenderPassDraw(pipeline, vertexBufferList, vert - 1, 1, 1, 0, true);
// It is ok to draw vert vertices and inst instences
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 0, true);
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst - 1, 0, 1, true);
// more vertices causing OOB
TestRenderPassDraw(pipeline, vertexBufferList, vert + 1, 1, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, vert, 1, 1, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, vert + 1, inst, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 1, 0, false);
// more instances causing OOB
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst + 1, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 0, 1, false);
// Both OOB
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst + 1, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, vert, inst, 1, 1, false);
}
}
}
// Verify zero-attribute vertex buffer OOB for non-instanced Draw are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroAttribute) {
// Create a render pipeline with zero-attribute vertex buffer description
wgpu::RenderPipeline pipeline =
CreateRenderPipelineWithBufferDesc({{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {}}});
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
{
// Valid
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, true);
}
{
// OOB
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, 0}};
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, false);
}
}
// Verify vertex buffers don't need to be set to unused (hole) slots for Draw
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, UnusedSlots) {
// Set vertex buffer only to the second slot
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
VertexBufferList vertexBufferList = {{1, vertexBuffer, 0, wgpu::kWholeSize}};
{
// The first slot it unused so valid even if vertex buffer is not set to it.
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithBufferDesc(
{{0, wgpu::VertexStepMode::VertexBufferNotUsed, {}},
{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {}}});
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, true);
}
{
// The first slot it used so invalid if vertex buffer is not set to it.
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithBufferDesc(
{{0, wgpu::VertexStepMode::Vertex, {}},
{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {}}});
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, false);
}
}
// Control case for DrawIndexed
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedBasic) {
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
// Build index buffer for 12 indexes
wgpu::Buffer indexBuffer = CreateBuffer(12 * sizeof(uint32_t), wgpu::BufferUsage::Index);
// Build vertex buffer for 3 vertices
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize}};
IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
/* instanceCount */ 1, /* firstIndex */ 0, /* baseVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
}
// Verify index buffer OOB for DrawIndexed are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedIndexBufferOOB) {
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance();
for (IndexBufferParams params : kIndexParamsList) {
// Build index buffer use given params
wgpu::Buffer indexBuffer = CreateBuffer(params.indexBufferSize, wgpu::BufferUsage::Index);
// Build vertex buffer for 3 vertices
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
// Build vertex buffer for 5 instances
wgpu::Buffer instanceBuffer = CreateBuffer(5 * kFloat32x2Stride);
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize},
{1, instanceBuffer, 0, wgpu::kWholeSize}};
IndexBufferDesc indexBufferDesc = {indexBuffer, params.indexFormat,
params.indexBufferOffsetForEncoder,
params.indexBufferSizeForEncoder};
DAWN_ASSERT(params.maxValidIndexNumber > 0);
uint32_t n = params.maxValidIndexNumber;
// Control case
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ n,
/* instanceCount */ 5, /* firstIndex */ 0, /* baseVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n - 1, 5, 1, 0, 0,
true);
// Index buffer OOB, indexCount too large
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n + 1, 5, 0, 0, 0,
false);
// Index buffer OOB, indexCount + firstIndex too large
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 1, 0, 0,
false);
if (!HasToggleEnabled("disable_base_vertex")) {
// baseVertex is not considered in CPU validation and has no effect on validation
// Although baseVertex is too large, it will still pass
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 0, 100, 0,
true);
// Index buffer OOB, indexCount too large
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n + 1, 5, 0, 100,
0, false);
}
}
}
// Verify instance mode vertex buffer OOB for DrawIndexed are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedVertexBufferOOB) {
for (VertexBufferParams vertexParams : kVertexParamsList) {
for (VertexBufferParams instanceParams : kInstanceParamsList) {
// Create pipeline with given buffer stride
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance(
vertexParams.bufferStride, instanceParams.bufferStride);
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
constexpr uint64_t indexStride = sizeof(uint32_t);
// Build index buffer for 12 indexes
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
// Build vertex buffer for vertices
wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
// Build vertex buffer for instances
wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
VertexBufferList vertexBufferList = {
{0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
vertexParams.bufferSizeForEncoder},
{1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
instanceParams.bufferSizeForEncoder}};
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
DAWN_ASSERT(instanceParams.maxValidAccessNumber > 0);
uint32_t inst = instanceParams.maxValidAccessNumber;
// Control case
TestRenderPassDrawIndexed(
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
/* instanceCount */ inst, /* firstIndex */ 0, /* baseVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ true);
// Vertex buffer (stepMode = instance) OOB, instanceCount too large
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst + 1, 0,
0, 0, false);
if (!HasToggleEnabled("disable_base_instance")) {
// firstInstance is considered in CPU validation
// Vertex buffer (stepMode = instance) in bound
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst - 1,
0, 0, 1, true);
// Vertex buffer (stepMode = instance) OOB, instanceCount + firstInstance too
// large
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst, 0,
0, 1, false);
}
}
}
}
// Verify zero-attribute instance step mode vertex buffer OOB for instanced DrawIndex
// are caught in command encoder
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedZeroAttribute) {
// Create a render pipeline with a vertex step mode and a zero-attribute
// instance step mode vertex buffer description
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithBufferDesc(
{{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
{kFloat32x2Stride, wgpu::VertexStepMode::Instance, {}}});
// Build index buffer for 12 indexes
wgpu::Buffer indexBuffer = CreateBuffer(12 * sizeof(uint32_t), wgpu::BufferUsage::Index);
IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
// Build vertex buffer for 3 vertices
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
// Build vertex buffer for 3 instances
wgpu::Buffer instanceBuffer = CreateBuffer(3 * kFloat32x2Stride);
{
// Valid
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize},
{1, vertexBuffer, 0, wgpu::kWholeSize}};
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 3, 0, 0, 0,
true);
}
{
// OOB
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, wgpu::kWholeSize},
{1, vertexBuffer, 0, 0}};
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 3, 0, 0, 0,
false);
}
}
// Verify vertex buffers don't need to be set to unused (hole) slots for DrawIndexed
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, DrawIndexedUnusedSlots) {
// Build index buffer
wgpu::Buffer indexBuffer = CreateBuffer(12 * sizeof(uint32_t), wgpu::BufferUsage::Index);
IndexBufferDesc indexBufferDesc = {indexBuffer, wgpu::IndexFormat::Uint32};
// Set vertex buffers only to the second and third slots
wgpu::Buffer vertexBuffer = CreateBuffer(3 * kFloat32x4Stride);
wgpu::Buffer instanceBuffer = CreateBuffer(3 * kFloat32x2Stride);
VertexBufferList vertexBufferList = {{1, vertexBuffer, 0, wgpu::kWholeSize},
{2, instanceBuffer, 0, wgpu::kWholeSize}};
{
// The first slot it unused so valid even if vertex buffer is not set to it.
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithBufferDesc(
{{0, wgpu::VertexStepMode::VertexBufferNotUsed, {}},
{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {}},
{kFloat32x2Stride, wgpu::VertexStepMode::Instance, {}}});
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 3, 0, 0, 0,
true);
}
{
// The first slot it used so invalid if vertex buffer is not set to it.
wgpu::RenderPipeline pipeline = CreateRenderPipelineWithBufferDesc(
{{0, wgpu::VertexStepMode::Vertex, {}},
{kFloat32x4Stride, wgpu::VertexStepMode::Vertex, {}},
{kFloat32x2Stride, wgpu::VertexStepMode::Instance, {}}});
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 3, 0, 0, 0,
false);
}
}
// Verify zero array stride vertex buffer OOB for Draw and DrawIndexed are caught in command encoder
// This test only test cases that strideCount > 0. Cases of strideCount == 0 are tested in
// ZeroStrideCountVertexBufferNeverOOB.
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBuffer) {
// In this test, we use VertexBufferParams.maxValidAccessNumber > 0 to indicate that such
// buffer parameter meet the requirement of pipeline, and maxValidAccessNumber == 0 to
// indicate that such buffer parameter will cause OOB.
const std::vector<VertexBufferParams> kVertexParamsListForZeroStride = {
// Control case
{0, 28, 0, wgpu::kWholeSize, 1},
// Non-zero offset
{0, 28, 4, wgpu::kWholeSize, 0},
{0, 28, 28, wgpu::kWholeSize, 0},
// Non-default size
{0, 28, 0, 28, 1},
{0, 28, 0, 27, 0},
// Non-zero offset and size
{0, 32, 4, 28, 1},
{0, 31, 4, 27, 0},
{0, 31, 4, wgpu::kWholeSize, 0},
};
const std::vector<VertexBufferParams> kInstanceParamsListForZeroStride = {
// Control case
{0, 20, 0, wgpu::kWholeSize, 1},
// Non-zero offset
{0, 24, 4, wgpu::kWholeSize, 1},
{0, 23, 4, wgpu::kWholeSize, 0},
{0, 20, 4, wgpu::kWholeSize, 0},
{0, 20, 20, wgpu::kWholeSize, 0},
// Non-default size
{0, 21, 0, 20, 1},
{0, 20, 0, 19, 0},
// Non-zero offset and size
{0, 30, 4, 20, 1},
{0, 30, 4, 19, 0},
};
// Build a pipeline that require a vertex step mode vertex buffer no smaller than 28 bytes
// and an instance step mode buffer no smaller than 20 bytes
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithZeroArrayStride();
for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
constexpr uint64_t indexStride = sizeof(uint32_t);
// Build index buffer for 12 indexes
wgpu::Buffer indexBuffer = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
// Build vertex buffer for vertices
wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
// Build vertex buffer for instances
wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
VertexBufferList vertexBufferList = {
{0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
vertexParams.bufferSizeForEncoder},
{1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
instanceParams.bufferSizeForEncoder}};
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
const bool vertexModeBufferOOB = vertexParams.maxValidAccessNumber == 0;
const bool instanceModeBufferOOB = instanceParams.maxValidAccessNumber == 0;
// Draw validate both vertex and instance step mode buffer OOB.
// vertexCount and instanceCount doesn't matter, as array stride is zero and all
// vertex/instance access the same space of buffer, as long as both (vertexCount +
// firstVertex) and (instanceCount + firstInstance) are larger than zero.
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 100,
/* instanceCount */ 100, /* firstVertex */ 0,
/* firstInstance */ 0,
/* isSuccess */ !vertexModeBufferOOB && !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 100, 100, 100, 100,
!vertexModeBufferOOB && !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 100, 100,
!vertexModeBufferOOB && !instanceModeBufferOOB);
// DrawIndexed only validate instance step mode buffer OOB.
// indexCount doesn't matter as long as no index buffer OOB happened, and instanceCount
// doesn't matter as long as (instanceCount + firstInstance) are larger than zero.
TestRenderPassDrawIndexed(
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 12,
/* instanceCount */ 100, /* firstIndex */ 0, /* baseVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ !instanceModeBufferOOB);
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 0, 0, 0, 100,
!instanceModeBufferOOB);
}
}
}
// Verify all vertex buffer never OOB for Draw and DrawIndexed with zero stride count
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroStrideCountVertexBufferNeverOOB) {
// In this test we use a render pipeline with non-zero array stride and a render pipeline with
// zero array stride, both use a vertex step mode vertex buffer with lastStride = 16 and a
// instance step mode vertex buffer with lastStride = 8. Binding a buffer with size less than
// lastStride to corresponding buffer slot will result in a OOB validation error is strideCount
// is larger than 0, but shall not result in validation error if strideCount == 0.
// Create the render pipeline with non-zero array stride (larger than lastStride)
std::vector<PipelineVertexBufferDesc> bufferDescListNonZeroArrayStride = {
{20u, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
{12u, wgpu::VertexStepMode::Instance, {{3, wgpu::VertexFormat::Float32x2}}},
};
wgpu::RenderPipeline pipelineWithNonZeroArrayStride =
CreateRenderPipelineWithBufferDesc(bufferDescListNonZeroArrayStride);
// Create the render pipeline with zero array stride
std::vector<PipelineVertexBufferDesc> bufferDescListZeroArrayStride = {
{0u, wgpu::VertexStepMode::Vertex, {{0, wgpu::VertexFormat::Float32x4}}},
{0u, wgpu::VertexStepMode::Instance, {{3, wgpu::VertexFormat::Float32x2}}},
};
wgpu::RenderPipeline pipelineWithZeroArrayStride =
CreateRenderPipelineWithBufferDesc(bufferDescListZeroArrayStride);
const std::vector<VertexBufferParams> kVertexParamsListForZeroStride = {
// Size enough for 1 vertex
{kFloat32x4Stride, 16, 0, wgpu::kWholeSize, 1},
// No enough size for 1 vertex
{kFloat32x4Stride, 19, 4, wgpu::kWholeSize, 0},
{kFloat32x4Stride, 16, 16, wgpu::kWholeSize, 0},
};
const std::vector<VertexBufferParams> kInstanceParamsListForZeroStride = {
// Size enough for 1 instance
{kFloat32x2Stride, 8, 0, wgpu::kWholeSize, 1},
// No enough size for 1 instance
{kFloat32x2Stride, 11, 4, wgpu::kWholeSize, 0},
{kFloat32x2Stride, 8, 8, wgpu::kWholeSize, 0},
};
for (VertexBufferParams vertexParams : kVertexParamsListForZeroStride) {
for (VertexBufferParams instanceParams : kInstanceParamsListForZeroStride) {
for (wgpu::RenderPipeline pipeline :
{pipelineWithNonZeroArrayStride, pipelineWithZeroArrayStride}) {
constexpr wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
constexpr uint64_t indexStride = sizeof(uint32_t);
// Build index buffer for 1 index
wgpu::Buffer indexBuffer = CreateBuffer(indexStride, wgpu::BufferUsage::Index);
// Build vertex buffer for vertices
wgpu::Buffer vertexBuffer = CreateBuffer(vertexParams.bufferSize);
// Build vertex buffer for instances
wgpu::Buffer instanceBuffer = CreateBuffer(instanceParams.bufferSize);
VertexBufferList vertexBufferList = {
{0, vertexBuffer, vertexParams.bufferOffsetForEncoder,
vertexParams.bufferSizeForEncoder},
{1, instanceBuffer, instanceParams.bufferOffsetForEncoder,
instanceParams.bufferSizeForEncoder}};
IndexBufferDesc indexBufferDesc = {indexBuffer, indexFormat};
const bool vertexModeBufferOOB = vertexParams.maxValidAccessNumber == 0;
const bool instanceModeBufferOOB = instanceParams.maxValidAccessNumber == 0;
// Draw validate both vertex and instance step mode buffer OOB.
// Control case, non-zero stride for both step mode buffer
TestRenderPassDraw(pipeline, vertexBufferList, /* vertexCount */ 1,
/* instanceCount */ 1, /* firstVertex */ 0,
/* firstInstance */ 0,
/* isSuccess */ !vertexModeBufferOOB && !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 1, 0, 0, 1,
!vertexModeBufferOOB && !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 0, 1, 1, 0,
!vertexModeBufferOOB && !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 1, 1,
!vertexModeBufferOOB && !instanceModeBufferOOB);
// Vertex step mode buffer will never OOB if (vertexCount + firstVertex) is zero,
// and only instance step mode buffer OOB will fail validation
TestRenderPassDraw(pipeline, vertexBufferList, 0, 1, 0, 0, !instanceModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 0, 1, !instanceModeBufferOOB);
// Instance step mode buffer will never OOB if (instanceCount + firstInstance) is
// zero, and only vertex step mode buffer OOB will fail validation
TestRenderPassDraw(pipeline, vertexBufferList, 1, 0, 0, 0, !vertexModeBufferOOB);
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 1, 0, !vertexModeBufferOOB);
// If both (vertexCount + firstVertex) and (instanceCount + firstInstance) are zero,
// all vertex buffer will never OOB
TestRenderPassDraw(pipeline, vertexBufferList, 0, 0, 0, 0, true);
// DrawIndexed only validate instance step mode buffer OOB.
// Control case, non-zero stride for instance step mode buffer
TestRenderPassDrawIndexed(
pipeline, indexBufferDesc, vertexBufferList, /* indexCount */ 1,
/* instanceCount */ 1, /* firstIndex */ 0, /* baseVertex */ 0,
/* firstInstance */ 0, /* isSuccess */ !instanceModeBufferOOB);
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 1, 0, 0, 0,
1, !instanceModeBufferOOB);
// Instance step mode buffer will never OOB if (instanceCount + firstInstance) is
// zero, validation shall always succeed
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 1, 0, 0, 0,
0, true);
}
}
}
}
// Verify that if setVertexBuffer and/or setIndexBuffer for multiple times, only the last one is
// taken into account
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, SetBufferMultipleTime) {
wgpu::IndexFormat indexFormat = wgpu::IndexFormat::Uint32;
uint32_t indexStride = sizeof(uint32_t);
// Build index buffer for 11 indexes
wgpu::Buffer indexBuffer11 = CreateBuffer(11 * indexStride, wgpu::BufferUsage::Index);
// Build index buffer for 12 indexes
wgpu::Buffer indexBuffer12 = CreateBuffer(12 * indexStride, wgpu::BufferUsage::Index);
// Build vertex buffer for 2 vertices
wgpu::Buffer vertexBuffer2 = CreateBuffer(2 * kFloat32x4Stride);
// Build vertex buffer for 3 vertices
wgpu::Buffer vertexBuffer3 = CreateBuffer(3 * kFloat32x4Stride);
// Build vertex buffer for 4 instances
wgpu::Buffer instanceBuffer4 = CreateBuffer(4 * kFloat32x2Stride);
// Build vertex buffer for 5 instances
wgpu::Buffer instanceBuffer5 = CreateBuffer(5 * kFloat32x2Stride);
// Test for setting vertex buffer for multiple times
{
wgpu::RenderPipeline pipeline = CreateBasicRenderPipelineWithInstance();
// Set to vertexBuffer3 and instanceBuffer5 at last
VertexBufferList vertexBufferList = {{0, vertexBuffer2, 0, wgpu::kWholeSize},
{1, instanceBuffer4, 0, wgpu::kWholeSize},
{1, instanceBuffer5, 0, wgpu::kWholeSize},
{0, vertexBuffer3, 0, wgpu::kWholeSize}};
// For Draw, the max vertexCount is 3 and the max instanceCount is 5
TestRenderPassDraw(pipeline, vertexBufferList, 3, 5, 0, 0, true);
TestRenderPassDraw(pipeline, vertexBufferList, 4, 5, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, 3, 6, 0, 0, false);
// For DrawIndex, the max instanceCount is 5
TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12, 5,
0, 0, 0, true);
TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12, 6,
0, 0, 0, false);
// Set to vertexBuffer2 and instanceBuffer4 at last
vertexBufferList = VertexBufferList{{0, vertexBuffer3, 0, wgpu::kWholeSize},
{1, instanceBuffer5, 0, wgpu::kWholeSize},
{0, vertexBuffer2, 0, wgpu::kWholeSize},
{1, instanceBuffer4, 0, wgpu::kWholeSize}};
// For Draw, the max vertexCount is 2 and the max instanceCount is 4
TestRenderPassDraw(pipeline, vertexBufferList, 2, 4, 0, 0, true);
TestRenderPassDraw(pipeline, vertexBufferList, 3, 4, 0, 0, false);
TestRenderPassDraw(pipeline, vertexBufferList, 2, 5, 0, 0, false);
// For DrawIndex, the max instanceCount is 4
TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12, 4,
0, 0, 0, true);
TestRenderPassDrawIndexed(pipeline, {indexBuffer12, indexFormat}, vertexBufferList, 12, 5,
0, 0, 0, false);
}
// Test for setIndexBuffer multiple times
{
wgpu::RenderPipeline pipeline = CreateBasicRenderPipeline();
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
// Index buffer is set to indexBuffer12 at last
renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
// It should be ok to draw 12 index
renderPassEncoder.DrawIndexed(12, 1, 0, 0, 0);
renderPassEncoder.End();
// Expect success
encoder.Finish();
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
// Index buffer is set to indexBuffer12 at last
renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
// It should be index buffer OOB to draw 13 index
renderPassEncoder.DrawIndexed(13, 1, 0, 0, 0);
renderPassEncoder.End();
// Expect failure
ASSERT_DEVICE_ERROR(encoder.Finish());
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
// Index buffer is set to indexBuffer11 at last
renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
// It should be ok to draw 11 index
renderPassEncoder.DrawIndexed(11, 1, 0, 0, 0);
renderPassEncoder.End();
// Expect success
encoder.Finish();
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder renderPassEncoder =
encoder.BeginRenderPass(GetBasicRenderPassDescriptor());
renderPassEncoder.SetPipeline(pipeline);
// Index buffer is set to indexBuffer11 at last
renderPassEncoder.SetIndexBuffer(indexBuffer12, indexFormat);
renderPassEncoder.SetIndexBuffer(indexBuffer11, indexFormat);
renderPassEncoder.SetVertexBuffer(0, vertexBuffer3);
// It should be index buffer OOB to draw 12 index
renderPassEncoder.DrawIndexed(12, 1, 0, 0, 0);
renderPassEncoder.End();
// Expect failure
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
}
} // anonymous namespace
} // namespace dawn