From 2ace1fe84d62e2b2b5fdd25894dc3186864ee8fe Mon Sep 17 00:00:00 2001 From: Benoit Dumesnil Date: Mon, 14 Mar 2022 20:43:54 +0100 Subject: [PATCH] Add test of D3D12 bindless feedback for shadermodel 6.6. --- .../demos/d3d12/d3d12_descriptor_indexing.cpp | 161 ++++++++++++++++-- util/test/demos/d3d12/d3d12_test.cpp | 8 + util/test/demos/d3d12/d3d12_test.h | 1 + .../tests/D3D12/D3D12_Descriptor_Indexing.py | 93 +++++++++- 4 files changed, 240 insertions(+), 23 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_descriptor_indexing.cpp b/util/test/demos/d3d12/d3d12_descriptor_indexing.cpp index 9871aba5f..d7bdf4936 100644 --- a/util/test/demos/d3d12/d3d12_descriptor_indexing.cpp +++ b/util/test/demos/d3d12/d3d12_descriptor_indexing.cpp @@ -128,6 +128,88 @@ float4 main(v2f IN) : SV_Target0 } } +)EOSHADER"; + + std::string pixel6_6Heap = R"EOSHADER( + +struct v2f +{ + float4 pos : SV_POSITION; + float4 col : COLOR0; + float2 uv : TEXCOORD0; +}; + +struct tex_ref +{ + uint tex; + uint binding; +}; + +struct CBuffer +{ + uint tex_idx; +}; + +float4 main(v2f IN) : SV_Target0 +{ + StructuredBuffer buf = ResourceDescriptorHeap[8]; + if(IN.uv.y < 0.1f) + { + SamplerState s = SamplerDescriptorHeap[0]; + Texture2D fixedtex = ResourceDescriptorHeap[12]; + return fixedtex.Sample(s, IN.uv.xy*5.0f); + } + else + { + float2 uv = IN.uv.xy; + + float4 ret = float4(1,1,1,1); + for(int i=0; i < 100; i++) + { + tex_ref t = buf[i]; + if(t.tex == 100) break; + + if(t.tex == 0) + { + SamplerState s1 = SamplerDescriptorHeap[0]; + SamplerState s2 = SamplerDescriptorHeap[1]; + SamplerState s3 = SamplerDescriptorHeap[2]; + Texture2D tex1 = ResourceDescriptorHeap[t.binding]; + Texture2D tex2 = ResourceDescriptorHeap[t.binding+1]; + Texture2D tex3 = ResourceDescriptorHeap[t.binding+2]; + ret *= tex1.SampleLevel(s1, uv.xy, 0); + ret *= tex2.SampleLevel(s2, uv.xy, 0); + ret *= tex3.SampleLevel(s3, uv.xy, 0); + RWStructuredBuffer uav = ResourceDescriptorHeap[10]; + uav[0] = t.binding; + } + else if(t.tex == 1) + { + SamplerState s1 = SamplerDescriptorHeap[4]; + SamplerState s2 = SamplerDescriptorHeap[5]; + SamplerState s3 = SamplerDescriptorHeap[6]; + Texture2D tex1 = ResourceDescriptorHeap[40+t.binding]; + Texture2D tex2 = ResourceDescriptorHeap[40+t.binding+10]; + ConstantBuffer cbv = ResourceDescriptorHeap[9]; + Texture2D tex3 = ResourceDescriptorHeap[cbv.tex_idx]; + ret *= tex1.SampleLevel(s1, uv.xy, 0); + ret *= tex2.SampleLevel(s2, uv.xy, 0); + ret *= tex3.SampleLevel(s3, uv.xy, 0); + } + else if(t.tex == 2) + { + SamplerState s = SamplerDescriptorHeap[7]; + Texture2D tex = ResourceDescriptorHeap[80+t.binding]; + ret *= tex.SampleLevel(s, uv.xy, 0); + } + + uv *= 1.8f; + } + + return ret; + } +} + )EOSHADER"; int main() @@ -136,9 +218,10 @@ float4 main(v2f IN) : SV_Target0 if(!Init()) return 3; - ID3DBlobPtr vsblob[2]; - ID3DBlobPtr psblob[2]; - ID3DBlobPtr csblob[2]; + bool supportSM66 = m_HighestShaderModel >= D3D_SHADER_MODEL_6_6; + ID3DBlobPtr vsblob[3]; + ID3DBlobPtr psblob[4]; + ID3DBlobPtr csblob[3]; vsblob[0] = Compile(D3DDefaultVertex, "main", "vs_4_0"); psblob[0] = Compile(pixel, "main", "ps_5_1"); csblob[0] = Compile(compute, "main", "cs_5_1"); @@ -147,6 +230,14 @@ float4 main(v2f IN) : SV_Target0 vsblob[1] = Compile(D3DDefaultVertex, "main", "vs_6_0"); psblob[1] = Compile(pixel, "main", "ps_6_0"); csblob[1] = Compile(compute, "main", "cs_6_0"); + if(supportSM66) + { + vsblob[2] = Compile(D3DDefaultVertex, "main", "vs_6_6"); + psblob[2] = Compile(pixel, "main", "ps_6_6"); + csblob[2] = Compile(compute, "main", "cs_6_6"); + + psblob[3] = Compile(pixel6_6Heap, "main", "ps_6_6"); + } } ID3D12ResourcePtr vb = MakeBuffer().Data(DefaultTri); @@ -161,21 +252,33 @@ float4 main(v2f IN) : SV_Target0 staticSamp.AddressU = staticSamp.AddressV = staticSamp.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; staticSamp.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - ID3D12RootSignaturePtr graphicssig = MakeSig( + ID3D12RootSignaturePtr graphicssigs[4]; + graphicssigs[0] = graphicssigs[1] = graphicssigs[2] = MakeSig( { tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 20, 0), tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 150, 0), }, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp); - - ID3D12PipelineStatePtr graphicspso[2]; - ID3D12PipelineStatePtr computepso[2]; + ID3D12PipelineStatePtr graphicspso[4]; + ID3D12PipelineStatePtr computepso[4]; computepso[0] = MakePSO().RootSig(computesig).CS(csblob[0]); - graphicspso[0] = MakePSO().RootSig(graphicssig).InputLayout().VS(vsblob[0]).PS(psblob[0]); + graphicspso[0] = MakePSO().RootSig(graphicssigs[0]).InputLayout().VS(vsblob[0]).PS(psblob[0]); if(m_DXILSupport) { computepso[1] = MakePSO().RootSig(computesig).CS(csblob[1]); - graphicspso[1] = MakePSO().RootSig(graphicssig).InputLayout().VS(vsblob[1]).PS(psblob[1]); + graphicspso[1] = MakePSO().RootSig(graphicssigs[1]).InputLayout().VS(vsblob[1]).PS(psblob[1]); + if(supportSM66) + { + computepso[2] = MakePSO().RootSig(computesig).CS(csblob[2]); + graphicspso[2] = MakePSO().RootSig(graphicssigs[2]).InputLayout().VS(vsblob[2]).PS(psblob[2]); + + computepso[3] = computepso[2]; + graphicssigs[3] = MakeSig({}, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT | + D3D12_ROOT_SIGNATURE_FLAG_SAMPLER_HEAP_DIRECTLY_INDEXED | + D3D12_ROOT_SIGNATURE_FLAG_CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED, + 0, NULL); + graphicspso[3] = MakePSO().RootSig(graphicssigs[3]).InputLayout().VS(vsblob[2]).PS(psblob[3]); + } } ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); @@ -192,6 +295,15 @@ float4 main(v2f IN) : SV_Target0 .InitialState(D3D12_RESOURCE_STATE_COPY_DEST); ID3D12ResourcePtr uploadBuf = MakeBuffer().Size(1024 * 1024).Upload(); + ID3D12ResourcePtr constBuf = MakeBuffer().Size(256).Upload(); + ID3D12ResourcePtr outUAV = MakeBuffer().Size(256).UAV(); + { + byte *mapptr = NULL; + constBuf->Map(0, NULL, (void **)&mapptr); + uint32_t value = 6; + memcpy(mapptr, &value, sizeof(uint32_t)); + constBuf->Unmap(0, NULL); + } { D3D12_PLACED_SUBRESOURCE_FOOTPRINT layout = {}; @@ -252,7 +364,6 @@ float4 main(v2f IN) : SV_Target0 MakeSRV(blacktex).CreateGPU(i); MakeSRV(blacktex).CreateCPU(i); } - ID3D12ResourcePtr structBuf = MakeBuffer().UAV().Size(8192); D3D12_GPU_DESCRIPTOR_HANDLE structGPU = MakeUAV(structBuf).Format(DXGI_FORMAT_R32_UINT).CreateGPU(16); @@ -269,6 +380,17 @@ float4 main(v2f IN) : SV_Target0 MakeSRV(smiley).CreateGPU(59); MakeSRV(smiley).CreateGPU(99); MakeSRV(smiley).CreateGPU(103); + MakeCBV(constBuf).SizeBytes(256).CreateGPU(9); + MakeUAV(outUAV).Format(DXGI_FORMAT_R32_UINT).CreateGPU(10); + + D3D12_SAMPLER_DESC samplerDesc = {}; + samplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + samplerDesc.AddressU = samplerDesc.AddressV = samplerDesc.AddressW = + D3D12_TEXTURE_ADDRESS_MODE_WRAP; + UINT increment = dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); + D3D12_CPU_DESCRIPTOR_HANDLE samplerStart = m_Sampler->GetCPUDescriptorHandleForHeapStart(); + for(int i = 0; i < 8; ++i) + dev->CreateSampler(&samplerDesc, {samplerStart.ptr + increment * i}); while(Running()) { @@ -283,20 +405,20 @@ float4 main(v2f IN) : SV_Target0 ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); - const char *markers[] = {"Tests sm_5_1", "Tests sm_6_0"}; - - for(int i = 0; i < 2; i++) + const char *markers[] = {"Tests sm_5_1", "Tests sm_6_0", "Tests sm_6_6", "Tests sm_6_6_heap"}; + int testsToRun = supportSM66 ? 4 : 2; + for(int i = 0; i < testsToRun; i++) { if(!computepso[i]) continue; setMarker(cmd, markers[i]); - cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + ID3D12DescriptorHeap *heaps[] = {m_CBVUAVSRV.GetInterfacePtr(), m_Sampler.GetInterfacePtr()}; + cmd->SetDescriptorHeaps(2, heaps); UINT zero[4] = {}; cmd->ClearUnorderedAccessViewUint(structGPU, structCPU, structBuf, zero, 0, NULL); - cmd->SetPipelineState(computepso[i]); cmd->SetComputeRootSignature(computesig); cmd->SetComputeRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); @@ -308,9 +430,12 @@ float4 main(v2f IN) : SV_Target0 IASetVertexBuffer(cmd, vb, sizeof(DefaultA2V), 0); cmd->SetPipelineState(graphicspso[i]); - cmd->SetGraphicsRootSignature(graphicssig); - cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); - cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootSignature(graphicssigs[i]); + if(i < 3) + { + cmd->SetGraphicsRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + } RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); RSSetScissorRect(cmd, {0, 0, screenWidth, screenHeight}); diff --git a/util/test/demos/d3d12/d3d12_test.cpp b/util/test/demos/d3d12/d3d12_test.cpp index 86458ca05..3cd85914e 100644 --- a/util/test/demos/d3d12/d3d12_test.cpp +++ b/util/test/demos/d3d12/d3d12_test.cpp @@ -184,6 +184,14 @@ void D3D12GraphicsTest::Prepare(int argc, char **argv) tmpdev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS5, &opts5, sizeof(opts5)); tmpdev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS6, &opts6, sizeof(opts6)); tmpdev->CheckFeatureSupport(D3D12_FEATURE_D3D12_OPTIONS7, &opts7, sizeof(opts7)); + D3D12_FEATURE_DATA_SHADER_MODEL oShaderModel = {}; + oShaderModel.HighestShaderModel = D3D_SHADER_MODEL_6_6; + HRESULT hr = tmpdev->CheckFeatureSupport(D3D12_FEATURE_SHADER_MODEL, &oShaderModel, + sizeof(oShaderModel)); + if(SUCCEEDED(hr)) + { + m_HighestShaderModel = oShaderModel.HighestShaderModel; + } } } } diff --git a/util/test/demos/d3d12/d3d12_test.h b/util/test/demos/d3d12/d3d12_test.h index 38b7c1fc3..7f1f4cd89 100644 --- a/util/test/demos/d3d12/d3d12_test.h +++ b/util/test/demos/d3d12/d3d12_test.h @@ -236,6 +236,7 @@ struct D3D12GraphicsTest : public GraphicsTest D3D12_FEATURE_DATA_D3D12_OPTIONS5 opts5 = {}; D3D12_FEATURE_DATA_D3D12_OPTIONS6 opts6 = {}; D3D12_FEATURE_DATA_D3D12_OPTIONS7 opts7 = {}; + D3D_SHADER_MODEL m_HighestShaderModel = D3D_SHADER_MODEL_5_1; ID3D12FencePtr m_GPUSyncFence; HANDLE m_GPUSyncHandle = NULL; diff --git a/util/test/tests/D3D12/D3D12_Descriptor_Indexing.py b/util/test/tests/D3D12/D3D12_Descriptor_Indexing.py index 093a8d2a9..479e9b085 100644 --- a/util/test/tests/D3D12/D3D12_Descriptor_Indexing.py +++ b/util/test/tests/D3D12/D3D12_Descriptor_Indexing.py @@ -12,11 +12,8 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase): return super().check_support() - def check_capture(self): - - for sm in ["sm_5_1", "sm_6_0"]: - base = self.find_action("Tests " + sm) - action = self.find_action("Dispatch", base.eventId) + def check_compute(self, eventId): + action = self.find_action("Dispatch", eventId) self.check(action is not None) self.controller.SetFrameEvent(action.eventId, False) @@ -43,6 +40,15 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase): if root.views[i].dynamicallyUsed: raise rdtest.TestFailureException("Compute root range 0[{}] i dynamically used".format(i)) + def check_capture(self): + + for sm in ["sm_5_1", "sm_6_0", "sm_6_6"]: + base = self.find_action("Tests " + sm) + if base == None: + rdtest.log.print("Skipping test " + sm) + continue + self.check_compute(base.eventId) + action = self.find_action("Draw", base.eventId) self.check(action is not None) self.controller.SetFrameEvent(action.eventId, False) @@ -87,3 +93,80 @@ class D3D12_Descriptor_Indexing(rdtest.TestCase): "Range {} element {} expected to be unused, but is.".format(rangeIdx, idx)) rdtest.log.success("Dynamic usage is as expected for {}".format(sm)) + + for sm in ["sm_6_6_heap"]: + base = self.find_action("Tests " + sm) + if base == None: + rdtest.log.print("Skipping test " + sm) + continue + self.check_compute(base.eventId) + + action = self.find_action("Draw", base.eventId) + self.check(action is not None) + self.controller.SetFrameEvent(action.eventId, False) + + pipe = self.controller.GetD3D12PipelineState() + + # Check bindings: + # - CBV + # - Samplers + # - SRV resources + # - UAV resources + bind_info = { + 0: [9], + 1: [0, 1, 2, 4, 5, 6, 7], + 2: [8, 12, 19, 20, 21, 49, 59, 6, 99, 103], + 3: [10], + } + + if len(pipe.rootElements) != 4: + raise rdtest.TestFailureException("Wrong number of root signature ranges: {}, not 4" + .format(len(pipe.rootElements))) + cbvRangeIdx = 0 + samplerRangeIdx = 1 + srvRangeIdx = 2 + uavRangeIdx = 3 + for rangeIdx, root in enumerate(pipe.rootElements): + if root.dynamicallyUsedCount != len(bind_info[rangeIdx]): + raise rdtest.TestFailureException( + "{} : Root range {} doesn't have the right used count. {} is not the expected count of {}" + .format(sm, rangeIdx, root.dynamicallyUsedCount, len(bind_info[rangeIdx]))) + + for el in root.views: + expected_used = ( el.tableIndex in bind_info[srvRangeIdx] ) or ( ( el.tableIndex in bind_info[uavRangeIdx] ) ) + if not expected_used: + raise rdtest.TestFailureException( + "Descriptor {} expected to be unused, but is.".format(el.tableIndex)) + for el in root.constantBuffers: + expected_used = el.tableIndex in bind_info[cbvRangeIdx] + if not expected_used: + raise rdtest.TestFailureException( + "CBV {} expected to be unused, but is.".format(el.tableIndex)) + for el in root.samplers: + expected_used = el.tableIndex in bind_info[samplerRangeIdx] + if not expected_used: + raise rdtest.TestFailureException( + "Sampler {} expected to be unused, but is.".format(el.tableIndex)) + + for elemId in bind_info[srvRangeIdx]: + actually_used = any( srv.tableIndex == elemId for srv in pipe.rootElements[srvRangeIdx].views) + if not actually_used: + raise rdtest.TestFailureException( + "SRV {} expected to be used, but isn't.".format(elemId)) + for elemId in bind_info[uavRangeIdx]: + actually_used = any( uav.tableIndex == elemId for uav in pipe.rootElements[uavRangeIdx].views) + if not actually_used: + raise rdtest.TestFailureException( + "UAV {} expected to be used, but isn't.".format(elemId)) + for elemId in bind_info[cbvRangeIdx]: + actually_used = any( cbv.tableIndex == elemId for cbv in pipe.rootElements[cbvRangeIdx].constantBuffers) + if not actually_used: + raise rdtest.TestFailureException( + "CBV {} expected to be used, but isn't.".format(elemId)) + for elemId in bind_info[samplerRangeIdx]: + actually_used = any( samp.tableIndex == elemId for samp in pipe.rootElements[samplerRangeIdx].samplers) + if not actually_used: + raise rdtest.TestFailureException( + "Sampler {} expected to be used, but isn't.".format(elemId)) + + rdtest.log.success("Dynamic usage is as expected for {}".format(sm))