Revert "dawn_native: Indirect draw/dispatch"

This reverts commit 7eb6be186b.

Reason for revert: This change is breaking Clusterfuzz bots. crbug.com/972358

Bug: chromium:972358

Original change's description:
> dawn_native: Indirect draw/dispatch
>
> Adds indirect draw and dispatch for all backends (without validation).
>
> Tests for opengl negative offset are skipped since there is no easy
> way to add the index buffer offset. Current idea is to use a compute
> shader to modify the indirect draw buffer.
>
> Change-Id: I1d3eec7c699b211423f4b911769cca17bfbcd045
> Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7481
> Commit-Queue: Idan Raiter <idanr@google.com>
> Reviewed-by: Kai Ninomiya <kainino@chromium.org>

TBR=cwallez@chromium.org,kainino@chromium.org,idanr@google.com

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: I9b7b63de0900e20c4da8561ceb9b2dece77c3885
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7960
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng
2019-06-10 17:15:27 +00:00
committed by Commit Bot service account
parent 7eb6be186b
commit 39b8ddfbd9
21 changed files with 1 additions and 1003 deletions

View File

@@ -1,131 +0,0 @@
// Copyright 2019 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 "dawn/dawncpp.h"
#include "tests/DawnTest.h"
#include "utils/DawnHelpers.h"
#include <array>
#include <initializer_list>
class ComputeIndirectTests : public DawnTest {
public:
// Write into the output buffer if we saw the biggest dispatch
// This is a workaround since D3D12 doesn't have gl_NumWorkGroups
const char* shaderSource = R"(
#version 450
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(std140, set = 0, binding = 0) uniform inputBuf {
uvec3 expectedDispatch;
};
layout(std140, set = 0, binding = 1) buffer outputBuf {
uvec3 workGroups;
};
void main() {
if (gl_GlobalInvocationID == expectedDispatch - uvec3(1, 1, 1)) {
workGroups = expectedDispatch;
}
})";
void BasicTest(std::initializer_list<uint32_t> buffer, uint64_t indirectOffset);
};
void ComputeIndirectTests::BasicTest(std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset) {
dawn::BindGroupLayout bgl = utils::MakeBindGroupLayout(
device, {
{0, dawn::ShaderStageBit::Compute, dawn::BindingType::UniformBuffer},
{1, dawn::ShaderStageBit::Compute, dawn::BindingType::StorageBuffer},
});
// Set up shader and pipeline
dawn::ShaderModule module =
utils::CreateShaderModule(device, dawn::ShaderStage::Compute, shaderSource);
dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, &bgl);
dawn::ComputePipelineDescriptor csDesc;
csDesc.layout = pl;
dawn::PipelineStageDescriptor computeStage;
computeStage.module = module;
computeStage.entryPoint = "main";
csDesc.computeStage = &computeStage;
dawn::ComputePipeline pipeline = device.CreateComputePipeline(&csDesc);
// Set up dst storage buffer to contain dispatch x, y, z
dawn::Buffer dst = utils::CreateBufferFromData<uint32_t>(device,
dawn::BufferUsageBit::Storage |
dawn::BufferUsageBit::TransferSrc |
dawn::BufferUsageBit::TransferDst,
{0, 0, 0});
std::vector<uint32_t> indirectBufferData = bufferList;
dawn::Buffer indirectBuffer =
utils::CreateBufferFromData<uint32_t>(device, dawn::BufferUsageBit::Indirect, bufferList);
dawn::Buffer expectedBuffer =
utils::CreateBufferFromData(device, &indirectBufferData[indirectOffset / sizeof(uint32_t)],
3 * sizeof(uint32_t), dawn::BufferUsageBit::Uniform);
// Set up bind group and issue dispatch
dawn::BindGroup bindGroup =
utils::MakeBindGroup(device, bgl,
{
{0, expectedBuffer, 0, 3 * sizeof(uint32_t)},
{1, dst, 0, 3 * sizeof(uint32_t)},
});
dawn::CommandBuffer commands;
{
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.SetBindGroup(0, bindGroup, 0, nullptr);
pass.DispatchIndirect(indirectBuffer, indirectOffset);
pass.EndPass();
commands = encoder.Finish();
}
queue.Submit(1, &commands);
// Verify the dispatch got called with group counts in indirect buffer
EXPECT_BUFFER_U32_RANGE_EQ(&indirectBufferData[indirectOffset / sizeof(uint32_t)], dst, 0, 3);
}
// Test basic indirect
TEST_P(ComputeIndirectTests, Basic) {
// See https://bugs.chromium.org/p/dawn/issues/detail?id=159
DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia());
BasicTest({2, 3, 4}, 0);
}
// Test indirect with buffer offset
TEST_P(ComputeIndirectTests, IndirectOffset) {
// See https://bugs.chromium.org/p/dawn/issues/detail?id=159
DAWN_SKIP_TEST_IF(IsD3D12() && IsNvidia());
BasicTest({0, 0, 0, 2, 3, 4}, 3 * sizeof(uint32_t));
}
DAWN_INSTANTIATE_TEST(ComputeIndirectTests,
D3D12Backend,
MetalBackend,
OpenGLBackend,
VulkanBackend);

