From aaefec8b795d1d8aaa6a5bbd7c043d88736e938b Mon Sep 17 00:00:00 2001 From: Jake Turner Date: Thu, 6 Mar 2025 08:16:46 +0000 Subject: [PATCH] D3D12_Shader_Debug_Zoo tests for loading from global variables Includes updating the python test to validate the results from compute shader debugging --- .../demos/d3d12/d3d12_shader_debug_zoo.cpp | 49 ++++++++++-- .../tests/D3D12/D3D12_Shader_Debug_Zoo.py | 74 +++++++++++++++---- 2 files changed, 104 insertions(+), 19 deletions(-) diff --git a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp index 0ceb60701..acee4b18c 100644 --- a/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp +++ b/util/test/demos/d3d12/d3d12_shader_debug_zoo.cpp @@ -191,6 +191,11 @@ StructuredBuffer int16srv : register(t42); Buffer int16srv : register(t43); #endif +static const int gConstInt = 10; +static const int gConstIntArray[6] = { 1, 2, 3, 4, 5, 6 }; +static int gInt = 3; +static int gIntArray[2] = { 5, 6 }; + float4 main(v2f IN) : SV_Target0 { float posinf = IN.oneVal/IN.zeroVal.x; @@ -968,6 +973,21 @@ float4 main(v2f IN) : SV_Target0 return structrwtest[z2+5].b; } + if(IN.tri == 105) + { + // idx = 0 + int idx = intval - IN.tri - 7; + return float4(gConstInt, gConstIntArray[idx+5], gConstIntArray[idx+1], gConstIntArray[idx+4]); + } + if(IN.tri == 106) + { + // idx = 0 + int idx = intval - IN.tri - 7; + int prev = gInt; + gInt += (idx+1); + gIntArray[idx] = gInt; + return float4(prev, gInt, gIntArray[idx], gIntArray[idx+1]); + } return float4(0.4f, 0.4f, 0.4f, 0.4f); } @@ -1017,13 +1037,32 @@ cbuffer consts : register(b0) RWStructuredBuffer bufIn : register(u0); RWStructuredBuffer bufOut : register(u1); +groupshared int gsmInt[128]; + [numthreads(1,1,1)] -void main() +void main(int3 inTestIndex : SV_GroupID) { - bufOut[0].x += bufIn[0].x * (uint)boolX; - bufOut[0].y += bufIn[0].y * (uint)intY; - bufOut[0].z += bufIn[0].z * (uint)floatZ; - bufOut[0].w += bufIn[0].w * (uint)doubleX; + int testIndex = inTestIndex.x; + int4 testResult = 123; + if (testIndex == 0) + { + testResult = bufOut[0]; + testResult.x += bufIn[0].x * (uint)boolX; + testResult.y += bufIn[0].y * (uint)intY; + testResult.z += bufIn[0].z * (uint)floatZ; + testResult.w += bufIn[0].w * (uint)doubleX; + } + else if (testIndex == 1) + { + gsmInt[testIndex-1] = testIndex; + testResult.x = gsmInt[testIndex-1]; + testResult.y = testIndex; + } + else + { + testResult.x = inTestIndex.x; + } + bufOut[testIndex] = testResult; } )EOSHADER"; diff --git a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py index 7d10fef09..40edd2d85 100644 --- a/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py +++ b/util/test/tests/D3D12/D3D12_Shader_Debug_Zoo.py @@ -1,11 +1,39 @@ import renderdoc as rd from typing import List import rdtest - +import struct class D3D12_Shader_Debug_Zoo(rdtest.TestCase): demos_test_name = 'D3D12_Shader_Debug_Zoo' + def parse_var_type(self, varType): + scalarType = varType + countElems = 1 + if str(varType[-1]).isdigit(): + if str(varType[-2]).isdigit(): + scalarType = varType[:-2] + countElems = int(varType[-2:]) + else: + scalarType = varType[:-1] + countElems = int(varType[-1:]) + return (scalarType, countElems) + + def get_source_var_value(self, sourceVars: List[rd.SourceVariableMapping], name, varType, debuggerVars): + sourceVar = [v for v in sourceVars if v.name == name] + if len(sourceVar) != 1: + raise rdtest.TestFailureException(f"Couldn't find source variable {name} {varType}") + + scalarType, countElems = self.parse_var_type(varType) + + debugged = self.evaluate_source_var(sourceVar[0], debuggerVars) + if scalarType == 'float': + return list(debugged.value.f32v[0:countElems]) + elif scalarType == 'int': + return list(debugged.value.s32v[0:countElems]) + else: + raise rdtest.TestFailureException(f"Unhandled scalarType {scalarType} {varType}") + return None + def check_capture(self): if not self.controller.GetAPIProperties().shaderDebugging: rdtest.log.success("Shader debugging not enabled, skipping test") @@ -166,9 +194,6 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase): else: rdtest.log.print(f"Skipping undebuggable Pixel shader at {action.eventId} for {shaderModels[sm]}.") - if failed: - raise rdtest.TestFailureException("Some tests were not as expected") - rdtest.log.end_section("VertexSample tests") test_marker: rd.ActionDescription = self.find_action("Banned") @@ -235,17 +260,38 @@ class D3D12_Shader_Debug_Zoo(rdtest.TestCase): continue # Debug the shader - trace: rd.ShaderDebugTrace = self.controller.DebugThread([0,0,0], [0,0,0]) - cycles, variables = self.process_trace(trace) - # Check for non-zero cycles - # TODO: Check source variables have expected values (bit like output variables in Vertex and Pixel Shaders) - self.controller.FreeTrace(trace) - if cycles == 0: - rdtest.log.error("Shader debug cycle count was zero") - failed = True - continue + for groupX in range(action.dispatchDimension[0]): + groupid = (groupX, 0, 0) + threadid = (0, 0, 0) + testIndex = groupX + trace: rd.ShaderDebugTrace = self.controller.DebugThread(groupid, threadid) + cycles, variables = self.process_trace(trace) + # Check for non-zero cycles + if cycles == 0: + rdtest.log.error("Shader debug cycle count was zero") + self.controller.FreeTrace(trace) + failed = True + continue - rdtest.log.success("Test {} matched as expected".format(test)) + bufOut = pipe.GetReadWriteResources(rd.ShaderStage.Compute)[1].descriptor.resource + bufdata = self.controller.GetBufferData(bufOut, testIndex*16, 16) + expectedValue = struct.unpack_from("4i", bufdata, 0) + # Test result is in variable called "int4 testResult" + name = 'testResult' + varType = 'int4' + try: + debuggedValue = self.get_source_var_value(trace.instInfo[-1].sourceVars, name, varType, variables) + if not rdtest.value_compare(expectedValue, debuggedValue): + raise rdtest.TestFailureException(f"'{name}' debugger {debuggedValue} doesn't match expected {expectedValue}") + + except rdtest.TestFailureException as ex: + rdtest.log.error(f"Test {test} Group:{groupid} Thread:{threadid} Index:{testIndex} failed {ex}") + failed = True + continue + finally: + self.controller.FreeTrace(trace) + + rdtest.log.success(f"Test {test} Group:{groupid} Thread:{threadid} as expected") rdtest.log.end_section(section) if failed: