Skip to content

Commit 1f29df7

Browse files
committed
Add test for D3D12 predication
1 parent fcf298c commit 1f29df7

File tree

5 files changed

+253
-0
lines changed

5 files changed

+253
-0
lines changed

util/test/demos/d3d12/d3d12_helpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ COM_SMARTPTR(ID3D12CommandQueueDownlevel);
8383
COM_SMARTPTR(ID3D12StateObject);
8484
COM_SMARTPTR(ID3D12StateObjectProperties);
8585

86+
COM_SMARTPTR(ID3D12QueryHeap);
87+
8688
struct D3D12GraphicsTest;
8789

8890
class D3D12PSOCreator
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/******************************************************************************
2+
* The MIT License (MIT)
3+
*
4+
* Copyright (c) 2026 Baldur Karlsson
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in
14+
* all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22+
* THE SOFTWARE.
23+
******************************************************************************/
24+
25+
#include "d3d12_test.h"
26+
27+
RD_TEST(D3D12_Predication, D3D12GraphicsTest)
28+
{
29+
static constexpr const char *Description =
30+
"Tests predication in D3D12 with queries both in and out of the capture";
31+
32+
int main()
33+
{
34+
// initialise, create window, create device, etc
35+
if(!Init())
36+
return 3;
37+
38+
ID3DBlobPtr vsblob = Compile(D3DDefaultVertex, "main", "vs_4_0");
39+
ID3DBlobPtr psblob = Compile(D3DDefaultPixel, "main", "ps_4_0");
40+
41+
D3D12_QUERY_HEAP_DESC desc = {};
42+
desc.Count = 4096;
43+
desc.Type = D3D12_QUERY_HEAP_TYPE_OCCLUSION;
44+
45+
ID3D12QueryHeapPtr qh;
46+
dev->CreateQueryHeap(&desc, __uuidof(ID3D12QueryHeap), (void **)&qh);
47+
48+
ID3D12ResourcePtr queryData = MakeBuffer().Size(sizeof(uint64_t) * (desc.Count + 1) * 3);
49+
50+
uint64_t val = 1;
51+
SetBufferData(queryData, D3D12_RESOURCE_STATE_COMMON, (byte *)&val, sizeof(val));
52+
53+
ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri);
54+
55+
ID3D12RootSignaturePtr sig = MakeSig({});
56+
57+
ID3D12PipelineStatePtr pso = MakePSO().RootSig(sig).InputLayout().VS(vsblob).PS(psblob);
58+
59+
ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
60+
61+
ID3D12ResourcePtr rtvtex = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, 4, 4)
62+
.RTV()
63+
.InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET);
64+
65+
rtvtex->SetName(L"rtvtex");
66+
67+
ID3D12ResourcePtr rtvMStex = MakeTexture(DXGI_FORMAT_R16G16B16A16_FLOAT, 4, 4)
68+
.RTV()
69+
.Multisampled(4)
70+
.InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET);
71+
72+
rtvMStex->SetName(L"rtvMStex");
73+
74+
ID3D12ResourcePtr dsvMStex = MakeTexture(DXGI_FORMAT_D32_FLOAT_S8X24_UINT, 4, 4)
75+
.DSV()
76+
.NoSRV()
77+
.Multisampled(4)
78+
.InitialState(D3D12_RESOURCE_STATE_COMMON);
79+
80+
dsvMStex->SetName(L"dsvMStex");
81+
82+
{
83+
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
84+
Reset(cmd);
85+
ClearRenderTargetView(cmd, MakeRTV(rtvtex).CreateCPU(1), {0.2f, 0.2f, 0.2f, 1.0f});
86+
ClearRenderTargetView(cmd, MakeRTV(rtvMStex).CreateCPU(2), {0.2f, 0.2f, 0.2f, 1.0f});
87+
88+
ResourceBarrier(cmd, dsvMStex, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_DEPTH_WRITE);
89+
90+
ClearDepthStencilView(cmd, dsvMStex, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, 0.2f,
91+
0x55);
92+
93+
cmd->Close();
94+
95+
Submit({cmd});
96+
}
97+
98+
while(Running())
99+
{
100+
ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer();
101+
102+
Reset(cmd);
103+
104+
MakeRTV(rtvtex).CreateCPU(1);
105+
D3D12_CPU_DESCRIPTOR_HANDLE rtvMS = MakeRTV(rtvMStex).CreateCPU(2);
106+
MakeDSV(dsvMStex).CreateCPU(0);
107+
108+
ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
109+
110+
D3D12_CPU_DESCRIPTOR_HANDLE rtv =
111+
MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(0);
112+
113+
ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f});
114+
115+
cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
116+
117+
IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0);
118+
cmd->SetPipelineState(pso);
119+
cmd->SetGraphicsRootSignature(sig);
120+
121+
float w = (float)screenWidth;
122+
float h = (float)screenHeight;
123+
124+
w /= 2.0f;
125+
h /= 2.0f;
126+
127+
RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight});
128+
129+
OMSetRenderTargets(cmd, {rtvMS}, MakeDSV(dsvMStex).CreateCPU(0));
130+
OMSetRenderTargets(cmd, {rtv}, {});
131+
132+
int baseIdx = (curFrame % desc.Count);
133+
int prevIdx = ((curFrame - 1 + desc.Count) % desc.Count);
134+
int destIdx = 1 + baseIdx;
135+
136+
cmd->BeginQuery(qh, D3D12_QUERY_TYPE_OCCLUSION, baseIdx);
137+
138+
RSSetViewport(cmd, {0.0f, 0.0f, w, h, 0.0f, 1.0f});
139+
cmd->DrawInstanced(3, 1, 0, 0);
140+
141+
cmd->EndQuery(qh, D3D12_QUERY_TYPE_OCCLUSION, baseIdx);
142+
143+
ResourceBarrier(cmd, queryData, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
144+
145+
cmd->ResolveQueryData(qh, D3D12_QUERY_TYPE_OCCLUSION, baseIdx, 1, queryData,
146+
sizeof(uint64_t) * destIdx * 3 + 8);
147+
148+
if(curFrame > 1)
149+
cmd->ResolveQueryData(qh, D3D12_QUERY_TYPE_OCCLUSION, prevIdx, 1, queryData,
150+
sizeof(uint64_t) * destIdx * 3 + 0);
151+
152+
ResourceBarrier(cmd, queryData, D3D12_RESOURCE_STATE_COPY_DEST,
153+
D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT);
154+
155+
cmd->SetPredication(queryData, 0, D3D12_PREDICATION_OP_EQUAL_ZERO);
156+
157+
RSSetViewport(cmd, {w, 0.0f, w, h, 0.0f, 1.0f});
158+
cmd->DrawInstanced(3, 1, 0, 0);
159+
160+
cmd->SetPredication(queryData, sizeof(uint64_t) * destIdx * 3 + 8,
161+
D3D12_PREDICATION_OP_EQUAL_ZERO);
162+
163+
RSSetViewport(cmd, {0.0f, h, w, h, 0.0f, 1.0f});
164+
cmd->DrawInstanced(3, 1, 0, 0);
165+
166+
cmd->SetPredication(queryData, sizeof(uint64_t) * destIdx * 3 + 0,
167+
D3D12_PREDICATION_OP_EQUAL_ZERO);
168+
169+
RSSetViewport(cmd, {w, h, w, h, 0.0f, 1.0f});
170+
cmd->DrawInstanced(3, 1, 0, 0);
171+
172+
cmd->SetPredication(queryData, sizeof(uint64_t) * destIdx * 3 + 16,
173+
D3D12_PREDICATION_OP_EQUAL_ZERO);
174+
175+
RSSetViewport(cmd, {w * 0.5f, h * 0.5f, w, h, 0.0f, 1.0f});
176+
cmd->DrawInstanced(3, 1, 0, 0);
177+
178+
FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET);
179+
180+
cmd->Close();
181+
182+
Submit({cmd});
183+
184+
Present();
185+
}
186+
187+
return 0;
188+
}
189+
};
190+
191+
REGISTER_TEST();

