import renderdoc as rd
import rdtest

class VK_Multi_View(rdtest.TestCase):
    demos_test_name = 'VK_Multi_View'

    def check_capture(self):
        if not self.controller.GetAPIProperties().shaderDebugging:
            rdtest.log.success("Shader debugging not enabled, skipping test")
            return

        x = 200
        y = 150

        for test_name in ["Vertex: viewIndex", "Geometry: viewIndex", "Fragment: viewIndex", "No viewIndex"]:
            rdtest.log.print("Test {}".format(test_name))
            label = self.find_action(test_name)
            if label is None:
                continue
            action = label.next
            self.controller.SetFrameEvent(action.eventId, True)

            pipe: rd.PipeState = self.controller.GetPipelineState()
            if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable:
                raise rdtest.TestFailureException("Test {} shader can not be debugged".format(test_name))

            for view in range(2):
                # Debug the pixel shader
                inputs = rd.DebugPixelInputs()
                inputs.view = view
                trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs)
                if trace.debugger is None:
                    self.controller.FreeTrace(trace)
                    raise rdtest.TestFailureException("Test {} view {} did not debug at all".format(test_name, view))

                cycles, variables = self.process_trace(trace)
                output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0)
                debugged = self.evaluate_source_var(output, variables)
                slice = view + 1
                sub = rd.Subresource(0, slice, 0)
                self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4], sub=sub)
                self.controller.FreeTrace(trace)

                inst = 0
                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)
                rdtest.log.print(f"View {view} Slice {slice} passed")

        for test_name in ["viewportIndex choice"]:
            rdtest.log.print("Test {}".format(test_name))
            label = self.find_action(test_name)
            if label is None:
                continue
            action = label.next
            self.controller.SetFrameEvent(action.eventId, True)

            pipe: rd.PipeState = self.controller.GetPipelineState()
            if not pipe.GetShaderReflection(rd.ShaderStage.Pixel).debugInfo.debuggable:
                raise rdtest.TestFailureException("Test {} shader can not be debugged".format(test_name))

            for view in range(2):
                if view == 0:
                    x, y = 100, 140
                else:
                    x, y = 300, 140

                # Debug the pixel shader
                inputs = rd.DebugPixelInputs()
                inputs.view = view
                trace: rd.ShaderDebugTrace = self.controller.DebugPixel(x, y, inputs)
                if trace.debugger is None:
                    self.controller.FreeTrace(trace)
                    raise rdtest.TestFailureException("Test {} view {} did not debug at all".format(test_name, view))

                cycles, variables = self.process_trace(trace)
                output: rd.SourceVariableMapping = self.find_output_source_var(trace, rd.ShaderBuiltin.ColorOutput, 0)
                debugged = self.evaluate_source_var(output, variables)
                slice = view + 1
                sub = rd.Subresource(0, slice, 0)
                self.check_pixel_value(pipe.GetOutputTargets()[0].resource, x, y, debugged.value.f32v[0:4], sub=sub)
                self.controller.FreeTrace(trace)

                inst = 0
                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)
                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))

