mirror of
https://github.com/encounter/dawn-cmake.git
synced 2025-12-17 17:05:31 +00:00
Reland "dawn_native: Indirect draw/dispatch"
This is a reland of 7eb6be186b
Fixes casting issue on 32 bit machines
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>
Change-Id: Id28c5658ee18ec5c030f721fb44d9f11ebe21ff9
Bug:dawn:54,chromium:972358
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/7961
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Idan Raiter <idanr@google.com>
This commit is contained in:
committed by
Commit Bot service account
parent
9d4d3eac14
commit
05f7ad5ac8
131
src/tests/end2end/ComputeIndirectTests.cpp
Normal file
131
src/tests/end2end/ComputeIndirectTests.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
// 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);
|
||||
165
src/tests/end2end/DrawIndexedIndirectTests.cpp
Normal file
165
src/tests/end2end/DrawIndexedIndirectTests.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// 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);
|
||||
128
src/tests/end2end/DrawIndirectTests.cpp
Normal file
128
src/tests/end2end/DrawIndirectTests.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
// 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);
|
||||
@@ -0,0 +1,90 @@
|
||||
// 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);
|
||||
}
|
||||
143
src/tests/unittests/validation/DrawIndirectValidationTests.cpp
Normal file
143
src/tests/unittests/validation/DrawIndirectValidationTests.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
// 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);
|
||||
}
|
||||
Reference in New Issue
Block a user