util/test/demos/demos.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@
218218
<ClCompile Include="d3d12\d3d12_overlay_test.cpp" />
219219
<ClCompile Include="d3d12\d3d12_parameter_zoo.cpp" />
220220
<ClCompile Include="d3d12\d3d12_pixel_history.cpp" />
221+
<ClCompile Include="d3d12\d3d12_predication.cpp" />
221222
<ClCompile Include="d3d12\d3d12_primitiveid.cpp" />
222223
<ClCompile Include="d3d12\d3d12_reflection_zoo.cpp" />
223224
<ClCompile Include="d3d12\d3d12_rendertarget_binds.cpp" />

util/test/demos/demos.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,9 @@
754754
<ClCompile Include="d3d11\d3d11_annotations.cpp">
755755
<Filter>D3D11\demos</Filter>
756756
</ClCompile>
757+
<ClCompile Include="d3d12\d3d12_predication.cpp">
758+
<Filter>D3D12\demos</Filter>
759+
</ClCompile>
757760
</ItemGroup>
758761
<ItemGroup>
759762
<Filter Include="D3D11">
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import renderdoc as rd
2+
import rdtest
3+
4+
5+
class D3D12_Predication(rdtest.TestCase):
6+
demos_test_name = 'D3D12_Predication'
7+
8+
def check_capture(self):
9+
a = self.find_action("Draw")
10+
b = self.find_action("Draw", a.eventId+1)
11+
c = self.find_action("Draw", b.eventId+1)
12+
d = self.find_action("Draw", c.eventId+1)
13+
e = self.find_action("Draw", d.eventId+1)
14+
15+
viewport_array = lambda view: (view.x, view.y, view.width, view.height)
16+
17+
self.controller.SetFrameEvent(a.eventId, False)
18+
pipe = self.controller.GetPipelineState()
19+
self.check_triangle(vp=viewport_array(pipe.GetViewport(0)))
20+
21+
rdtest.log.success("Non-predicated triangle is correct")
22+
23+
self.check(self.controller.GetD3D12PipelineState().predication.resourceId == rd.ResourceId())
24+
25+
self.controller.SetFrameEvent(b.eventId, False)
26+
pipe = self.controller.GetPipelineState()
27+
self.check_triangle(vp=viewport_array(pipe.GetViewport(0)))
28+
29+
self.check(self.controller.GetD3D12PipelineState().predication.resourceId != rd.ResourceId())
30+
self.check(self.controller.GetD3D12PipelineState().predication.offset == 0)
31+
32+
rdtest.log.success("Fixed data predicated triangle is correct")
33+
34+
self.controller.SetFrameEvent(c.eventId, False)
35+
pipe = self.controller.GetPipelineState()
36+
self.check_triangle(vp=viewport_array(pipe.GetViewport(0)))
37+
38+
self.check(self.controller.GetD3D12PipelineState().predication.resourceId != rd.ResourceId())
39+
self.check(self.controller.GetD3D12PipelineState().predication.offset > 0)
40+
41+
rdtest.log.success("Current frame query-predicated triangle is correct")
42+
43+
self.controller.SetFrameEvent(d.eventId, False)
44+
pipe = self.controller.GetPipelineState()
45+
self.check_triangle(vp=viewport_array(pipe.GetViewport(0)))
46+
47+
rdtest.log.success("Previous frame query-predicated triangle is correct")
48+
49+
self.controller.SetFrameEvent(e.eventId, False)
50+
pipe = self.controller.GetPipelineState()
51+
self.check_pixel_value(pipe.GetOutputTargets()[0].resource, 200, 150, [0.2, 0.2, 0.2, 1.0])
52+
53+
self.check(self.controller.GetD3D12PipelineState().predication.resourceId != rd.ResourceId())
54+
self.check(self.controller.GetD3D12PipelineState().predication.offset > 0)
55+
56+
rdtest.log.success("Failing predicated triangle is correct")

0 commit comments

Comments
 (0)