mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-05-15 11:51:22 +00:00
For size parameter in setIndexBuffer and setVertexBuffer, use wgpu::kWholeSize rather than 0 to indicate using the default size, i.e. remaining buffer size after offset. Using size=0 is still avaliable but will cause a deprecation warning. Bug: dawn:1058 Change-Id: I4c4f6ca6fc7346c225815bdcd607fed110ce2b9a Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/65020 Commit-Queue: Zhaoming Jiang <zhaoming.jiang@intel.com> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
751 lines
37 KiB
C++
751 lines
37 KiB
C++
// 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 "tests/unittests/validation/ValidationTest.h"
|
|
|
|
#include "common/Constants.h"
|
|
#include "utils/ComboRenderPipelineDescriptor.h"
|
|
#include "utils/WGPUHelpers.h"
|
|
|
|
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"(
|
|
[[stage(fragment)]] fn main() -> [[location(0)]] vec4<f32> {
|
|
return vec4<f32>(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 << " : vec4<f32>,";
|
|
attributeCount++;
|
|
}
|
|
}
|
|
|
|
std::stringstream shaderStringStream;
|
|
|
|
shaderStringStream << R"(
|
|
[[stage(vertex)]]
|
|
fn main()" << inputStringStream.str()
|
|
<< R"() -> [[builtin(position)]] vec4<f32> {
|
|
return vec4<f32>(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.EndPass();
|
|
|
|
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.EndPass();
|
|
|
|
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},
|
|
};
|
|
// 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},
|
|
};
|
|
|
|
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, 3, 1, 0, 0, true);
|
|
}
|
|
|
|
{
|
|
// Explicit zero size
|
|
VertexBufferList vertexBufferList = {{0, vertexBuffer, 0, 0}};
|
|
// size=0 used as default size is deprecated but still avaliable.
|
|
// TODO(dawn:1058): Change the expectation when size=0 special case is removed.
|
|
EXPECT_DEPRECATION_WARNING(
|
|
TestRenderPassDraw(pipeline, vertexBufferList, 3, 1, 0, 0, true));
|
|
}
|
|
}
|
|
|
|
// 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}};
|
|
|
|
uint32_t n = params.maxValidAccessNumber;
|
|
// It is ok to draw n vertices with vertex buffer
|
|
TestRenderPassDraw(pipeline, vertexBufferList, n, 1, 0, 0, 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},
|
|
};
|
|
|
|
uint32_t vert = vertexParams.maxValidAccessNumber;
|
|
uint32_t inst = instanceParams.maxValidAccessNumber;
|
|
// It is ok to draw vert vertices
|
|
TestRenderPassDraw(pipeline, vertexBufferList, vert, 1, 0, 0, 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);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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, 12, 1, 0, 0, 0,
|
|
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};
|
|
|
|
uint32_t n = params.maxValidIndexNumber;
|
|
|
|
// Control case
|
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, n, 5, 0, 0, 0,
|
|
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);
|
|
|
|
auto indexFormat = wgpu::IndexFormat::Uint32;
|
|
auto 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};
|
|
|
|
uint32_t inst = instanceParams.maxValidAccessNumber;
|
|
// Control case
|
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, inst, 0,
|
|
0, 0, 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 instance mode vertex buffer OOB for DrawIndexed are caught in command encoder
|
|
TEST_F(DrawVertexAndIndexBufferOOBValidationTests, ZeroArrayStrideVertexBufferOOB) {
|
|
// 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) {
|
|
auto indexFormat = wgpu::IndexFormat::Uint32;
|
|
auto 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 isSuccess = (vertexParams.maxValidAccessNumber > 0) &&
|
|
(instanceParams.maxValidAccessNumber > 0);
|
|
// vertexCount and instanceCount doesn't matter, as array stride is zero and all
|
|
// vertex/instance access the same space of buffer
|
|
TestRenderPassDraw(pipeline, vertexBufferList, 100, 100, 0, 0, isSuccess);
|
|
// indexCount doesn't matter as long as no index buffer OOB happened
|
|
TestRenderPassDrawIndexed(pipeline, indexBufferDesc, vertexBufferList, 12, 100, 0,
|
|
0, 0, isSuccess);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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.EndPass();
|
|
|
|
// 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.EndPass();
|
|
|
|
// 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.EndPass();
|
|
|
|
// 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.EndPass();
|
|
|
|
// Expect failure
|
|
ASSERT_DEVICE_ERROR(encoder.Finish());
|
|
}
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|