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:
parent
de1ac9fd7b
commit
7eb6be186b
5
BUILD.gn
5
BUILD.gn
|
@ -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",
|
||||
|
|
24
dawn.json
24
dawn.json
|
@ -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": [
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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))) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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>();
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue