From 4f7d9c7cb2e55dfc6aa4381c67dd7844b17ebe56 Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Mon, 1 Jun 2026 18:26:26 +0100 Subject: [PATCH] Updates to D3D12_Execute_Indirect testing Check overlays produce some output for each draw within the different EI except the non-determistic EI. --- .../demos/d3d12/d3d12_execute_indirect.cpp | 89 +++++++++++-- .../tests/D3D12/D3D12_Execute_Indirect.py | 122 +++++++++++++----- 2 files changed, 167 insertions(+), 44 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_execute_indirect.cpp b/util/test/demos/d3d12/d3d12_execute_indirect.cpp index bb4ff8bc7..74dda582e 100644 --- a/util/test/demos/d3d12/d3d12_execute_indirect.cpp +++ b/util/test/demos/d3d12/d3d12_execute_indirect.cpp @@ -252,6 +252,20 @@ void main(uint3 gid : SV_GroupID) )EOSHADER"; + float sqSize; + Vec2f viewXY; + + void NextTest() + { + viewXY.x += sqSize; + + if(viewXY.x + sqSize >= (float)screenWidth) + { + viewXY.x = 0.0f; + viewXY.y += sqSize; + } + } + int main() { // initialise, create window, create device, etc @@ -274,16 +288,34 @@ void main(uint3 gid : SV_GroupID) Vec4f col; }; - const A2V tri[9] = { + const A2V tri[3] = { {Vec4f(-0.5f, -0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f)}, {Vec4f(0.0f, 0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f)}, {Vec4f(0.5f, -0.5f, 0.0f), Vec4f(0.0f, 1.0f, 0.0f, 1.0f)}, }; + A2V tris[6 * 3]; + + for(int t = 0; t < 6; t++) + { + float r = 1.0f; + float x = (float(t) / 6.0f) * 350.0f; + float ang0 = x * (3.141592654f / 180.0f); + float ang1 = (x + 45.0f) * (3.141592654f / 180.0f); + + tris[t * 3 + 0].pos = Vec4f(0.0f, 0.0f); + tris[t * 3 + 0].col = Vec4f(x / 300.0f, 1.0f, 0.0f, 1.0f); + tris[t * 3 + 1].pos = Vec4f(r * sinf(ang0), r * cosf(ang0)); + tris[t * 3 + 1].col = Vec4f(x / 300.0f, 1.0f, 0.0f, 1.0f); + tris[t * 3 + 2].pos = Vec4f(r * sinf(ang1), r * cosf(ang1)); + tris[t * 3 + 2].col = Vec4f(x / 300.0f, 1.0f, 0.0f, 1.0f); + } + float checkdata[1024] = {}; checkdata[64 + 7] = 1.234f; - ID3D12ResourcePtr vb = MakeBuffer().Data(tri); + ID3D12ResourcePtr vb = MakeBuffer().Data(tris); + ID3D12ResourcePtr trivb = MakeBuffer().Data(tri); ID3D12ResourcePtr cbv = MakeBuffer().Data(checkdata); ID3D12ResourcePtr srv = MakeBuffer().Data(checkdata); ID3D12ResourcePtr uav = MakeBuffer().UAV().Data(checkdata); @@ -311,7 +343,7 @@ void main(uint3 gid : SV_GroupID) D3D12_DRAW_ARGUMENTS draw; } patchargs; - patchargs.vb.BufferLocation = vb->GetGPUVirtualAddress(); + patchargs.vb.BufferLocation = trivb->GetGPUVirtualAddress(); patchargs.vb.SizeInBytes = sizeof(tri); patchargs.vb.StrideInBytes = sizeof(A2V); patchargs.cbv = cbv->GetGPUVirtualAddress() + 256; @@ -340,6 +372,7 @@ void main(uint3 gid : SV_GroupID) ptr += sizeof(float); memcpy(ptr, &patchargs.constData2, sizeof(float)); ptr += sizeof(float); + size_t drawoffset = (size_t)ptr - (size_t)patchArgsData.data(); memcpy(ptr, &patchargs.draw, sizeof(D3D12_DRAW_ARGUMENTS)); ptr += sizeof(D3D12_DRAW_ARGUMENTS); @@ -360,12 +393,12 @@ void main(uint3 gid : SV_GroupID) } patchargs2; patchargs2.vb.BufferLocation = vb->GetGPUVirtualAddress(); - patchargs2.vb.SizeInBytes = sizeof(tri); + patchargs2.vb.SizeInBytes = sizeof(tris); patchargs2.vb.StrideInBytes = sizeof(A2V); patchargs2.draw.VertexCountPerInstance = 3; patchargs2.draw.InstanceCount = 1; patchargs2.draw.StartInstanceLocation = 0; - patchargs2.draw.StartVertexLocation = 0; + patchargs2.draw.StartVertexLocation = 3; std::vector patchArgsData2; patchArgsData2.resize(sizeof(PatchArgs2)); @@ -390,7 +423,7 @@ void main(uint3 gid : SV_GroupID) patchargs3.draw.VertexCountPerInstance = 3; patchargs3.draw.InstanceCount = 1024; patchargs3.draw.StartInstanceLocation = 0; - patchargs3.draw.StartVertexLocation = 0; + patchargs3.draw.StartVertexLocation = 6; std::vector patchArgsData3; patchArgsData3.resize(sizeof(PatchArgs3)); @@ -446,6 +479,17 @@ void main(uint3 gid : SV_GroupID) for(uint32_t i = 0; i < countDrawsInFullBuffer; ++i) { memcpy(fullargsPtr, patchArgsData.data(), patchArgsDataSize); + D3D12_VERTEX_BUFFER_VIEW view; + view.BufferLocation = vb->GetGPUVirtualAddress(); + view.SizeInBytes = sizeof(tris); + view.StrideInBytes = sizeof(A2V); + memcpy(fullargsPtr, &view, sizeof(D3D12_VERTEX_BUFFER_VIEW)); + D3D12_DRAW_ARGUMENTS draw; + draw.VertexCountPerInstance = 3; + draw.InstanceCount = 1; + draw.StartInstanceLocation = 0; + draw.StartVertexLocation = (9 + i * 3) % 18; + memcpy(fullargsPtr + drawoffset, &draw, sizeof(D3D12_DRAW_ARGUMENTS)); fullargsPtr += patchArgsDataSize; } @@ -469,8 +513,13 @@ void main(uint3 gid : SV_GroupID) float baseConstData[4] = {10.0f, 9.0f, 8.0f, 7.0f}; + sqSize = float(screenHeight) / 4.0f; + while(Running()) { + viewXY.x = 0.0f; + viewXY.y = 0.0f; + std::vector cmds; ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); @@ -490,11 +539,10 @@ void main(uint3 gid : SV_GroupID) D3D12_CPU_DESCRIPTOR_HANDLE rtv = MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0); + ClearRenderTargetView(cmd, rtv, {0.0f, 0.0f, 0.0f, 1.0f}); pushMarker(cmd, "Multiple draws"); for(int i = 0; i < 8; i++) { - ClearRenderTargetView(cmd, rtv, {0.0f, 0.0f, 0.0f, 1.0f}); - cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); cmd->SetPipelineState(patchpso); @@ -502,7 +550,7 @@ void main(uint3 gid : SV_GroupID) cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); cmd->SetGraphicsRootDescriptorTable(5, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); @@ -510,6 +558,7 @@ void main(uint3 gid : SV_GroupID) cmd->SetGraphicsRoot32BitConstants(3, 4, baseConstData, 0); cmd->ExecuteIndirect(patchArgSig, 1, patchArgBuf, 0, NULL, 0); + NextTest(); } popMarker(cmd); @@ -532,7 +581,7 @@ void main(uint3 gid : SV_GroupID) cmd->SetGraphicsRootShaderResourceView(1, srv->GetGPUVirtualAddress() + 256); cmd->SetGraphicsRootUnorderedAccessView(2, uav->GetGPUVirtualAddress() + 256); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); @@ -540,6 +589,7 @@ void main(uint3 gid : SV_GroupID) cmd->SetGraphicsRoot32BitConstants(3, 4, baseConstData, 0); cmd->ExecuteIndirect(patchArgSig2, 1, patchArgBuf2, 0, NULL, 0); + NextTest(); } popMarker(cmd); @@ -608,13 +658,14 @@ void main(uint3 gid : SV_GroupID) cmd->SetPipelineState(plainpso); cmd->SetGraphicsRootSignature(plainsig); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); cmd->ExecuteIndirect(plainArgSig, 8, customvbargs, 4096 * (sizeof(Vec4f)) + sizeof(Vec4u), NULL, 0); + NextTest(); } popMarker(cmd); @@ -630,12 +681,13 @@ void main(uint3 gid : SV_GroupID) cmd->SetPipelineState(plainpso); cmd->SetGraphicsRootSignature(plainsig); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); cmd->ExecuteIndirect(plainArgSig, countDrawsInFullBuffer, fullargsDrawBuf, 0, NULL, 0); + NextTest(); } popMarker(cmd); @@ -648,12 +700,13 @@ void main(uint3 gid : SV_GroupID) cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); cmd->SetGraphicsRootDescriptorTable(5, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); cmd->SetGraphicsRoot32BitConstants(3, 4, baseConstData, 0); cmd->ExecuteIndirect(patchArgSig, countDrawsInFullBuffer, fullargsStateDrawBuf, 0, NULL, 0); + NextTest(); } popMarker(cmd); @@ -666,8 +719,13 @@ void main(uint3 gid : SV_GroupID) cmd->SetGraphicsRootConstantBufferView(0, cbv->GetGPUVirtualAddress() + 256); cmd->SetGraphicsRootShaderResourceView(1, srv->GetGPUVirtualAddress() + 256); cmd->SetGraphicsRootUnorderedAccessView(2, uav->GetGPUVirtualAddress() + 256); + D3D12_VERTEX_BUFFER_VIEW view; + view.BufferLocation = vb->GetGPUVirtualAddress(); + view.SizeInBytes = sizeof(tris); + view.StrideInBytes = sizeof(A2V); + cmd->IASetVertexBuffers(0, 1, &view); - RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); OMSetRenderTargets(cmd, {rtv}, {}); @@ -675,7 +733,10 @@ void main(uint3 gid : SV_GroupID) cmd->SetGraphicsRoot32BitConstants(3, 4, baseConstData, 0); cmd->ExecuteIndirect(plainArgSig, 1, patchArgBuf3, 0, NULL, 0); + NextTest(); + RSSetViewport(cmd, {viewXY.x, viewXY.y, sqSize, sqSize, 0.0f, 1.0f}); cmd->ExecuteIndirect(plainArgSig, 1, patchArgBuf3, 0, NULL, 0); + NextTest(); } popMarker(cmd); diff --git a/util/test/tests/D3D12/D3D12_Execute_Indirect.py b/util/test/tests/D3D12/D3D12_Execute_Indirect.py index 959351ec3..74c09f86f 100644 --- a/util/test/tests/D3D12/D3D12_Execute_Indirect.py +++ b/util/test/tests/D3D12/D3D12_Execute_Indirect.py @@ -6,17 +6,50 @@ from typing import List class D3D12_Execute_Indirect(rdtest.TestCase): demos_test_name = 'D3D12_Execute_Indirect' - def check_pixel_history_succeeds(self): + def check_overlays(self, eid: int, x: int, y: int): + with rdtest.log.auto_section(f'EID {eid} Checking Overlays at {x}, {y}'): + pipe: rd.PipeState = self.controller.GetPipelineState() + if len(pipe.GetOutputTargets()) == 0: + raise rdtest.TestFailureException("No output targets found") + + col_tex: rd.ResourceId = pipe.GetOutputTargets()[0].resource + + for overlay in rd.DebugOverlay: + if overlay == rd.DebugOverlay.NoOverlay: + continue + if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping: + continue + if overlay == rd.DebugOverlay.ViewportScissor: + continue + tex = rd.TextureDisplay() + tex.resourceId = col_tex + tex.overlay = overlay + tex.subresource.sample = 0 + + out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture) + out.SetTextureDisplay(tex) + out.Display() + overlayTex: rd.ResourceId = out.GetDebugOverlayTexID() + if overlay == rd.DebugOverlay.ClearBeforeDraw: + overlayTex = col_tex + if overlay == rd.DebugOverlay.ClearBeforePass: + overlayTex = col_tex + + picked = self.controller.PickPixel(overlayTex, x, y, rd.Subresource(), rd.CompType.UNorm) + emptyPixel = (0.0, 0.0, 0.0, 0.0) + if picked.floatValue == emptyPixel: + raise rdtest.TestFailureException(f"{overlay.name} overlay is empty") + out.Shutdown() + + def check_pixel_history_succeeds(self, x: int, y: int): pipe: rd.PipeState = self.controller.GetPipelineState() rt = pipe.GetOutputTargets()[0] tex = rt.resource sub = rd.Subresource() - x = 200 - y = 150 modifs: List[rd.PixelModification] = self.controller.PixelHistory(tex, x, y, sub, rt.format.compType) - if len(modifs) == 0: - raise rdtest.TestFailureException("No pixel history found") - rdtest.log.success("Pixel History Worked") + if len(modifs) < 2: + raise rdtest.TestFailureException(f"No pixel history found at ({x}, {y})") + rdtest.log.success(f"Pixel History {x}, {y} Worked") def check_root_consts(self, expected: List[float]): pipe: rd.PipeState = self.controller.GetPipelineState() @@ -41,22 +74,29 @@ class D3D12_Execute_Indirect(rdtest.TestCase): 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())}") - self.check_pixel_history_succeeds() + self.check_pixel_history_succeeds(285, 110) + if drawNum == 0: + self.check_overlays(action.eventId, 285, 110) rdtest.log.success("Draw without Root Signature replayed correctly"); from_eid = self.find_action("Multiple draws").eventId ei_eid = self.find_action("ExecuteIndirect", from_eid).eventId self.controller.SetFrameEvent(ei_eid - 1, False) self.check_root_consts([10.0, 9.0, 8.0, 7.0]) + viewX = 0 + viewY = 0 + sqSize = 300 / 4 + viewW = sqSize + viewH = sqSize for i in range(8): action = self.find_action("IndirectDraw", from_eid) - self.controller.SetFrameEvent(action.eventId, False) + eid = action.eventId + self.controller.SetFrameEvent(eid, False) self.check_root_consts([123.0, 9.0, 8.0, 7.0]) - self.check_triangle(back=[0.0, 0.0, 0.0, 1.0]) - # Should be a green triangle in the centre of the screen on a black background - self.check_triangle(back=[0.0, 0.0, 0.0, 1.0]) + self.check_triangle(back=[0.0, 0.0, 0.0, 1.0], vp=[viewX, viewY, viewW, viewH]) + vsin_ref = { 0: { 'vtx': 0, @@ -101,9 +141,12 @@ class D3D12_Execute_Indirect(rdtest.TestCase): } self.check_mesh_data(postvs_ref, postvs_data) - self.check_pixel_history_succeeds() + x = int(viewX + viewW/2) + y = int(viewY + viewH/2) + self.check_pixel_history_succeeds(x,y) + self.check_overlays(eid, x,y) - from_eid = action.eventId + 1 + from_eid = eid + 1 pipe = self.controller.GetPipelineState() @@ -115,11 +158,18 @@ class D3D12_Execute_Indirect(rdtest.TestCase): self.check(rw[0].descriptor.resource != rd.ResourceId()) self.check(pipe.GetConstantBlock(rd.ShaderStage.Vertex, 0, 0).descriptor.resource != rd.ResourceId()) + viewX += sqSize + if viewX + sqSize >= 400: + viewX = 0 + viewY += sqSize + + viewX = 0 + viewY = 0 action = self.find_action("Post draw") self.controller.SetFrameEvent(action.eventId, False) # triangle should still be visible - self.check_triangle(back=[0.0, 0.0, 0.0, 1.0]) + self.check_triangle(back=[0.0, 0.0, 0.0, 1.0], vp=[viewX, viewY, viewW, viewH]) # but state should be reset pipe = self.controller.GetPipelineState() @@ -134,7 +184,7 @@ class D3D12_Execute_Indirect(rdtest.TestCase): rdtest.log.success("State is reset after execute") - self.check_pixel_history_succeeds() + self.check_pixel_history_succeeds(185, 50) action = self.find_action("Post Single dispatch") self.controller.SetFrameEvent(action.eventId, False) @@ -155,7 +205,7 @@ class D3D12_Execute_Indirect(rdtest.TestCase): rdtest.log.success("Dispatch buffer output is correct") - self.check_pixel_history_succeeds() + self.check_pixel_history_succeeds(185, 50) # The final draw is in indeterminate order because the parameters are defined by a compute shader in # indeterminate order @@ -168,16 +218,16 @@ class D3D12_Execute_Indirect(rdtest.TestCase): action = self.find_action("IndirectDraw", action.eventId) drawPoints = [ - (60, 20), - (210, 20), - (360, 20), + (310, 78), + (338, 78), + (367, 78), - (60, 130), - (360, 130), + (310, 107), + (367, 107), - (60, 240), - (210, 240), - (360, 240), + (310, 135), + (338, 135), + (367, 135), ] sdfile = self.controller.GetStructuredFile() @@ -219,7 +269,7 @@ class D3D12_Execute_Indirect(rdtest.TestCase): raise rdtest.TestFailureException( "Detected an exploded polygon with {} selected".format(action.GetName(sdfile))) - self.check_pixel_history_succeeds() + self.check_pixel_history_succeeds(185, 50) rdtest.log.success(f"Pass {passNum} of unordered draw was correct") @@ -233,7 +283,7 @@ class D3D12_Execute_Indirect(rdtest.TestCase): 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())}") - self.check_pixel_history_succeeds() + self.check_pixel_history_succeeds(185, 50) 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 @@ -241,12 +291,20 @@ class D3D12_Execute_Indirect(rdtest.TestCase): 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, False) + eid = action.eventId + self.controller.SetFrameEvent(eid, 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())}") - self.check_pixel_history_succeeds() + f"With event {eid} selected we should have one output target but there is {len(pipe.GetOutputTargets())}") + x = 100 + y = 210 - drawNum * 20 + self.check_overlays(eid, x, y) + self.check_pixel_history_succeeds(x, 210) + if drawNum > 0: + self.check_pixel_history_succeeds(x, 190) + if drawNum > 1: + self.check_pixel_history_succeeds(x, 170) action = action.next rdtest.log.success("Fully used argument buffer with multiple states + draws replayed") @@ -256,12 +314,16 @@ class D3D12_Execute_Indirect(rdtest.TestCase): if drawNum == 1: action = self.find_action("IndirectDraw", action.eventId+1) + eid = action.eventId self.controller.SetFrameEvent(action.eventId, False) pipe = self.controller.GetPipelineState() if len(pipe.GetOutputTargets()) != 1: raise rdtest.TestFailureException( f"With event {action.eventId} selected we should have one output target but there is {len(pipe.GetOutputTargets())}") - self.check_pixel_history_succeeds() + x = 200 + 80 * drawNum + y = 205 + self.check_pixel_history_succeeds(x, y) + self.check_overlays(eid, x, y) overlay = rd.DebugOverlay.QuadOverdrawPass tex = rd.TextureDisplay()