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>
This commit is contained in:
Idan Raiter 2019-06-07 17:32:37 +00:00 committed by Commit Bot service account
parent de1ac9fd7b
commit 7eb6be186b
21 changed files with 1003 additions and 1 deletions

View File

@ -568,9 +568,11 @@ test("dawn_unittests") {
"src/tests/unittests/validation/BindGroupValidationTests.cpp",
"src/tests/unittests/validation/BufferValidationTests.cpp",
"src/tests/unittests/validation/CommandBufferValidationTests.cpp",
"src/tests/unittests/validation/ComputeIndirectValidationTests.cpp",
"src/tests/unittests/validation/ComputeValidationTests.cpp",
"src/tests/unittests/validation/CopyCommandsValidationTests.cpp",
"src/tests/unittests/validation/DebugMarkerValidationTests.cpp",
"src/tests/unittests/validation/DrawIndirectValidationTests.cpp",
"src/tests/unittests/validation/DynamicStateCommandValidationTests.cpp",
"src/tests/unittests/validation/FenceValidationTests.cpp",
"src/tests/unittests/validation/QueueSubmitValidationTests.cpp",
@ -632,12 +634,15 @@ test("dawn_end2end_tests") {
"src/tests/end2end/ClipSpaceTests.cpp",
"src/tests/end2end/ColorStateTests.cpp",
"src/tests/end2end/ComputeCopyStorageBufferTests.cpp",
"src/tests/end2end/ComputeIndirectTests.cpp",
"src/tests/end2end/ComputeSharedMemoryTests.cpp",
"src/tests/end2end/CopyTests.cpp",
"src/tests/end2end/DebugMarkerTests.cpp",
"src/tests/end2end/DepthStencilStateTests.cpp",
"src/tests/end2end/DestroyTests.cpp",
"src/tests/end2end/DrawIndexedIndirectTests.cpp",
"src/tests/end2end/DrawIndexedTests.cpp",
"src/tests/end2end/DrawIndirectTests.cpp",
"src/tests/end2end/DrawTests.cpp",
"src/tests/end2end/DynamicBufferOffsetTests.cpp",
"src/tests/end2end/FenceTests.cpp",

View File

@ -204,7 +204,8 @@
{"value": 16, "name": "index"},
{"value": 32, "name": "vertex"},
{"value": 64, "name": "uniform"},
{"value": 128, "name": "storage"}
{"value": 128, "name": "storage"},
{"value": 256, "name": "indirect"}
]
},
"char": {
@ -353,6 +354,13 @@
{"name": "z", "type": "uint32_t"}
]
},
{
"name": "dispatch indirect",
"args": [
{"name": "indirect buffer", "type": "buffer"},
{"name": "indirect offset", "type": "uint64_t"}
]
},
{
"name": "end pass",
"TODO": "This returns the top-level encoder in the WebGPU IDL"
@ -764,6 +772,20 @@
{"name": "first instance", "type": "uint32_t"}
]
},
{
"name": "draw indirect",
"args": [
{"name": "indirect buffer", "type": "buffer"},
{"name": "indirect offset", "type": "uint64_t"}
]
},
{
"name": "draw indexed indirect",
"args": [
{"name": "indirect buffer", "type": "buffer"},
{"name": "indirect offset", "type": "uint64_t"}
]
},
{
"name": "insert debug marker",
"args": [

View File

@ -35,6 +35,10 @@ static constexpr uint32_t kMaxColorAttachments = 4u;
static constexpr uint32_t kTextureRowPitchAlignment = 256u;
// Dynamic buffer offsets require offset to be divisible by 256
static constexpr uint64_t kMinDynamicBufferOffsetAlignment = 256u;
// Indirect command sizes
static constexpr uint64_t kDispatchIndirectSize = 3 * sizeof(uint32_t);
static constexpr uint64_t kDrawIndirectSize = 4 * sizeof(uint32_t);
static constexpr uint64_t kDrawIndexedIndirectSize = 5 * sizeof(uint32_t);
// Non spec defined constants.
static constexpr float kLodMin = 0.0;

View File

@ -999,6 +999,13 @@ namespace dawn_native {
DAWN_TRY(persistentState.ValidateCanDispatch());
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* cmd = mIterator.NextCommand<DispatchIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDispatch());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mIterator.NextCommand<InsertDebugMarkerCmd>();
mIterator.NextData<char>(cmd->length + 1);
@ -1085,6 +1092,20 @@ namespace dawn_native {
DAWN_TRY(persistentState.ValidateCanDrawIndexed());
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* cmd = mIterator.NextCommand<DrawIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDraw());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* cmd = mIterator.NextCommand<DrawIndexedIndirectCmd>();
DAWN_TRY(persistentState.ValidateCanDrawIndexed());
usageTracker.BufferUsedAs(cmd->indirectBuffer.Get(),
dawn::BufferUsageBit::Indirect);
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mIterator.NextCommand<InsertDebugMarkerCmd>();
mIterator.NextData<char>(cmd->length + 1);

View File

@ -58,6 +58,10 @@ namespace dawn_native {
DispatchCmd* dispatch = commands->NextCommand<DispatchCmd>();
dispatch->~DispatchCmd();
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = commands->NextCommand<DispatchIndirectCmd>();
dispatch->~DispatchIndirectCmd();
} break;
case Command::Draw: {
DrawCmd* draw = commands->NextCommand<DrawCmd>();
draw->~DrawCmd();
@ -66,6 +70,14 @@ namespace dawn_native {
DrawIndexedCmd* draw = commands->NextCommand<DrawIndexedCmd>();
draw->~DrawIndexedCmd();
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* draw = commands->NextCommand<DrawIndirectCmd>();
draw->~DrawIndirectCmd();
} break;
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* draw = commands->NextCommand<DrawIndexedIndirectCmd>();
draw->~DrawIndexedIndirectCmd();
} break;
case Command::EndComputePass: {
EndComputePassCmd* cmd = commands->NextCommand<EndComputePassCmd>();
cmd->~EndComputePassCmd();
@ -163,6 +175,10 @@ namespace dawn_native {
commands->NextCommand<DispatchCmd>();
break;
case Command::DispatchIndirect:
commands->NextCommand<DispatchIndirectCmd>();
break;
case Command::Draw:
commands->NextCommand<DrawCmd>();
break;
@ -171,6 +187,14 @@ namespace dawn_native {
commands->NextCommand<DrawIndexedCmd>();
break;
case Command::DrawIndirect:
commands->NextCommand<DrawIndirectCmd>();
break;
case Command::DrawIndexedIndirect:
commands->NextCommand<DrawIndexedIndirectCmd>();
break;
case Command::EndComputePass:
commands->NextCommand<EndComputePassCmd>();
break;

View File

@ -38,8 +38,11 @@ namespace dawn_native {
CopyTextureToBuffer,
CopyTextureToTexture,
Dispatch,
DispatchIndirect,
Draw,
DrawIndexed,
DrawIndirect,
DrawIndexedIndirect,
EndComputePass,
EndRenderPass,
InsertDebugMarker,
@ -133,6 +136,11 @@ namespace dawn_native {
uint32_t z;
};
struct DispatchIndirectCmd {
Ref<BufferBase> indirectBuffer;
uint64_t indirectOffset;
};
struct DrawCmd {
uint32_t vertexCount;
uint32_t instanceCount;
@ -148,6 +156,16 @@ namespace dawn_native {
uint32_t firstInstance;
};
struct DrawIndirectCmd {
Ref<BufferBase> indirectBuffer;
uint64_t indirectOffset;
};
struct DrawIndexedIndirectCmd {
Ref<BufferBase> indirectBuffer;
uint64_t indirectOffset;
};
struct EndComputePassCmd {};
struct EndRenderPassCmd {};

View File

@ -14,6 +14,7 @@
#include "dawn_native/ComputePassEncoder.h"
#include "dawn_native/Buffer.h"
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/Commands.h"
#include "dawn_native/ComputePipeline.h"
@ -49,6 +50,25 @@ namespace dawn_native {
dispatch->z = z;
}
void ComputePassEncoderBase::DispatchIndirect(BufferBase* indirectBuffer,
uint64_t indirectOffset) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) {
return;
}
if (indirectOffset >= indirectBuffer->GetSize() ||
indirectOffset + kDispatchIndirectSize > indirectBuffer->GetSize()) {
mTopLevelEncoder->HandleError("Indirect offset out of bounds");
return;
}
DispatchIndirectCmd* dispatch =
mAllocator->Allocate<DispatchIndirectCmd>(Command::DispatchIndirect);
dispatch->indirectBuffer = indirectBuffer;
dispatch->indirectOffset = indirectOffset;
}
void ComputePassEncoderBase::SetPipeline(ComputePipelineBase* pipeline) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) {

View File

@ -34,6 +34,7 @@ namespace dawn_native {
CommandEncoderBase* topLevelEncoder);
void Dispatch(uint32_t x, uint32_t y, uint32_t z);
void DispatchIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset);
void SetPipeline(ComputePipelineBase* pipeline);
protected:

View File

@ -14,6 +14,7 @@
#include "dawn_native/RenderPassEncoder.h"
#include "common/Constants.h"
#include "dawn_native/Buffer.h"
#include "dawn_native/CommandEncoder.h"
#include "dawn_native/Commands.h"
@ -73,6 +74,42 @@ namespace dawn_native {
draw->firstInstance = firstInstance;
}
void RenderPassEncoderBase::DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) {
return;
}
if (indirectOffset >= indirectBuffer->GetSize() ||
indirectOffset + kDrawIndirectSize > indirectBuffer->GetSize()) {
mTopLevelEncoder->HandleError("Indirect offset out of bounds");
return;
}
DrawIndirectCmd* cmd = mAllocator->Allocate<DrawIndirectCmd>(Command::DrawIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
}
void RenderPassEncoderBase::DrawIndexedIndirect(BufferBase* indirectBuffer,
uint64_t indirectOffset) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(indirectBuffer))) {
return;
}
if (indirectOffset >= indirectBuffer->GetSize() ||
indirectOffset + kDrawIndexedIndirectSize > indirectBuffer->GetSize()) {
mTopLevelEncoder->HandleError("Indirect offset out of bounds");
return;
}
DrawIndexedIndirectCmd* cmd =
mAllocator->Allocate<DrawIndexedIndirectCmd>(Command::DrawIndexedIndirect);
cmd->indirectBuffer = indirectBuffer;
cmd->indirectOffset = indirectOffset;
}
void RenderPassEncoderBase::SetPipeline(RenderPipelineBase* pipeline) {
if (mTopLevelEncoder->ConsumedError(ValidateCanRecordCommands()) ||
mTopLevelEncoder->ConsumedError(GetDevice()->ValidateObject(pipeline))) {

View File

@ -43,6 +43,9 @@ namespace dawn_native {
int32_t baseVertex,
uint32_t firstInstance);
void DrawIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset);
void DrawIndexedIndirect(BufferBase* indirectBuffer, uint64_t indirectOffset);
void SetPipeline(RenderPipelineBase* pipeline);
void SetStencilReference(uint32_t reference);

View File

@ -684,6 +684,17 @@ namespace dawn_native { namespace d3d12 {
commandList->Dispatch(dispatch->x, dispatch->y, dispatch->z);
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
ComPtr<ID3D12CommandSignature> signature =
ToBackend(GetDevice())->GetDispatchIndirectSignature();
commandList->ExecuteIndirect(signature.Get(), 1,
buffer->GetD3D12Resource().Get(),
dispatch->indirectOffset, nullptr, 0);
} break;
case Command::EndComputePass: {
mCommands.NextCommand<EndComputePassCmd>();
return;
@ -818,6 +829,30 @@ namespace dawn_native { namespace d3d12 {
draw->firstInstance);
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline);
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
ComPtr<ID3D12CommandSignature> signature =
ToBackend(GetDevice())->GetDrawIndirectSignature();
commandList->ExecuteIndirect(signature.Get(), 1,
buffer->GetD3D12Resource().Get(),
draw->indirectOffset, nullptr, 0);
} break;
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* draw = mCommands.NextCommand<DrawIndexedIndirectCmd>();
FlushSetVertexBuffers(commandList, &vertexBuffersInfo, lastPipeline);
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
ComPtr<ID3D12CommandSignature> signature =
ToBackend(GetDevice())->GetDrawIndexedIndirectSignature();
commandList->ExecuteIndirect(signature.Get(), 1,
buffer->GetD3D12Resource().Get(),
draw->indirectOffset, nullptr, 0);
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
const char* label = mCommands.NextData<char>(cmd->length + 1);

View File

@ -68,6 +68,34 @@ namespace dawn_native { namespace d3d12 {
mResourceAllocator = std::make_unique<ResourceAllocator>(this);
NextSerial();
// Initialize indirect commands
D3D12_INDIRECT_ARGUMENT_DESC argumentDesc = {};
argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DISPATCH;
D3D12_COMMAND_SIGNATURE_DESC programDesc = {};
programDesc.ByteStride = 3 * sizeof(uint32_t);
programDesc.NumArgumentDescs = 1;
programDesc.pArgumentDescs = &argumentDesc;
ToBackend(GetDevice())
->GetD3D12Device()
->CreateCommandSignature(&programDesc, NULL, IID_PPV_ARGS(&mDispatchIndirectSignature));
argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
programDesc.ByteStride = 4 * sizeof(uint32_t);
ToBackend(GetDevice())
->GetD3D12Device()
->CreateCommandSignature(&programDesc, NULL, IID_PPV_ARGS(&mDrawIndirectSignature));
argumentDesc.Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
programDesc.ByteStride = 5 * sizeof(uint32_t);
ToBackend(GetDevice())
->GetD3D12Device()
->CreateCommandSignature(&programDesc, NULL,
IID_PPV_ARGS(&mDrawIndexedIndirectSignature));
}
Device::~Device() {
@ -101,6 +129,18 @@ namespace dawn_native { namespace d3d12 {
return mCommandQueue;
}
ComPtr<ID3D12CommandSignature> Device::GetDispatchIndirectSignature() const {
return mDispatchIndirectSignature;
}
ComPtr<ID3D12CommandSignature> Device::GetDrawIndirectSignature() const {
return mDrawIndirectSignature;
}
ComPtr<ID3D12CommandSignature> Device::GetDrawIndexedIndirectSignature() const {
return mDrawIndexedIndirectSignature;
}
DescriptorHeapAllocator* Device::GetDescriptorHeapAllocator() const {
return mDescriptorHeapAllocator.get();
}

View File

@ -51,6 +51,10 @@ namespace dawn_native { namespace d3d12 {
ComPtr<ID3D12Device> GetD3D12Device() const;
ComPtr<ID3D12CommandQueue> GetCommandQueue() const;
ComPtr<ID3D12CommandSignature> GetDispatchIndirectSignature() const;
ComPtr<ID3D12CommandSignature> GetDrawIndirectSignature() const;
ComPtr<ID3D12CommandSignature> GetDrawIndexedIndirectSignature() const;
DescriptorHeapAllocator* GetDescriptorHeapAllocator() const;
MapRequestTracker* GetMapRequestTracker() const;
ResourceAllocator* GetResourceAllocator() const;
@ -107,6 +111,10 @@ namespace dawn_native { namespace d3d12 {
ComPtr<ID3D12Device> mD3d12Device;
ComPtr<ID3D12CommandQueue> mCommandQueue;
ComPtr<ID3D12CommandSignature> mDispatchIndirectSignature;
ComPtr<ID3D12CommandSignature> mDrawIndirectSignature;
ComPtr<ID3D12CommandSignature> mDrawIndexedIndirectSignature;
struct PendingCommandList {
ComPtr<ID3D12GraphicsCommandList> commandList;
bool open = false;

View File

@ -645,6 +645,17 @@ namespace dawn_native { namespace metal {
threadsPerThreadgroup:lastPipeline->GetLocalWorkGroupSize()];
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
Buffer* buffer = ToBackend(dispatch->indirectBuffer.Get());
id<MTLBuffer> indirectBuffer = buffer->GetMTLBuffer();
[encoder dispatchThreadgroupsWithIndirectBuffer:indirectBuffer
indirectBufferOffset:dispatch->indirectOffset
threadsPerThreadgroup:lastPipeline
->GetLocalWorkGroupSize()];
} break;
case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>();
lastPipeline = ToBackend(cmd->pipeline).Get();
@ -817,6 +828,29 @@ namespace dawn_native { namespace metal {
}
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
id<MTLBuffer> indirectBuffer = buffer->GetMTLBuffer();
[encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology()
indirectBuffer:indirectBuffer
indirectBufferOffset:draw->indirectOffset];
} break;
case Command::DrawIndexedIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
Buffer* buffer = ToBackend(draw->indirectBuffer.Get());
id<MTLBuffer> indirectBuffer = buffer->GetMTLBuffer();
[encoder drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology()
indexType:lastPipeline->GetMTLIndexType()
indexBuffer:indexBuffer
indexBufferOffset:indexBufferBaseOffset
indirectBuffer:indirectBuffer
indirectBufferOffset:draw->indirectOffset];
} break;
case Command::InsertDebugMarker: {
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();
auto label = mCommands.NextData<char>(cmd->length + 1);

View File

@ -499,6 +499,19 @@ namespace dawn_native { namespace opengl {
glMemoryBarrier(GL_ALL_BARRIER_BITS);
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
uint64_t indirectBufferOffset = dispatch->indirectOffset;
Buffer* indirectBuffer = ToBackend(dispatch->indirectBuffer.Get());
glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, indirectBuffer->GetHandle());
glDispatchComputeIndirect(
reinterpret_cast<GLintptr>(static_cast<intptr_t>(indirectBufferOffset)));
// TODO(cwallez@chromium.org): add barriers to the API
glMemoryBarrier(GL_ALL_BARRIER_BITS);
} break;
case Command::SetComputePipeline: {
SetComputePipelineCmd* cmd = mCommands.NextCommand<SetComputePipelineCmd>();
lastPipeline = ToBackend(cmd->pipeline).Get();
@ -706,6 +719,36 @@ namespace dawn_native { namespace opengl {
}
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
inputBuffers.Apply();
uint64_t indirectBufferOffset = draw->indirectOffset;
Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuffer->GetHandle());
glDrawArraysIndirect(
lastPipeline->GetGLPrimitiveTopology(),
reinterpret_cast<void*>(static_cast<intptr_t>(indirectBufferOffset)));
} break;
case Command::DrawIndexedIndirect: {
DrawIndexedIndirectCmd* draw = mCommands.NextCommand<DrawIndexedIndirectCmd>();
inputBuffers.Apply();
dawn::IndexFormat indexFormat =
lastPipeline->GetVertexInputDescriptor()->indexFormat;
GLenum formatType = IndexFormatType(indexFormat);
uint64_t indirectBufferOffset = draw->indirectOffset;
Buffer* indirectBuffer = ToBackend(draw->indirectBuffer.Get());
glBindBuffer(GL_DRAW_INDIRECT_BUFFER, indirectBuffer->GetHandle());
glDrawElementsIndirect(
lastPipeline->GetGLPrimitiveTopology(), formatType,
reinterpret_cast<void*>(static_cast<intptr_t>(indirectBufferOffset)));
} break;
case Command::InsertDebugMarker:
case Command::PopDebugGroup:
case Command::PushDebugGroup: {

View File

@ -425,6 +425,16 @@ namespace dawn_native { namespace vulkan {
device->fn.CmdDispatch(commands, dispatch->x, dispatch->y, dispatch->z);
} break;
case Command::DispatchIndirect: {
DispatchIndirectCmd* Dispatch = mCommands.NextCommand<DispatchIndirectCmd>();
VkBuffer indirectBuffer = ToBackend(Dispatch->indirectBuffer)->GetHandle();
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_COMPUTE);
device->fn.CmdDispatchIndirect(
commands, indirectBuffer,
static_cast<VkDeviceSize>(Dispatch->indirectOffset));
} break;
case Command::SetBindGroup: {
SetBindGroupCmd* cmd = mCommands.NextCommand<SetBindGroupCmd>();
VkDescriptorSet set = ToBackend(cmd->group.Get())->GetHandle();
@ -521,6 +531,26 @@ namespace dawn_native { namespace vulkan {
draw->firstInstance);
} break;
case Command::DrawIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle();
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
device->fn.CmdDrawIndirect(commands, indirectBuffer,
static_cast<VkDeviceSize>(draw->indirectOffset), 1,
0);
} break;
case Command::DrawIndexedIndirect: {
DrawIndirectCmd* draw = mCommands.NextCommand<DrawIndirectCmd>();
VkBuffer indirectBuffer = ToBackend(draw->indirectBuffer)->GetHandle();
descriptorSets.Flush(device, commands, VK_PIPELINE_BIND_POINT_GRAPHICS);
device->fn.CmdDrawIndexedIndirect(
commands, indirectBuffer, static_cast<VkDeviceSize>(draw->indirectOffset),
1, 0);
} break;
case Command::InsertDebugMarker: {
if (device->GetDeviceInfo().debugMarker) {
InsertDebugMarkerCmd* cmd = mCommands.NextCommand<InsertDebugMarkerCmd>();

View 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);

View 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);

View 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);

View File

@ -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);
}

View 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);
}