View File

@@ -1,165 +0,0 @@
// Copyright 2018 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/DawnTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h"
constexpr uint32_t kRTSize = 4;
class DrawIndexedIndirectTest : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
dawn::ShaderModule vsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
#version 450
layout(location = 0) in vec4 pos;
void main() {
gl_Position = pos;
})");
dawn::ShaderModule fsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
#version 450
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
})");
utils::ComboRenderPipelineDescriptor descriptor(device);
descriptor.cVertexStage.module = vsModule;
descriptor.cFragmentStage.module = fsModule;
descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip;
descriptor.cVertexInput.bufferCount = 1;
descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float);
descriptor.cVertexInput.cBuffers[0].attributeCount = 1;
descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4;
descriptor.cColorStates[0]->format = renderPass.colorFormat;
pipeline = device.CreateRenderPipeline(&descriptor);
vertexBuffer = utils::CreateBufferFromData<float>(
device, dawn::BufferUsageBit::Vertex,
{// First quad: the first 3 vertices represent the bottom left triangle
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f,
0.0f, 1.0f,
// Second quad: the first 3 vertices represent the top right triangle
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f,
0.0f, 1.0f});
indexBuffer = utils::CreateBufferFromData<uint32_t>(
device, dawn::BufferUsageBit::Index,
{0, 1, 2, 0, 3, 1,
// The indices below are added to test negatve baseVertex
0 + 4, 1 + 4, 2 + 4, 0 + 4, 3 + 4, 1 + 4});
}
utils::BasicRenderPass renderPass;
dawn::RenderPipeline pipeline;
dawn::Buffer vertexBuffer;
dawn::Buffer indexBuffer;
void Test(std::initializer_list<uint32_t> bufferList,
uint64_t indexOffset,
uint64_t indirectOffset,
RGBA8 bottomLeftExpected,
RGBA8 topRightExpected) {
dawn::Buffer indirectBuffer = utils::CreateBufferFromData<uint32_t>(
device, dawn::BufferUsageBit::Indirect, bufferList);
uint64_t zeroOffset = 0;
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
{
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
pass.SetIndexBuffer(indexBuffer, indexOffset);
pass.DrawIndexedIndirect(indirectBuffer, indirectOffset);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3);
EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1);
}
};
// The most basic DrawIndexed triangle draw.
TEST_P(DrawIndexedIndirectTest, Uint32) {
RGBA8 filled(0, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
// Test a draw with no indices.
Test({0, 0, 0, 0, 0}, 0, 0, notFilled, notFilled);
// Test a draw with only the first 3 indices of the first quad (bottom left triangle)
Test({3, 1, 0, 0, 0}, 0, 0, filled, notFilled);
// Test a draw with only the last 3 indices of the first quad (top right triangle)
Test({3, 1, 3, 0, 0}, 0, 0, notFilled, filled);
// Test a draw with all 6 indices (both triangles).
Test({6, 1, 0, 0, 0}, 0, 0, filled, filled);
}
// Test the parameter 'baseVertex' of DrawIndexed() works.
TEST_P(DrawIndexedIndirectTest, BaseVertex) {
// TODO(crbug.com/dawn/161): add workaround for OpenGL index buffer offset (could be compute
// shader that adds it to the draw calls)
DAWN_SKIP_TEST_IF(IsOpenGL());
RGBA8 filled(0, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
// Test a draw with only the first 3 indices of the second quad (top right triangle)
Test({3, 1, 0, 4, 0}, 0, 0, notFilled, filled);
// Test a draw with only the last 3 indices of the second quad (bottom left triangle)
Test({3, 1, 3, 4, 0}, 0, 0, filled, notFilled);
// Test negative baseVertex
// Test a draw with only the first 3 indices of the first quad (bottom left triangle)
Test({3, 1, 0, -4, 0}, 6 * sizeof(uint32_t), 0, filled, notFilled);
// Test a draw with only the last 3 indices of the first quad (top right triangle)
Test({3, 1, 3, -4, 0}, 6 * sizeof(uint32_t), 0, notFilled, filled);
}
TEST_P(DrawIndexedIndirectTest, IndirectOffset) {
RGBA8 filled(0, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
// Test an offset draw call, with indirect buffer containing 2 calls:
// 1) first 3 indices of the second quad (top right triangle)
// 2) last 3 indices of the second quad
// Test #1 (no offset)
Test({3, 1, 0, 4, 0, 3, 1, 3, 4, 0}, 0, 0, notFilled, filled);
// Offset to draw #2
Test({3, 1, 0, 4, 0, 3, 1, 3, 4, 0}, 0, 5 * sizeof(uint32_t), filled, notFilled);
}
DAWN_INSTANTIATE_TEST(DrawIndexedIndirectTest,
D3D12Backend,
MetalBackend,
OpenGLBackend,
VulkanBackend);

View File

@@ -1,128 +0,0 @@
// Copyright 2019 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/DawnTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h"
constexpr uint32_t kRTSize = 4;
class DrawIndirectTest : public DawnTest {
protected:
void SetUp() override {
DawnTest::SetUp();
renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
dawn::ShaderModule vsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
#version 450
layout(location = 0) in vec4 pos;
void main() {
gl_Position = pos;
})");
dawn::ShaderModule fsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
#version 450
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(0.0, 1.0, 0.0, 1.0);
})");
utils::ComboRenderPipelineDescriptor descriptor(device);
descriptor.cVertexStage.module = vsModule;
descriptor.cFragmentStage.module = fsModule;
descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip;
descriptor.cVertexInput.bufferCount = 1;
descriptor.cVertexInput.cBuffers[0].stride = 4 * sizeof(float);
descriptor.cVertexInput.cBuffers[0].attributeCount = 1;
descriptor.cVertexInput.cAttributes[0].format = dawn::VertexFormat::Float4;
descriptor.cColorStates[0]->format = renderPass.colorFormat;
pipeline = device.CreateRenderPipeline(&descriptor);
vertexBuffer = utils::CreateBufferFromData<float>(
device, dawn::BufferUsageBit::Vertex,
{// The bottom left triangle
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
// The top right triangle
-1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f});
}
utils::BasicRenderPass renderPass;
dawn::RenderPipeline pipeline;
dawn::Buffer vertexBuffer;
void Test(std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset,
RGBA8 bottomLeftExpected,
RGBA8 topRightExpected) {
dawn::Buffer indirectBuffer = utils::CreateBufferFromData<uint32_t>(
device, dawn::BufferUsageBit::Indirect, bufferList);
uint64_t zeroOffset = 0;
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
{
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
pass.SetPipeline(pipeline);
pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
pass.DrawIndirect(indirectBuffer, indirectOffset);
pass.EndPass();
}
dawn::CommandBuffer commands = encoder.Finish();
queue.Submit(1, &commands);
EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3);
EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1);
}
};
// The basic triangle draw.
TEST_P(DrawIndirectTest, Uint32) {
RGBA8 filled(0, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
// Test a draw with no indices.
Test({0, 0, 0, 0}, 0, notFilled, notFilled);
// Test a draw with only the first 3 indices (bottom left triangle)
Test({3, 1, 0, 0}, 0, filled, notFilled);
// Test a draw with only the last 3 indices (top right triangle)
Test({3, 1, 3, 0}, 0, notFilled, filled);
// Test a draw with all 6 indices (both triangles).
Test({6, 1, 0, 0}, 0, filled, filled);
}
TEST_P(DrawIndirectTest, IndirectOffset) {
RGBA8 filled(0, 255, 0, 255);
RGBA8 notFilled(0, 0, 0, 0);
// Test an offset draw call, with indirect buffer containing 2 calls:
// 1) only the first 3 indices (bottom left triangle)
// 2) only the last 3 indices (top right triangle)
// Test #1 (no offset)
Test({3, 1, 0, 0, 3, 1, 3, 0}, 0, filled, notFilled);
// Offset to draw #2
Test({3, 1, 0, 0, 3, 1, 3, 0}, 4 * sizeof(uint32_t), notFilled, filled);
}
DAWN_INSTANTIATE_TEST(DrawIndirectTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend);

View File

@@ -1,90 +0,0 @@
// Copyright 2019 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 <initializer_list>
#include <limits>
#include "tests/unittests/validation/ValidationTest.h"
#include "utils/DawnHelpers.h"
class ComputeIndirectValidationTest : public ValidationTest {
protected:
void SetUp() override {
ValidationTest::SetUp();
dawn::ShaderModule computeModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Compute, R"(
#version 450
layout(local_size_x = 1) in;
void main() {
})");
// Set up compute pipeline
dawn::PipelineLayout pl = utils::MakeBasicPipelineLayout(device, nullptr);
dawn::ComputePipelineDescriptor csDesc;
csDesc.layout = pl;
dawn::PipelineStageDescriptor computeStage;
computeStage.module = computeModule;
computeStage.entryPoint = "main";
csDesc.computeStage = &computeStage;
pipeline = device.CreateComputePipeline(&csDesc);
}
void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) {
if (expectation == utils::Expectation::Success) {
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
void TestIndirectOffset(utils::Expectation expectation,
std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset) {
dawn::Buffer indirectBuffer = utils::CreateBufferFromData<uint32_t>(
device, dawn::BufferUsageBit::Indirect, bufferList);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::ComputePassEncoder pass = encoder.BeginComputePass();
pass.SetPipeline(pipeline);
pass.DispatchIndirect(indirectBuffer, indirectOffset);
pass.EndPass();
ValidateExpectation(encoder, expectation);
}
dawn::ComputePipeline pipeline;
};
// Verify out of bounds indirect dispatch calls are caught early
TEST_F(ComputeIndirectValidationTest, IndirectOffsetBounds) {
// In bounds
TestIndirectOffset(utils::Expectation::Success, {1, 2, 3}, 0);
// In bounds, bigger buffer
TestIndirectOffset(utils::Expectation::Success, {1, 2, 3, 4, 5, 6}, 0);
// In bounds, bigger buffer, positive offset
TestIndirectOffset(utils::Expectation::Success, {1, 2, 3, 4, 5, 6}, 3 * sizeof(uint32_t));
// Out of bounds, buffer too small
TestIndirectOffset(utils::Expectation::Failure, {1, 2}, 0);
// Out of bounds, index too big
TestIndirectOffset(utils::Expectation::Failure, {1, 2, 3}, 1 * sizeof(uint32_t));
// Out of bounds, index past buffer
TestIndirectOffset(utils::Expectation::Failure, {1, 2, 3}, 4 * sizeof(uint32_t));
// Out of bounds, index + size of command overflows
uint64_t offset = std::numeric_limits<uint64_t>::max();
TestIndirectOffset(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6}, offset);
}

