From 20578f46a6395e5a952ce125d11e4885a26fd8e4 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Mon, 29 Jan 2024 15:56:05 +0000 Subject: [PATCH] Extend D3D12 ExecuteIndirect test to do multiple draws per Execute The argument buffers are used fully used with no spare bytes The python test iterates over every draw to check the replay has a valid output target count This verifies the replay did not crash when replaying ExecuteIndirect with multiple draws and an argument buffer with no spare bytes --- .../demos/d3d12/d3d12_execute_indirect.cpp | 48 ++++++++++++++++++- .../tests/D3D12/D3D12_Execute_Indirect.py | 26 +++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_execute_indirect.cpp b/util/test/demos/d3d12/d3d12_execute_indirect.cpp index 4c05eb3d5..c07c0e52d 100644 --- a/util/test/demos/d3d12/d3d12_execute_indirect.cpp +++ b/util/test/demos/d3d12/d3d12_execute_indirect.cpp @@ -269,7 +269,7 @@ void main(uint3 gid : SV_GroupID) ID3D12CommandSignaturePtr patchArgSig = MakeCommandSig(patchsig, {vbArg(0), cbvArg(0), srvArg(1), uavArg(2), drawArg()}); - struct + struct PatchArgs { D3D12_VERTEX_BUFFER_VIEW vb; D3D12_GPU_VIRTUAL_ADDRESS cbv; @@ -322,6 +322,16 @@ void main(uint3 gid : SV_GroupID) ID3D12PipelineStatePtr comppso = MakePSO().RootSig(compsig).CS(csblob); ID3D12ResourcePtr customvbargs = MakeBuffer().Size(1024 * 1024 * 4); + const uint32_t countDrawsInFullBuffer = 3; + ID3D12ResourcePtr fullargsDrawBuf = + MakeBuffer().Size(countDrawsInFullBuffer * sizeof(D3D12_INDIRECT_ARGUMENT_DESC)); + + PatchArgs fullargsStateDraw[countDrawsInFullBuffer]; + for(uint32_t i = 0; i < countDrawsInFullBuffer; ++i) + fullargsStateDraw[i] = patchargs; + + ID3D12ResourcePtr fullargsStateDrawBuf = + MakeBuffer().Upload().Size(countDrawsInFullBuffer * sizeof(patchargs)).Data(fullargsStateDraw); ID3D12RootSignaturePtr plainsig = MakeSig({}); ID3D12PipelineStatePtr plainpso = @@ -448,6 +458,42 @@ void main(uint3 gid : SV_GroupID) } popMarker(cmd); + pushMarker(cmd, "Full Arg Buffer: Pure Draw"); + { + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + D3D12_VERTEX_BUFFER_VIEW view; + view.BufferLocation = customvbargs->GetGPUVirtualAddress() + 4096 * (sizeof(Vec4f)) + 256; + view.StrideInBytes = sizeof(A2V); + view.SizeInBytes = sizeof(A2V) * 1120; + cmd->IASetVertexBuffers(0, 1, &view); + + cmd->SetPipelineState(plainpso); + cmd->SetGraphicsRootSignature(plainsig); + + RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); + + OMSetRenderTargets(cmd, {rtv}, {}); + + cmd->ExecuteIndirect(plainArgSig, countDrawsInFullBuffer, fullargsDrawBuf, 0, NULL, 0); + } + popMarker(cmd); + + pushMarker(cmd, "Full Arg Buffer: State + Draw"); + { + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + cmd->SetPipelineState(patchpso); + cmd->SetGraphicsRootSignature(patchsig); + + RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); + + OMSetRenderTargets(cmd, {rtv}, {}); + cmd->ExecuteIndirect(patchArgSig, countDrawsInFullBuffer, fullargsStateDrawBuf, 0, NULL, 0); + } + popMarker(cmd); + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); cmd->Close(); diff --git a/util/test/tests/D3D12/D3D12_Execute_Indirect.py b/util/test/tests/D3D12/D3D12_Execute_Indirect.py index 4c3f9c26c..af941c71b 100644 --- a/util/test/tests/D3D12/D3D12_Execute_Indirect.py +++ b/util/test/tests/D3D12/D3D12_Execute_Indirect.py @@ -172,4 +172,28 @@ class D3D12_Execute_Indirect(rdtest.TestCase): raise rdtest.TestFailureException( "Detected an exploded polygon with {} selected".format(action.GetName(sdfile))) - rdtest.log.success("Pass {} of unordered draw was correct") \ No newline at end of file + rdtest.log.success(f"Pass {passNum} of unordered draw was correct") + + # This does not draw anything but its argument buffer is fully used with no spare bytes + # Iterate over every draw and check the replay has valid output target + action = self.find_action("Full Arg Buffer") + action = self.find_action("IndirectDraw", action.eventId) + for drawNum in range(3): + self.controller.SetFrameEvent(action.eventId + drawNum, False) + pipe = self.controller.GetPipelineState() + if len(pipe.GetOutputTargets()) != 1: + raise rdtest.TestFailureException( + f"With event {action.eventId + drawNum} selected we should have one output target but there is {len(pipe.GetOutputTargets())}") + rdtest.log.success("Fully used argument buffer with multiple draws replayed") + + # This does not draw anything but its argument buffer is fully used with no spare bytes + # Iterate over every draw and check the replay has valid output target + action = self.find_action("Full Arg Buffer: State + Draw") + action = self.find_action("IndirectDraw", action.eventId) + for drawNum in range(3): + self.controller.SetFrameEvent(action.eventId + drawNum, False) + pipe = self.controller.GetPipelineState() + if len(pipe.GetOutputTargets()) != 1: + raise rdtest.TestFailureException( + f"With event {action.eventId + drawNum} selected we should have one output target but there is {len(pipe.GetOutputTargets())}") + rdtest.log.success("Fully used argument buffer with multiple states + draws replayed")