diff --git a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp index e9ca720f9..18a56a2a8 100644 --- a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp +++ b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp @@ -22,6 +22,7 @@ * THE SOFTWARE. ******************************************************************************/ +#include "3rdparty/fmt/core.h" #include "d3d12_test.h" RD_TEST(D3D12_Shader_Debug_Zoo, D3D12GraphicsTest) @@ -1721,6 +1722,138 @@ void main(int3 inTestIndex : SV_GroupID) )EOSHADER"; + std::string computeShaderDerivs = shaderTypes + R"EOSHADER( + +// error X3556: integer divides may be much slower, try using uints if possible. +// we want to do this on purpose +#pragma warning( disable : 3556 ) + +cbuffer rootconsts : register(b0) +{ + uint root_test; +}; + +RWStructuredBuffer outbuf : register(u0); +Texture2D smiley : register(t5); +SamplerState linearclamp : register(s0); +SamplerComparisonState linearcompare : register(s1); + +static uint3 tid; +static uint3 gid; +static uint flatId; + +void SetOutput(float4 val) +{ + outbuf[root_test * 1024 + flatId] = val; +} + +void Init(float4 val) +{ + flatId = tid.z * GROUP_SIZE_X * GROUP_SIZE_Y + tid.y * GROUP_SIZE_X + tid.x; + SetOutput(val); +} + +#define IsTest(x) (root_test == x) + +#if WORKGROUP_SUPPORT +groupshared uint3 gsmUint3[1024]; +#endif // #if WORKGROUP_SUPPORT + +[numthreads(GROUP_SIZE_X, GROUP_SIZE_Y, 1)] +void main(uint3 inDTID : SV_DispatchThreadID, uint3 inGID : SV_GroupThreadID, uint3 inGroup : SV_GroupID) +{ + float4 testResult = float4(0,0,0,0); + tid = inDTID; + gid = inGID; + Init(testResult); + uint id = flatId; + uint ZERO = id / 10000; + uint ONE = ZERO + 1; + float2 inpos; + inpos.xy = gid.xy / 8.0; + +#if WORKGROUP_SUPPORT + gsmUint3[flatId].xyz = tid; +#endif // #if WORKGROUP_SUPPORT +#if SUBGROUP_SUPPORT + uint subgroupId = WaveGetLaneIndex(); + id += subgroupId; + testResult.w = id * ZERO; +#endif // #if SUBGROUP_SUPPORT + + if(IsTest(0)) + { + float4 test0; + test0.x = ddx(0.5f); + test0.y = ddy(0.5f); + test0.z = ddx_fine(0.5f); + test0.w = ddy_fine(0.5f); + testResult = test0; + } + if(IsTest(1)) + { + float3 test1 = ddx(gid*gid); + testResult.xyz = test1; + } + if(IsTest(2)) + { + float3 test2 = ddy(gid*gid); + testResult.xyz = test2; + } + if(IsTest(3)) + { + float3 test3 = ddx_fine(gid*gid); + testResult.xyz = test3; + } + if(IsTest(4)) + { + float3 test4 = ddy_fine(gid*gid); + testResult.xyz = test4; + } + if(IsTest(5)) + { + float3 test5 = ddx_coarse(gid*gid); + testResult.xyz = test5; + } + if(IsTest(6)) + { + float3 test6 = ddy_coarse(gid*gid); + testResult.xyz = test6; + } + if(IsTest(7)) + { + float2 test7; + test7.x = smiley.CalculateLevelOfDetail(linearclamp, inpos); + test7.y = smiley.CalculateLevelOfDetailUnclamped(linearclamp, inpos); + testResult.xy = test7; + } + if(IsTest(8)) + { + float2 uv = ONE * float2(0.55f, 0.48f); + float4 test8 = smiley.Sample(linearclamp, uv); + testResult = test8; + } + if(IsTest(9)) + { + float2 uv = ONE * float2(0.75f, 0.68f); + float4 test9 = smiley.SampleBias(linearclamp, uv, 4.0); + testResult = test9; + } + if(IsTest(10)) + { + float2 uv = ONE * float2(0.25f, 0.38f); + float4 test10; + test10.x = smiley.SampleCmp(linearcompare, uv, 0.1); + test10.y = smiley.SampleCmp(linearcompare, uv, 0.3); + test10.z = smiley.SampleCmp(linearcompare, uv, 0.5); + test10.w = smiley.SampleCmp(linearcompare, uv, 0.7); + testResult = test10; + } + + SetOutput(testResult); +} + +)EOSHADER"; int main() { // initialise, create window, create device, etc @@ -1772,6 +1905,10 @@ void main(int3 inTestIndex : SV_GroupID) lastTest += sizeof("testIndex == ") - 1; const uint32_t numComputeTests = atoi(computeMain.c_str() + lastTest) + 1; + lastTest = computeShaderDerivs.rfind("IsTest("); + lastTest += sizeof("IsTest(") - 1; + const uint32_t numComputeDerivsTests = atoi(computeShaderDerivs.c_str() + lastTest) + 1; + std::vector inputLayout; inputLayout.reserve(4); inputLayout.push_back({ @@ -2510,9 +2647,103 @@ void main(int3 inTestIndex : SV_GroupID) computePSOs[2] = MakePSO().RootSig(sigCompute).CS(csblob); } + D3D12_STATIC_SAMPLER_DESC samplers[] = {staticSamp, staticSamp}; + samplers[0].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[1].ShaderVisibility = D3D12_SHADER_VISIBILITY_ALL; + samplers[1].ComparisonFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; + samplers[1].ShaderRegister = 1; + + ID3D12RootSignaturePtr sigComputeDerivs = MakeSig( + { + constParam(D3D12_SHADER_VISIBILITY_ALL, 0, 0, 1), + uavParam(D3D12_SHADER_VISIBILITY_ALL, 0, 0), + tableParam(D3D12_SHADER_VISIBILITY_ALL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 8, 0), + }, + D3D12_ROOT_SIGNATURE_FLAG_NONE, 2, samplers); + + struct + { + int x, y; + } csDimens[] = { + {8, 4}, + {32, 1}, + }; + ID3D12PipelineStatePtr computeDerivsPSOs[8] = {NULL, NULL}; + std::string computeDerivsNames[8]; + uint32_t countComputeDerivsPSOs = 0; + std::string localDefines; + if(supportSM66) + { + for(size_t i = 0; i < ARRAY_COUNT(csDimens); ++i) + { + std::map macros; + macros["GROUP_SIZE_X"] = fmt::format("{}", csDimens[i].x); + macros["GROUP_SIZE_Y"] = fmt::format("{}", csDimens[i].y); + macros["GROUP_SIZE_Z"] = "1"; + macros["WORKGROUP_SUPPORT"] = "0"; + macros["SUBGROUP_SUPPORT"] = "0"; + + localDefines = "#define SM_6_6 1\n"; + for(const auto &it : macros) + localDefines += fmt::format("#define {} {}\n", it.first, it.second); + + compute = shaderDefines + localDefines + computeShaderDerivs; + csblob = Compile(compute, "main", "cs_6_6"); + computeDerivsNames[countComputeDerivsPSOs] = fmt::format( + "{}x{}x{}", macros["GROUP_SIZE_X"], macros["GROUP_SIZE_Y"], macros["GROUP_SIZE_Z"]); + computeDerivsPSOs[countComputeDerivsPSOs] = MakePSO().RootSig(sigComputeDerivs).CS(csblob); + ++countComputeDerivsPSOs; + + // with workgroup + macros["WORKGROUP_SUPPORT"] = "1"; + localDefines = "#define SM_6_6 1\n"; + for(const auto &it : macros) + localDefines += fmt::format("#define {} {}\n", it.first, it.second); + + compute = shaderDefines + localDefines + computeShaderDerivs; + csblob = Compile(compute, "main", "cs_6_6"); + computeDerivsNames[countComputeDerivsPSOs] = + fmt::format("{}x{}x{} : Workgroup", macros["GROUP_SIZE_X"], macros["GROUP_SIZE_Y"], + macros["GROUP_SIZE_Z"]); + computeDerivsPSOs[countComputeDerivsPSOs] = MakePSO().RootSig(sigComputeDerivs).CS(csblob); + ++countComputeDerivsPSOs; + + // with subgroup + macros["SUBGROUP_SUPPORT"] = "1"; + macros["WORKGROUP_SUPPORT"] = "0"; + localDefines = "#define SM_6_6 1\n"; + for(const auto &it : macros) + localDefines += fmt::format("#define {} {}\n", it.first, it.second); + + compute = shaderDefines + localDefines + computeShaderDerivs; + csblob = Compile(compute, "main", "cs_6_6"); + computeDerivsNames[countComputeDerivsPSOs] = + fmt::format("{}x{}x{} : Subgroup", macros["GROUP_SIZE_X"], macros["GROUP_SIZE_Y"], + macros["GROUP_SIZE_Z"]); + computeDerivsPSOs[countComputeDerivsPSOs] = MakePSO().RootSig(sigComputeDerivs).CS(csblob); + ++countComputeDerivsPSOs; + + // with subgroup+workgroup + macros["SUBGROUP_SUPPORT"] = "1"; + macros["WORKGROUP_SUPPORT"] = "1"; + localDefines = "#define SM_6_6 1\n"; + for(const auto &it : macros) + localDefines += fmt::format("#define {} {}\n", it.first, it.second); + + compute = shaderDefines + localDefines + computeShaderDerivs; + csblob = Compile(compute, "main", "cs_6_6"); + computeDerivsNames[countComputeDerivsPSOs] = + fmt::format("{}x{}x{} : Subgroup + Workgroup", macros["GROUP_SIZE_X"], + macros["GROUP_SIZE_Y"], macros["GROUP_SIZE_Z"]); + computeDerivsPSOs[countComputeDerivsPSOs] = MakePSO().RootSig(sigComputeDerivs).CS(csblob); + ++countComputeDerivsPSOs; + } + } + const uint32_t numCompTests = std::max(numComputeTests, numComputeDerivsTests); + const uint32_t uavSize = 1024; ID3D12ResourcePtr bufIn = MakeBuffer().Size(uavSize).UAV(); - ID3D12ResourcePtr bufOut = MakeBuffer().Size(uavSize).UAV(); + ID3D12ResourcePtr bufOut = MakeBuffer().Size(sizeof(Vec4f) * 1024 * numCompTests).UAV(); bufIn->SetName(L"bufIn"); bufOut->SetName(L"bufOut"); @@ -2743,6 +2974,29 @@ void main(int3 inTestIndex : SV_GroupID) } popMarker(cmd); + pushMarker(cmd, "Compute Derivative Tests"); + for(size_t p = 0; p < countComputeDerivsPSOs; p++) + { + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + + cmd->ClearUnorderedAccessViewUint(bufOutGPU, bufOutClearCPU, bufOut, bufOutInitData, 1, + &uavClearRect); + + cmd->SetComputeRootSignature(sigComputeDerivs); + cmd->SetComputeRootUnorderedAccessView(1, bufOutVA); + cmd->SetComputeRootDescriptorTable(2, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + + cmd->SetPipelineState(computeDerivsPSOs[p]); + pushMarker(cmd, computeDerivsNames[p]); + for(uint32_t t = 0; t < numComputeDerivsTests; t++) + { + cmd->SetComputeRoot32BitConstant(0, t, 0); + cmd->Dispatch(2, 1, 1); + } + popMarker(cmd); + } + popMarker(cmd); + cmd->Close(); Submit({cmd}); Present(); diff --git a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py index af036ef91..2a0dd3847 100644 --- a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py +++ b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py @@ -5,6 +5,102 @@ import struct class D3D12_Shader_Debug_Zoo(rdtest.TestCase): demos_test_name = 'D3D12_Shader_Debug_Zoo' + def check_compute_derivative_tests(self): + failed = False + thread_checks = [ + (0,0,0), + (1,1,0), + (2,2,0), + (3,3,0), + (4,2,0), + (5,1,0), + (6,0,0), + (7,1,0), + ] + compute_dims = [a for a in self.find_action( + "Compute Derivative Tests").children if 'x' in a.customName] + for comp_dim in compute_dims: + section = f"Compute tests with {comp_dim.customName}" + rdtest.log.begin_section(section) + + compute_tests = [ + a for a in comp_dim.children if a.flags & rd.ActionFlags.Dispatch] + + for test, action in enumerate(compute_tests): + failed = False + self.controller.SetFrameEvent(action.eventId, False) + pipe = self.controller.GetPipelineState() + csrefl = pipe.GetShaderReflection(rd.ShaderStage.Compute) + if not csrefl.debugInfo.debuggable: + rdtest.log.print(f"Compute shader is undebuggable at {action.eventId} for {test}.") + failed = True + continue + + rw = pipe.GetReadWriteResources(rd.ShaderStage.Compute) + if len(rw) != 1: + rdtest.log.error("Unexpected number of RW resources") + self.controller.FreeTrace(trace) + failed = True + continue + + groupid = (1, 0, 0) + dim = csrefl.dispatchThreadsDimension + + # Debug the shader + for threadid in thread_checks: + tid = ( min(threadid[0], dim[0]-1), min(threadid[1], dim[1]-1), min(threadid[2], dim[2]-1)) + # each test writes up to 16k data, one vec4 per thread * up to 1024 threads + bufdata = self.controller.GetBufferData( + rw[0].descriptor.resource, test*16*1024, 16*1024) + try: + expectedValue = struct.unpack_from( + "4f", bufdata, 16*tid[1]*dim[0] + 16*tid[0]) + except Exception as ex: + rdtest.log.error(f"Exception Test {test} failed {ex}") + failed = True + continue + + # Debug the shader + trace: rd.ShaderDebugTrace = self.controller.DebugThread(groupid, tid) + cycles, variables = self.process_trace(trace) + # Check for non-zero cycles + if cycles == 0: + rdtest.log.success(f"Test {test} Group:{groupid} Thread:{tid} : Shader debug cycle count was zero") + self.controller.FreeTrace(trace) + failed = True + continue + + # Find the source variable 'testResult' at the highest instruction index + name = 'testResult' + debugged = None + countInst = len(trace.instInfo) + for inst in range(countInst): + sourceVars = trace.instInfo[countInst-1-inst].sourceVars + try: + dataVars = [v for v in sourceVars if v.name == name] + if len(dataVars) == 0: + continue + debugged = self.evaluate_source_var(dataVars[0], variables) + except KeyError as ex: + continue + except rdtest.TestFailureException as ex: + continue + break + if debugged is None: + raise rdtest.TestFailureException(f"Couldn't find source variable {name} at {test}") + debuggedValue = list(debugged.value.f32v[0:4]) + + if not rdtest.value_compare(expectedValue, debuggedValue, eps=5.0E-06): + rdtest.log.error(f"Test {test} Group:{groupid} Thread:{tid} EID:{action.eventId} failed {name} debugger {debuggedValue} doesn't match expected {expectedValue}") + self.controller.FreeTrace(trace) + failed = True + continue + + rdtest.log.success(f"Test {test} Group:{groupid} Thread:{tid} as expected") + + rdtest.log.end_section(section) + return failed + def check_capture(self): if not self.controller.GetAPIProperties().shaderDebugging: rdtest.log.success("Shader debugging not enabled, skipping test") @@ -292,6 +388,9 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase): rdtest.log.success(f"Test {test} Group:{groupid} Thread:{threadid} as expected") rdtest.log.end_section(section) + if self.check_compute_derivative_tests(): + failed = True + if failed: raise rdtest.TestFailureException("Some tests were not as expected")