From fd550201658074d176292a2a7e4efb084c3df075 Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 9 Sep 2026 20:22:02 +0100 Subject: [PATCH] Add helper for checking vertex debugging matches postvs * There are a few 'extras' here as the uses aren't quite uniform, but they are close enough it makes sense to have a common helper --- util/test/rdtest/shared/Draw_Zoo.py | 37 +-------- util/test/rdtest/testcase.py | 78 +++++++++++++++++++ util/test/tests/GL/GL_Shader_Debug_Zoo.py | 61 ++------------- util/test/tests/Iter_Test.py | 60 +------------- .../test/tests/Vulkan/VK_Graphics_Pipeline.py | 46 +---------- .../tests/Vulkan/VK_KHR_Buffer_Address.py | 39 +--------- util/test/tests/Vulkan/VK_Multi_Entry.py | 46 +---------- util/test/tests/Vulkan/VK_Multi_View.py | 44 +---------- 8 files changed, 90 insertions(+), 321 deletions(-) diff --git a/util/test/rdtest/shared/Draw_Zoo.py b/util/test/rdtest/shared/Draw_Zoo.py index 8b59bb535..481d3dc60 100644 --- a/util/test/rdtest/shared/Draw_Zoo.py +++ b/util/test/rdtest/shared/Draw_Zoo.py @@ -121,7 +121,7 @@ class Draw_Zoo(rdtest.TestCase): idx = vsout_ref[vtx]['idx'] - self.check_debug(vtx, idx, inst, postvs) + self.check_vertex_debug(vtx, idx, inst, postvs) else: rdtest.log.print('Not checking shader debugging, unsupported') @@ -136,41 +136,6 @@ class Draw_Zoo(rdtest.TestCase): rdtest.log.success("Checked action {}".format(action.eventId)) - def check_debug(self, vtx, idx, inst, postvs): - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0) - - if trace.debugger is None: - self.controller.FreeTrace(trace) - - raise rdtest.TestFailureException("Couldn't debug vertex {} in instance {}".format(vtx, inst)) - - cycles, variables = self.process_trace(trace) - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[vtx].keys(): - raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) - - expect = postvs[vtx][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - raise rdtest.TestFailureException( - "Output {} at vert {} (idx {}) instance {} has different size ({} values) to expectation ({} values)" - .format(name, vtx, idx, inst, value.columns, len(expect))) - - debugged = value.value.f32v[0:value.columns] - - if not rdtest.value_compare(expect, debugged): - raise rdtest.TestFailureException( - "Debugged value {} at vert {} (idx {}) instance {}: {} doesn't exactly match postvs output {}".format( - name, vtx, idx, inst, debugged, expect)) - rdtest.log.success('Successfully debugged vertex {} in instance {}' - .format(vtx, inst)) - def check_capture(self): test_marker: rd.ActionDescription = self.find_action("Test") self.check_capture_action(test_marker) diff --git a/util/test/rdtest/testcase.py b/util/test/rdtest/testcase.py index 2141367bb..dc552dc57 100644 --- a/util/test/rdtest/testcase.py +++ b/util/test/rdtest/testcase.py @@ -536,6 +536,84 @@ class TestCase: log.success("Simple triangle is as expected") + def check_vertex_debug( + self, + vtx: int, + idx: int, + inst: int, + postvs: analyse.MeshData, + *, + view=-1, + eps=util.FLT_EPSILON, + single_postvs=False, + ignore_uninit=False, + name_retry: Callable[[str], str] | None = None + ): + trace = self.controller.DebugVertex(vtx, inst, idx, max(0, view)) + + ctx = f"vertex {vtx} (idx {idx}) instance {inst}" + if view >= 0: + ctx += f" view {view}" + + if trace.debugger is None: + self.controller.FreeTrace(trace) + + raise TestFailureException(f"Couldn't debug {ctx}") + + try: + cycles, variables = self.process_trace(trace) + + postvs_vtx = vtx + if single_postvs: + postvs_vtx = 0 + + for var in trace.sourceVars: + if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: + name = var.name + + if name not in postvs[postvs_vtx].keys() and name_retry is not None: + name = name_retry(name) + + if name not in postvs[postvs_vtx].keys(): + raise TestFailureException(f"Don't have expected output for {name}") + + expect = postvs[postvs_vtx][name] + assert expect is not None + value = self.evaluate_source_var(var, variables) + + expect_cols = 1 + if util.is_vector(expect): + expect_cols = len(expect) + if expect_cols != value.columns: + raise TestFailureException( + f"Output {name} at {ctx} has different size ({value.columns} values) to expectation ({expect_cols} values)") + + compType = rd.VarTypeCompType(value.type) + debugged: util.VectorValue = [] + if compType == rd.CompType.UInt: + debugged = list(value.value.u32v[0:value.columns]) + elif compType == rd.CompType.SInt: + debugged = list(value.value.s32v[0:value.columns]) + else: + debugged = list(value.value.f32v[0:value.columns]) + + # For now, ignore debugged values that are uninitialised. This is an application bug but it causes false + # reports of problems + if ignore_uninit and value.columns > 1: + assert util.is_vector(expect) + for comp in range(4): + if value.value.u32v[comp] == 0xcccccccc: + debugged[comp] = expect[comp] + + is_eq, diff_amt = util.value_compare_diff(expect, debugged, eps=5.0E-06) + if not is_eq: + raise TestFailureException( + f"Debugged value {name} at {ctx}: {debugged} doesn't exactly match postvs output {expect}. {diff_amt} difference") + + log.success(f'Successfully debugged vertex {ctx} in {cycles} cycles') + finally: + self.controller.FreeTrace(trace) + def run(self): self.capture_filename = self.get_capture() diff --git a/util/test/tests/GL/GL_Shader_Debug_Zoo.py b/util/test/tests/GL/GL_Shader_Debug_Zoo.py index f202ae2fb..36bee6257 100644 --- a/util/test/tests/GL/GL_Shader_Debug_Zoo.py +++ b/util/test/tests/GL/GL_Shader_Debug_Zoo.py @@ -97,65 +97,14 @@ class GL_Shader_Debug_Zoo(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, first_index=vtx, num_indices=1, instance=inst) - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0) - - if trace.debugger is None: + try: + self.check_vertex_debug(vtx, idx, inst, postvs, single_postvs=True, name_retry = lambda x: x.replace(".", "Block.")) + except rdtest.TestFailureException as err: failed = True - rdtest.log.error("Test {} in sub-section {} did not debug vertex".format(test, child)) - self.controller.FreeTrace(trace) + rdtest.log.error(f"Error debugging vertex at test {test} in sub-section {child}: {err.message}") continue - _, variables = self.process_trace(trace) - - outputs = 0 - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[0].keys(): - name = name.replace(".", "Block.") - if name not in postvs[0].keys(): - failed = True - rdtest.log.error("Don't have expected output for {}".format(name)) - continue - - expect = postvs[0][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - failed = True - rdtest.log.error( - "Output {} at EID {} has different size ({} values) to expectation ({} values)" - .format(name, action.eventId, value.columns, len(expect))) - continue - - compType = rd.VarTypeCompType(value.type) - if compType == rd.CompType.UInt: - debugged = list(value.value.u32v[0:value.columns]) - elif compType == rd.CompType.SInt: - debugged = list(value.value.s32v[0:value.columns]) - else: - debugged = list(value.value.f32v[0:value.columns]) - - if not rdtest.value_compare(expect, debugged): - failed = True - rdtest.log.error("Test {} in sub-section {} did not match vertex.\nExpected {} but got {}".format(test, child, expect, debugged)) - break - - is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06) - if not is_eq: - failed = True - rdtest.log.error( - "Debugged value {} at EID {} vert {} (idx {}) instance {}: {} difference. {} doesn't exactly match postvs output {}".format( - name, action.eventId, vtx, idx, inst, diff_amt, debugged, expect)) - - outputs = outputs + 1 - - self.controller.FreeTrace(trace) - - rdtest.log.success("Test {} vertex in sub-section {} matched as expected".format(test, child)) + rdtest.log.success(f"Test {test} vertex in sub-section {child} matched as expected") rdtest.log.end_section(child) diff --git a/util/test/tests/Iter_Test.py b/util/test/tests/Iter_Test.py index 1ee4abc72..d651456e6 100644 --- a/util/test/tests/Iter_Test.py +++ b/util/test/tests/Iter_Test.py @@ -152,70 +152,12 @@ class Iter_Test(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, first_index=vtx, num_indices=1, instance=inst) - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0) - - if trace.debugger is None: - self.controller.FreeTrace(trace) - - rdtest.log.print("No debug result") - return - try: - cycles, variables = self.process_trace(trace) + self.check_vertex_debug(vtx, idx, inst, postvs, eps=5.0E-06, single_postvs=True, ignore_uninit=True) except rdtest.TestFailureException as err: rdtest.log.error(f"Error debugging: {err.message}") return - outputs = 0 - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[0].keys(): - rdtest.log.error("Don't have expected output for {}".format(name)) - continue - - expect = postvs[0][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - rdtest.log.error( - "Output {} at EID {} has different size ({} values) to expectation ({} values)" - .format(name, action.eventId, value.columns, len(expect))) - continue - - compType = rd.VarTypeCompType(value.type) - if compType == rd.CompType.UInt: - debugged = list(value.value.u32v[0:value.columns]) - elif compType == rd.CompType.SInt: - debugged = list(value.value.s32v[0:value.columns]) - else: - debugged = list(value.value.f32v[0:value.columns]) - - # For now, ignore debugged values that are uninitialised. This is an application bug but it causes false - # reports of problems - for comp in range(4): - if value.value.u32v[comp] == 0xcccccccc: - debugged[comp] = expect[comp] - - # Unfortunately we can't ever trust that we should get back a matching results, because some shaders - # rely on undefined/inaccurate maths that we don't emulate. - # So the best we can do is log an error for manual verification - is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06) - if not is_eq: - rdtest.log.error( - "Debugged value {} at EID {} vert {} (idx {}) instance {}: {} difference. {} doesn't exactly match postvs output {}".format( - name, action.eventId, vtx, idx, inst, diff_amt, debugged, expect)) - - outputs = outputs + 1 - - rdtest.log.success('Successfully debugged vertex in {} cycles, {}/{} outputs match' - .format(cycles, outputs, len(refl.outputSignature))) - - self.controller.FreeTrace(trace) - def pixel_debug(self, action: rd.ActionDescription): pipe: rd.PipeState = self.controller.GetPipelineState() diff --git a/util/test/tests/Vulkan/VK_Graphics_Pipeline.py b/util/test/tests/Vulkan/VK_Graphics_Pipeline.py index 69bc2a497..255d05598 100644 --- a/util/test/tests/Vulkan/VK_Graphics_Pipeline.py +++ b/util/test/tests/Vulkan/VK_Graphics_Pipeline.py @@ -76,51 +76,7 @@ class VK_Graphics_Pipeline(rdtest.TestCase): raise rdtest.TestFailureException( f"Graphics bind 0[15] isn't the accessed descriptor {str(rd.DumpObject(access))}") - trace = self.controller.DebugVertex(0, 0, 0, 0) - - if trace.debugger is None: - raise rdtest.TestFailureException("No vertex debug result") - - cycles, variables = self.process_trace(trace) - - outputs = 0 - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs_data[0].keys(): - raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) - - expect = postvs_data[0][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - raise rdtest.TestFailureException( - "Vertex output {} has different size ({} values) to expectation ({} values)".format( - name, action.eventId, value.columns, len(expect))) - - compType = rd.VarTypeCompType(value.type) - if compType == rd.CompType.UInt: - debugged = list(value.value.u32v[0:value.columns]) - elif compType == rd.CompType.SInt: - debugged = list(value.value.s32v[0:value.columns]) - else: - debugged = list(value.value.f32v[0:value.columns]) - - is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06) - if not is_eq: - rdtest.log.error( - "Debugged vertex output value {}: {} difference. {} doesn't exactly match postvs output {}". - format(name, action.eventId, diff_amt, debugged, expect)) - - outputs = outputs + 1 - - rdtest.log.success('Successfully debugged vertex in {} cycles, {}/{} outputs match'.format( - cycles, outputs, len(vsrefl.outputSignature))) - - self.controller.FreeTrace(trace) + self.check_vertex_debug(0, 0, 0, postvs_data) history = self.controller.PixelHistory(pipe.GetOutputTargets()[0].resource, 200, 150, rd.Subresource(0, 0, 0), rd.CompType.Typeless) diff --git a/util/test/tests/Vulkan/VK_KHR_Buffer_Address.py b/util/test/tests/Vulkan/VK_KHR_Buffer_Address.py index bd15983e5..45d75aa7f 100644 --- a/util/test/tests/Vulkan/VK_KHR_Buffer_Address.py +++ b/util/test/tests/Vulkan/VK_KHR_Buffer_Address.py @@ -42,43 +42,6 @@ class VK_KHR_Buffer_Address(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, instance=inst) for vtx in range(action.numIndices): idx = vtx - self.check_debug(vtx, idx, inst, postvs) + self.check_vertex_debug(vtx, idx, inst, postvs) rdtest.log.success("All tests matched") - - - def check_debug(self, vtx, idx, inst, postvs): - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, 0) - - if trace.debugger is None: - self.controller.FreeTrace(trace) - - raise rdtest.TestFailureException("Couldn't debug vertex {} in instance {}".format(vtx, inst)) - - cycles, variables = self.process_trace(trace) - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[vtx].keys(): - raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) - - expect = postvs[vtx][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - raise rdtest.TestFailureException( - "Output {} at vert {} (idx {}) instance {} has different size ({} values) to expectation ({} values)" - .format(name, vtx, idx, inst, value.columns, len(expect))) - - debugged = value.value.f32v[0:value.columns] - - if not rdtest.value_compare(expect, debugged): - raise rdtest.TestFailureException( - "Debugged value {} at vert {} (idx {}) instance {}: {} doesn't exactly match postvs output {}".format( - name, vtx, idx, inst, debugged, expect)) - rdtest.log.success('Successfully debugged vertex {} in instance {}' - .format(vtx, inst)) - diff --git a/util/test/tests/Vulkan/VK_Multi_Entry.py b/util/test/tests/Vulkan/VK_Multi_Entry.py index fb2aaaf80..1a07bb2af 100644 --- a/util/test/tests/Vulkan/VK_Multi_Entry.py +++ b/util/test/tests/Vulkan/VK_Multi_Entry.py @@ -36,51 +36,7 @@ class VK_Multi_Entry(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, first_index=0, num_indices=1, instance=0) - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(0, 0, 0, 0) - - if trace.debugger is None: - raise rdtest.TestFailureException("No vertex debug result") - - cycles, variables = self.process_trace(trace) - - outputs = 0 - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[0].keys(): - raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) - - expect = postvs[0][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - raise rdtest.TestFailureException( - "Vertex output {} has different size ({} values) to expectation ({} values)".format( - name, action.eventId, value.columns, len(expect))) - - compType = rd.VarTypeCompType(value.type) - if compType == rd.CompType.UInt: - debugged = list(value.value.u32v[0:value.columns]) - elif compType == rd.CompType.SInt: - debugged = list(value.value.s32v[0:value.columns]) - else: - debugged = list(value.value.f32v[0:value.columns]) - - is_eq, diff_amt = rdtest.value_compare_diff(expect, debugged, eps=5.0E-06) - if not is_eq: - rdtest.log.error( - "Debugged vertex output value {}: {} difference. {} doesn't exactly match postvs output {}". - format(name, action.eventId, diff_amt, debugged, expect)) - - outputs = outputs + 1 - - rdtest.log.success('Successfully debugged vertex in {} cycles, {}/{} outputs match'.format( - cycles, outputs, len(refl.outputSignature))) - - self.controller.FreeTrace(trace) + self.check_vertex_debug(0, 0, 0, postvs, single_postvs=True) history = self.controller.PixelHistory(pipe.GetOutputTargets()[0].resource, 200, 150, rd.Subresource(0, 0, 0), rd.CompType.Typeless) diff --git a/util/test/tests/Vulkan/VK_Multi_View.py b/util/test/tests/Vulkan/VK_Multi_View.py index f8cb14e3d..2507ffcf2 100644 --- a/util/test/tests/Vulkan/VK_Multi_View.py +++ b/util/test/tests/Vulkan/VK_Multi_View.py @@ -45,7 +45,7 @@ class VK_Multi_View(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, instance=inst, view=view) for vtx in range(action.numIndices): idx = vtx - self.check_debug(vtx, idx, inst, view, postvs) + self.check_vertex_debug(vtx, idx, inst, postvs, view=view) rdtest.log.print(f"View {view} Slice {slice} passed") for test_name in ["viewportIndex choice"]: @@ -86,49 +86,9 @@ class VK_Multi_View(rdtest.TestCase): postvs = self.get_postvs(action, rd.MeshDataStage.VSOut, instance=inst, view=view) for vtx in range(action.numIndices): idx = vtx - self.check_debug(vtx, idx, inst, view, postvs) + self.check_vertex_debug(vtx, idx, inst, postvs, view=view) rdtest.log.print(f"View {view} Slice {slice} passed") rdtest.log.success("All tests matched") - def check_debug(self, vtx, idx, inst, view, postvs): - trace: rd.ShaderDebugTrace = self.controller.DebugVertex(vtx, inst, idx, view) - - if trace.debugger is None: - self.controller.FreeTrace(trace) - - raise rdtest.TestFailureException("Couldn't debug vertex {} in instance {} for view {}".format(vtx, inst, view)) - - cycles, variables = self.process_trace(trace) - - for var in trace.sourceVars: - var: rd.SourceVariableMapping - if var.variables[0].type == rd.DebugVariableType.Variable and var.signatureIndex >= 0: - name = var.name - - if name not in postvs[vtx].keys(): - raise rdtest.TestFailureException("Don't have expected output for {}".format(name)) - - expect = postvs[vtx][name] - value = self.evaluate_source_var(var, variables) - - if len(expect) != value.columns: - raise rdtest.TestFailureException( - "Output {} at vert {} (idx {}) instance {} view {} has different size ({} values) to expectation ({} values)" - .format(name, vtx, idx, inst, view, value.columns, len(expect))) - - if value.type == rd.VarType.SInt: - debugged = value.value.s32v[0:value.columns] - elif value.type == rd.VarType.UInt: - debugged = value.value.u32v[0:value.columns] - else: - debugged = value.value.f32v[0:value.columns] - - if not rdtest.value_compare(expect, debugged): - raise rdtest.TestFailureException( - "Debugged value {} at vert {} (idx {}) instance {} view {}: {} doesn't exactly match postvs output {}".format( - name, vtx, idx, inst, view, debugged, expect)) - rdtest.log.success('Successfully debugged vertex {} in instance {} for view {}' - .format(vtx, inst, view)) -