View File

@@ -1,143 +0,0 @@
// Copyright 2019 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 <initializer_list>
#include <limits>
#include "tests/unittests/validation/ValidationTest.h"
#include "utils/ComboRenderPipelineDescriptor.h"
#include "utils/DawnHelpers.h"
class DrawIndirectValidationTest : public ValidationTest {
protected:
void SetUp() override {
ValidationTest::SetUp();
dawn::ShaderModule vsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
#version 450
void main() {
gl_Position = vec4(0.0);
})");
dawn::ShaderModule fsModule =
utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
#version 450
layout(location = 0) out vec4 fragColor;
void main() {
fragColor = vec4(0.0);
})");
// Set up render pipeline
dawn::PipelineLayout pipelineLayout = utils::MakeBasicPipelineLayout(device, nullptr);
utils::ComboRenderPipelineDescriptor descriptor(device);
descriptor.layout = pipelineLayout;
descriptor.cVertexStage.module = vsModule;
descriptor.cFragmentStage.module = fsModule;
pipeline = device.CreateRenderPipeline(&descriptor);
}
void ValidateExpectation(dawn::CommandEncoder encoder, utils::Expectation expectation) {
if (expectation == utils::Expectation::Success) {
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
void TestIndirectOffsetDrawIndexed(utils::Expectation expectation,
std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset) {
TestIndirectOffset(expectation, bufferList, indirectOffset, true);
}
void TestIndirectOffsetDraw(utils::Expectation expectation,
std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset) {
TestIndirectOffset(expectation, bufferList, indirectOffset, false);
}
void TestIndirectOffset(utils::Expectation expectation,
std::initializer_list<uint32_t> bufferList,
uint64_t indirectOffset,
bool indexed) {
dawn::Buffer indirectBuffer = utils::CreateBufferFromData<uint32_t>(
device, dawn::BufferUsageBit::Indirect, bufferList);
DummyRenderPass renderPass(device);
dawn::CommandEncoder encoder = device.CreateCommandEncoder();
dawn::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetPipeline(pipeline);
if (indexed) {
uint32_t zeros[100] = {};
dawn::Buffer indexBuffer = utils::CreateBufferFromData(device, zeros, sizeof(zeros),
dawn::BufferUsageBit::Index);
pass.SetIndexBuffer(indexBuffer, 0);
pass.DrawIndexedIndirect(indirectBuffer, indirectOffset);
} else {
pass.DrawIndirect(indirectBuffer, indirectOffset);
}
pass.EndPass();
ValidateExpectation(encoder, expectation);
}
dawn::RenderPipeline pipeline;
};
// Verify out of bounds indirect draw calls are caught early
TEST_F(DrawIndirectValidationTest, DrawIndirectOffsetBounds) {
// In bounds
TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4}, 0);
// In bounds, bigger buffer
TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7}, 0);
// In bounds, bigger buffer, positive offset
TestIndirectOffsetDraw(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8},
4 * sizeof(uint32_t));
// Out of bounds, buffer too small
TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3}, 0);
// Out of bounds, index too big
TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4}, 1 * sizeof(uint32_t));
// Out of bounds, index past buffer
TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4}, 5 * sizeof(uint32_t));
// Out of bounds, index + size of command overflows
uint64_t offset = std::numeric_limits<uint64_t>::max();
TestIndirectOffsetDraw(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6, 7}, offset);
}
// Verify out of bounds indirect draw indexed calls are caught early
TEST_F(DrawIndirectValidationTest, DrawIndexedIndirectOffsetBounds) {
// In bounds
TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5}, 0);
// In bounds, bigger buffer
TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8, 9}, 0);
// In bounds, bigger buffer, positive offset
TestIndirectOffsetDrawIndexed(utils::Expectation::Success, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
5 * sizeof(uint32_t));
// Out of bounds, buffer too small
TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4}, 0);
// Out of bounds, index too big
TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5},
1 * sizeof(uint32_t));
// Out of bounds, index past buffer
TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5},
5 * sizeof(uint32_t));
// Out of bounds, index + size of command overflows
uint64_t offset = std::numeric_limits<uint64_t>::max();
TestIndirectOffsetDrawIndexed(utils::Expectation::Failure, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
offset);
}