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:
parent
9d4d3eac14
commit
05f7ad5ac8
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,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: {
|
||||
|
|
|
@ -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