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:
Idan Raiter 2019-06-10 20:56:27 +00:00 committed by Commit Bot service account
parent 9d4d3eac14
commit 05f7ad5ac8
21 changed files with 1002 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,18 @@ 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(static_cast<GLintptr>(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 +718,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);
}