diff --git a/util/test/demos/d3d12/d3d12_shader_debugdata_zoo.cpp b/util/test/demos/d3d12/d3d12_shader_debugdata_zoo.cpp new file mode 100644 index 000000000..c96d8f9a4 --- /dev/null +++ b/util/test/demos/d3d12/d3d12_shader_debugdata_zoo.cpp @@ -0,0 +1,435 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2025 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "d3d12_test.h" + +RD_TEST(D3D12_Shader_DebugData_Zoo, D3D12GraphicsTest) +{ + static constexpr const char *Description = "Tests shader debug data from different sources"; + + struct ConstsA2V + { + Vec3f pos; + }; + + std::string pixelBlit = R"EOSHADER( + +cbuffer rootconsts : register(b0) +{ + float offset; +} + +Texture2D intex : register(t0); + +float4 main(float4 pos : SV_Position) : SV_Target0 +{ + return intex.Load(float3(pos.x, pos.y - offset, 0)); +} + +)EOSHADER"; + + std::string common = R"EOSHADER( + +struct consts +{ + float3 pos : POSITION; +}; + +struct v2f +{ + float4 pos : SV_POSITION; + uint tri : TRIANGLE; + uint intval : INTVAL; +}; + +)EOSHADER"; + + std::string vertex = R"EOSHADER( + +v2f main(consts IN, uint tri : SV_InstanceID) +{ + v2f OUT = (v2f)0; + + OUT.pos = float4(IN.pos.x + IN.pos.z * float(tri), IN.pos.y, 0.0f, 1); + + OUT.tri = tri; + OUT.intval = tri + 11; + + return OUT; +} + +)EOSHADER"; + + std::string testDefines = R"EOSHADER( + +#define TEST_RESULT 0 +#define TEST_DEBUG_TYPE(TYPE) TYPE __test_ ## TYPE = 0; +#define TEST_DEBUG_VECTOR_TYPE(TYPE) \ +TEST_DEBUG_TYPE(TYPE) \ +TEST_DEBUG_TYPE(TYPE ## 1) \ +TEST_DEBUG_TYPE(TYPE ## 2) \ +TEST_DEBUG_TYPE(TYPE ## 3) \ +TEST_DEBUG_TYPE(TYPE ## 4) + +#define USE_DEBUG_VECTOR_TYPE(TYPE) \ + testResult.x += __test_ ## TYPE .x; \ + testResult.x += __test_ ## TYPE ## 1 .x; \ + testResult.xy += __test_ ## TYPE ## 2 .xy; \ + testResult.xyz += __test_ ## TYPE ## 3 .xyz; \ + testResult.xyzw += __test_ ## TYPE ## 4 .xyzw; + +#define TEST_DEBUG_VAR_SET(TYPE, VAR, VALUE) \ + VAR = VALUE * __ONE; \ + __test_ ## TYPE += VAR; + +#define TEST_DEBUG_VAR_DECLARE(TYPE, VAR, VALUE) TYPE VAR; TEST_DEBUG_VAR_SET(TYPE, VAR, VALUE) + +struct TestStruct +{ + struct + { + int4 a; + int3 b; + } anon; +}; + +)EOSHADER"; + + std::string testsBody = R"EOSHADER( + + TestStruct testStruct; + + TEST_DEBUG_VECTOR_TYPE(int); + TEST_DEBUG_VECTOR_TYPE(float); + + // TEST_DEBUG_VAR_START + TEST_DEBUG_VAR_DECLARE(int, testIndex, TEST_INDEX) + TEST_DEBUG_VAR_DECLARE(int1, intVal, TEST_INDEX) + TEST_DEBUG_VAR_DECLARE(float4, testResult, TEST_RESULT) + TEST_DEBUG_VAR_DECLARE(int2, jake, 5) + TEST_DEBUG_VAR_DECLARE(float1, bob, 3.0) + TEST_DEBUG_VAR_SET(int4, testStruct.anon.a, 1) + TEST_DEBUG_VAR_SET(int3, testStruct.anon.b, 2) + // TEST_DEBUG_VAR_END + + if(testIndex == 0) + { + testResult = intVal; + } + else if(testIndex == 1) + { + testResult = intVal * 2; + } + else if(testIndex == 2) + { + testResult = intVal / 2; + } + else if(testIndex == 3) + { + testResult.x = testStruct.anon.a.x; + testResult.y = testStruct.anon.b.x; + } + else + { + testResult = 0.4f; + } + + USE_DEBUG_VECTOR_TYPE(int); + USE_DEBUG_VECTOR_TYPE(float); +)EOSHADER"; + + std::string pixel = R"EOSHADER( + +#define TEST_INDEX IN.tri +)EOSHADER" + testDefines + + R"EOSHADER( + +float4 main(v2f IN) : SV_Target0 +{ + int __ONE = floor((IN.intval - 11)/(IN.tri+1.0e-6f)) + 1; +)EOSHADER" + testsBody + + R"EOSHADER( + + return testResult; +} + +)EOSHADER"; + + std::string compute = R"EOSHADER( + +#define TEST_INDEX inTestIndex +)EOSHADER" + testDefines + + R"EOSHADER( + +RWStructuredBuffer bufOut : register(u0); + +[numthreads(1,1,1)] +void main(int inTestIndex: SV_GroupID) +{ + int __ONE = floor(inTestIndex/(inTestIndex+1.0e-6f)) + 1; +)EOSHADER" + testsBody + + R"EOSHADER( + + bufOut[testIndex] = testResult; +} + +)EOSHADER"; + + void Prepare(int argc, char **argv) + { + D3D12GraphicsTest::Prepare(argc, argv); + + if(!Avail.empty()) + return; + + bool supportSM60 = (m_HighestShaderModel >= D3D_SHADER_MODEL_6_0) && m_DXILSupport; + if(!supportSM60) + Avail = "Shader Model 6.0 and DXIL is not supported"; + } + + int main() + { + // initialise, create window, create device, etc + if(!Init()) + return 3; + + const char *testMatch = NULL; + size_t lastTest = 0; + + testMatch = "testIndex == "; + lastTest = pixel.rfind(testMatch); + lastTest += strlen(testMatch); + const uint32_t numPSTests = atoi(pixel.c_str() + lastTest) + 1; + + lastTest = compute.rfind(testMatch); + lastTest += strlen(testMatch); + const uint32_t numCSTests = atoi(compute.c_str() + lastTest) + 1; + + TEST_ASSERT(numPSTests == numCSTests, + "Mismatched number of tests between pixel and compute shaders"); + + std::vector inputLayout; + inputLayout.reserve(4); + inputLayout.push_back({ + "POSITION", + 0, + DXGI_FORMAT_R32G32B32_FLOAT, + 0, + 0, + D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, + 0, + }); + + D3D12_STATIC_SAMPLER_DESC staticSamp = {}; + staticSamp.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; + staticSamp.AddressU = staticSamp.AddressV = staticSamp.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; + staticSamp.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + + ID3D12RootSignaturePtr sig = + MakeSig({}, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT, 1, &staticSamp); + + const int numShaderModels = 1; // 6.0 + ID3D12PipelineStatePtr psos[numShaderModels] = {}; + + uint32_t compileOptions = CompileOptionFlags::None; + std::string shaderDefines = ""; + { + ID3DBlobPtr vsblob = Compile(common + vertex, "main", "vs_6_0"); + psos[0] = MakePSO() + .RootSig(sig) + .InputLayout(inputLayout) + .VS(vsblob) + .PS(Compile(common + "\n#define SM_6_0 1\n" + shaderDefines + pixel, "main", + "ps_6_0", compileOptions | CompileOptionFlags::SkipOptimise)) + .RTVs({DXGI_FORMAT_R32G32B32A32_FLOAT}); + psos[0]->SetName(L"ps_6_0"); + } + static const uint32_t texDim = AlignUp(numPSTests, 64U) * 4; + + ID3D12ResourcePtr fltTex = MakeTexture(DXGI_FORMAT_R32G32B32A32_FLOAT, texDim, 4) + .RTV() + .InitialState(D3D12_RESOURCE_STATE_RENDER_TARGET); + D3D12_CPU_DESCRIPTOR_HANDLE fltRTV = MakeRTV(fltTex).CreateCPU(0); + D3D12_GPU_DESCRIPTOR_HANDLE fltSRV = MakeSRV(fltTex).CreateGPU(8); + + float triWidth = 8.0f / float(texDim); + + ConstsA2V triangle[] = { + {Vec3f(-1.0f, -1.0f, triWidth)}, + {Vec3f(-1.0f, 1.0f, triWidth)}, + {Vec3f(-1.0f + triWidth, 1.0f, triWidth)}, + }; + + ID3D12ResourcePtr vb = MakeBuffer().Data(triangle); + ResourceBarrier(vb, D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER); + + ID3D12RootSignaturePtr blitSig = MakeSig({ + constParam(D3D12_SHADER_VISIBILITY_PIXEL, 0, 0, 1), + tableParam(D3D12_SHADER_VISIBILITY_PIXEL, D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 0, 0, 1, 8), + }); + ID3D12PipelineStatePtr blitpso = MakePSO() + .RootSig(blitSig) + .VS(Compile(D3DFullscreenQuadVertex, "main", "vs_4_0")) + .PS(Compile(pixelBlit, "main", "ps_5_0")); + + const uint32_t renderDataSize = sizeof(float) * 22; + // Create resources for compute shader + const uint32_t computeDataStart = AlignUp(renderDataSize, 1024U); + ID3D12RootSignaturePtr sigCompute = MakeSig({ + tableParam(D3D12_SHADER_VISIBILITY_ALL, D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 0, 0, 2), + }); + + const uint32_t countComputeSMs = 1; + ID3D12PipelineStatePtr computePSOs[countComputeSMs] = {NULL}; + std::string computeSMs[countComputeSMs] = {"cs_6_0"}; + ID3DBlobPtr csblob = + Compile(compute, "main", "cs_6_0", compileOptions | CompileOptionFlags::SkipOptimise); + computePSOs[0] = MakePSO().RootSig(sigCompute).CS(csblob); + + const uint32_t uavSize = AlignUp(numCSTests, 1024U); + ID3D12ResourcePtr bufOut = MakeBuffer().Size(uavSize).UAV(); + bufOut->SetName(L"bufOut"); + + D3D12_GPU_DESCRIPTOR_HANDLE bufOutGPU = + MakeUAV(bufOut).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).CreateGPU(computeDataStart + 0); + D3D12_CPU_DESCRIPTOR_HANDLE bufOutClearCPU = + MakeUAV(bufOut).Format(DXGI_FORMAT_R32G32B32A32_FLOAT).CreateClearCPU(computeDataStart + 0); + + { + D3D12_CPU_DESCRIPTOR_HANDLE cpu = m_CBVUAVSRV->GetCPUDescriptorHandleForHeapStart(); + cpu.ptr += dev->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV) * 0; + + D3D12_UNORDERED_ACCESS_VIEW_DESC uavdesc = {}; + uavdesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; + uavdesc.ViewDimension = D3D12_UAV_DIMENSION_BUFFER; + uavdesc.Buffer.NumElements = numCSTests; + + dev->CreateUnorderedAccessView(bufOut, NULL, &uavdesc, cpu); + } + + std::vector bufOutInitData; + bufOutInitData.resize(uavSize); + for(uint32_t i = 0; i < uavSize; ++i) + { + bufOutInitData[i] = 222.0f + i / 4.0f; + } + + D3D12_RECT uavClearRect = {}; + uavClearRect.right = uavSize; + uavClearRect.bottom = 1; + + while(Running()) + { + ID3D12GraphicsCommandListPtr cmd = GetCommandBuffer(); + Reset(cmd); + + ID3D12ResourcePtr bb = StartUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + + D3D12_CPU_DESCRIPTOR_HANDLE rtv = + MakeRTV(bb).Format(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB).CreateCPU(2); + ClearRenderTargetView(cmd, rtv, {0.2f, 0.2f, 0.2f, 1.0f}); + + float blitOffsets[] = {0.0f}; + D3D12_RECT scissors[] = { + {0, 0, (int)texDim, 4}, + }; + const char *markers[] = { + "sm_6_0", + }; + static_assert(ARRAY_COUNT(blitOffsets) == ARRAY_COUNT(psos), "mismatched array dimension"); + static_assert(ARRAY_COUNT(scissors) == ARRAY_COUNT(psos), "mismatched array dimension"); + static_assert(ARRAY_COUNT(markers) == ARRAY_COUNT(psos), "mismatched array dimension"); + + // Clear, draw, and blit to backbuffer + size_t countGraphicsPasses = 1; + TEST_ASSERT(countGraphicsPasses <= ARRAY_COUNT(psos), "More graphic passes than psos"); + pushMarker(cmd, "Pixel"); + for(size_t i = 0; i < countGraphicsPasses; ++i) + { + OMSetRenderTargets(cmd, {fltRTV}, {}); + ClearRenderTargetView(cmd, fltRTV, {0.2f, 0.2f, 0.2f, 1.0f}); + + IASetVertexBuffer(cmd, vb, sizeof(ConstsA2V), 0); + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + cmd->SetGraphicsRootSignature(sig); + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetPipelineState(psos[i]); + + RSSetViewport(cmd, {0.0f, 0.0f, (float)texDim, 4.0f, 0.0f, 1.0f}); + RSSetScissorRect(cmd, {0, 0, (int)texDim, 4}); + + // Add a marker so we can easily locate this draw + setMarker(cmd, markers[i]); + cmd->DrawInstanced(3, numPSTests, 0, 0); + + ResourceBarrier(cmd, fltTex, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + + OMSetRenderTargets(cmd, {rtv}, {}); + RSSetViewport(cmd, {0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f}); + RSSetScissorRect(cmd, scissors[i]); + + cmd->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); + cmd->SetGraphicsRootSignature(blitSig); + cmd->SetPipelineState(blitpso); + cmd->SetGraphicsRoot32BitConstant(0, *(UINT *)&blitOffsets[i], 0); + cmd->SetGraphicsRootDescriptorTable(1, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + cmd->DrawInstanced(4, 1, 0, 0); + + ResourceBarrier(cmd, fltTex, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + D3D12_RESOURCE_STATE_RENDER_TARGET); + } + + FinishUsingBackbuffer(cmd, D3D12_RESOURCE_STATE_RENDER_TARGET); + popMarker(cmd); + + pushMarker(cmd, "Compute"); + size_t countComputePasses = 1; + TEST_ASSERT(countComputePasses <= ARRAY_COUNT(computePSOs), "More compute passes than psos"); + for(size_t i = 0; i < countComputePasses; ++i) + { + cmd->ClearUnorderedAccessViewFloat(bufOutGPU, bufOutClearCPU, bufOut, bufOutInitData.data(), + 1, &uavClearRect); + + cmd->SetComputeRootSignature(sigCompute); + cmd->SetDescriptorHeaps(1, &m_CBVUAVSRV.GetInterfacePtr()); + cmd->SetComputeRootDescriptorTable(0, m_CBVUAVSRV->GetGPUDescriptorHandleForHeapStart()); + + cmd->SetPipelineState(computePSOs[i]); + setMarker(cmd, computeSMs[i]); + cmd->Dispatch(numCSTests, 1, 1); + } + popMarker(cmd); + + cmd->Close(); + Submit({cmd}); + Present(); + } + + return 0; + } +}; + +REGISTER_TEST(); diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj index 818bfc6b5..bf3d1358b 100644 --- a/util/test/demos/demos.vcxproj +++ b/util/test/demos/demos.vcxproj @@ -214,6 +214,7 @@ + diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters index 6b73b3cfd..26838c3d3 100644 --- a/util/test/demos/demos.vcxproj.filters +++ b/util/test/demos/demos.vcxproj.filters @@ -709,6 +709,9 @@ D3D11\demos + + D3D12\demos + diff --git a/util/test/tests/D3D12/D3D12_Shader_DebugData_Zoo.py b/util/test/tests/D3D12/D3D12_Shader_DebugData_Zoo.py new file mode 100644 index 000000000..93ee3c4dc --- /dev/null +++ b/util/test/tests/D3D12/D3D12_Shader_DebugData_Zoo.py @@ -0,0 +1,207 @@ +import renderdoc as rd +import rdtest +import struct + +class D3D12_Shader_DebugData_Zoo(rdtest.TestCase): + demos_test_name = 'D3D12_Shader_DebugData_Zoo' + + def parse_shader_source(self, shaderSrcRaw, realTestResult, test): + ''' + // TEST_DEBUG_VAR_START + TEST_DEBUG_VAR_DECLARE(int, testIndex, TEST_INDEX) + TEST_DEBUG_VAR_DECLARE(int1, intVal, TEST_INDEX) + TEST_DEBUG_VAR_DECLARE(float4, testResult, TEST_RESULT) + TEST_DEBUG_VAR_DECLARE(int2, jake, 5) + TEST_DEBUG_VAR_DECLARE(float1, bob, 3.0) + TEST_DEBUG_VAR_USE(int4, testStruct.anon.a, 1) + // TEST_DEBUG_VAR_END + ''' + varsToCheck = [] + foundStart = False + foundEnd = False + shaderSrc = shaderSrcRaw.splitlines() + for line in shaderSrc: + line = line.strip() + if line.endswith('TEST_DEBUG_VAR_START'): + foundStart = True + continue + if line.endswith('TEST_DEBUG_VAR_END'): + foundEnd = True + break + if not foundStart: + continue + if not line.startswith('TEST_DEBUG_VAR_'): + continue + toks = line.split(',') + type = toks[0].split('(')[1].strip() + name = toks[1].strip() + valString = toks[2].split(')')[0].strip() + scalarType, countElems = self.parse_shader_var_type(type) + if valString == 'TEST_INDEX': + value = test + elif valString == 'TEST_RESULT': + value = realTestResult + else: + if scalarType == 'float': + scalarVal = float(valString) + elif scalarType == 'int': + scalarVal = int(valString) + else: + raise rdtest.TestFailureException(f"Unhandled scalarType {scalarType} {type}") + value = [scalarVal] * countElems + + var = (name, type, value) + varsToCheck.append(var) + + if not foundStart or not foundEnd: + raise rdtest.TestFailureException("Couldn't find TEST_DEBUG_VAR_START and TEST_DEBUG_VAR_END") + + return varsToCheck + + def check_capture(self): + if not self.controller.GetAPIProperties().shaderDebugging: + rdtest.log.success("Shader debugging not enabled, skipping test") + return + + failed = False + + shaderModels = [ + "sm_6_0", + ] + for sm in range(len(shaderModels)): + rdtest.log.begin_section(shaderModels[sm] + " tests") + + # Jump to the action + test_marker: rd.ActionDescription = self.find_action(shaderModels[sm]) + if (test_marker is None): + rdtest.log.print(f"Skipping Graphics tests for {shaderModels[sm]}") + rdtest.log.end_section(shaderModels[sm] + " tests") + continue + action = test_marker.next + self.controller.SetFrameEvent(action.eventId, False) + + pipe: rd.PipeState = self.controller.GetPipelineState() + + if pipe.GetShaderReflection(rd.ShaderStage.Vertex).debugInfo.debuggable: + # Debug the vertex shader + instId = 1 + trace: rd.ShaderDebugTrace = self.controller.DebugVertex(0, instId, 0, 0) + cycles, variables = self.process_trace(trace) + output = self.find_output_source_var(trace, rd.ShaderBuiltin.Undefined, 1) + debugged = self.evaluate_source_var(output, variables) + actual = debugged.value.u32v[0] + expected = instId + if not rdtest.value_compare(actual, expected): + failed = True + rdtest.log.error( + f"Vertex shader TRIANGLE output did not match expectation {actual} != {expected}") + if not failed: + rdtest.log.success("Basic VS debugging was successful") + else: + rdtest.log.print(f"Ignoring undebuggable Vertex shader at {action.eventId} for {shaderModels[sm]}.") + + if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable: + rdtest.log.print(f"Skipping undebuggable Pixel shader at {action.eventId} for {shaderModels[sm]}.") + rdtest.log.end_section(shaderModels[sm] + " tests") + continue + + # Loop over every test + for test in range(action.numInstances): + # Debug the shader + trace: rd.ShaderDebugTrace = self.controller.DebugPixel(4 * test, 0, rd.DebugPixelInputs()) + + cycles, variables = self.process_trace(trace) + + output = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0) + + debugged = self.evaluate_source_var(output, variables) + + try: + tex = pipe.GetOutputTargets()[0].resource + x = 4 * test + y = 0 + self.check_pixel_value(tex, x, y, debugged.value.f32v[0:4]) + picked = rd.PixelValue = self.controller.PickPixel(tex, x, y, rd.Subresource(0,0,0), rd.CompType.Typeless) + realTestResult = picked.floatValue + debugInfo = pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo + shaderSrcRaw = debugInfo.files[0].contents + varsToCheck = self.parse_shader_source(shaderSrcRaw, realTestResult, test) + for name, varType, expectedValue in varsToCheck: + debuggedValue = self.get_source_shader_var_value(trace.instInfo[-1].sourceVars, name, varType, variables) + if not rdtest.value_compare(expectedValue, debuggedValue): + raise rdtest.TestFailureException(f"'{name}' {varType} debugger {debuggedValue} doesn't match expected {expectedValue}") + rdtest.log.success(f"{len(varsToCheck)} source variables matched as expected") + + except rdtest.TestFailureException as ex: + rdtest.log.error(f"Test {test} failed {ex}") + failed = True + continue + finally: + self.controller.FreeTrace(trace) + + rdtest.log.success("Test {} matched as expected".format(test)) + + rdtest.log.end_section(shaderModels[sm] + " tests") + + csShaderModels = ["cs_6_0"] + for sm in range(len(csShaderModels)): + test = csShaderModels[sm] + section = test + " tests" + rdtest.log.begin_section(section) + + # Jump to the action + test_marker: rd.ActionDescription = self.find_action(test) + if test_marker is None: + rdtest.log.print(f"Skipping Compute tests for {csShaderModels[sm]}") + rdtest.log.end_section(section) + continue + action = test_marker.next + self.controller.SetFrameEvent(action.eventId, False) + pipe: rd.PipeState = self.controller.GetPipelineState() + if not pipe.GetShaderReflection(rd.ShaderStage.Compute).debugInfo.debuggable: + rdtest.log.print(f"Skipping undebuggable Compute shader at {action.eventId} for {csShaderModels[sm]}.") + rdtest.log.end_section(section) + continue + + # Loop over every test + for test in range(action.dispatchDimension[0]): + # Debug the shader + groupid = (test,0,0) + threadid = (0,0,0) + 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") + failed = True + continue + + # Result is stored in RWStructuredBuffer bufOut : register(u0); + bufOut = pipe.GetReadWriteResources(rd.ShaderStage.Compute)[0].descriptor.resource + bufdata = self.controller.GetBufferData(bufOut, test*16, 16) + realTestResult = struct.unpack_from("4f", bufdata, 0) + debugInfo = pipe.GetShaderReflection(rd.ShaderStage.Compute).debugInfo + shaderSrcRaw = debugInfo.files[0].contents + varsToCheck = self.parse_shader_source(shaderSrcRaw, realTestResult, test) + try: + for name, varType, expectedValue in varsToCheck: + debuggedValue = self.get_source_shader_var_value(trace.instInfo[-1].sourceVars, name, varType, variables) + if not rdtest.value_compare(expectedValue, debuggedValue): + raise rdtest.TestFailureException(f"'{name}' {varType} debugger {debuggedValue} doesn't match expected {expectedValue}") + rdtest.log.success(f"{len(varsToCheck)} source variables matched as expected") + + except rdtest.TestFailureException as ex: + rdtest.log.error(f"Test {test} failed {ex}") + failed = True + continue + finally: + self.controller.FreeTrace(trace) + + rdtest.log.success("Test {} matched as expected".format(test)) + + rdtest.log.end_section(section) + + if failed: + raise rdtest.TestFailureException("Some tests were not as expected") + + rdtest.log.success("All tests matched")