Example #1
0
    def check_capture(self):
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        for base_event_name in ["sm5.0", "sm5.1", "sm6.0"]:
            base = self.find_action(base_event_name)

            if base is None:
                continue

            base_event = base.eventId

            rdtest.log.print("Checking tests on {}".format(base_event_name))

            super(D3D12_Overlay_Test, self).check_capture(base_event)

            rdtest.log.success(
                "Base tests worked on {}".format(base_event_name))

            # Don't check any pixel values, but ensure all overlays at least work with no viewport/scissor bound
            sub_marker: rd.ActionDescription = self.find_action(
                "NoView draw", base_event)
            self.controller.SetFrameEvent(sub_marker.next.eventId, True)

            pipe: rd.PipeState = self.controller.GetPipelineState()

            tex = rd.TextureDisplay()
            tex.resourceId = pipe.GetOutputTargets()[0].resourceId

            for overlay in rd.DebugOverlay:
                if overlay == rd.DebugOverlay.NoOverlay:
                    continue

                # These overlays are just displaymodes really, not actually separate overlays
                if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                    continue

                if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                    continue

                rdtest.log.success(
                    "Checking overlay {} with no viewport/scissor".format(
                        str(overlay)))

                tex.overlay = overlay
                out.SetTextureDisplay(tex)

                out.Display()

                overlay_id: rd.ResourceId = out.GetDebugOverlayTexID()

                rdtest.log.success(
                    "Overlay {} rendered with no viewport/scissor".format(
                        str(overlay)))

            rdtest.log.success(
                "extended tests worked on {}".format(base_event_name))

        out.Shutdown()
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2), int(texdetails.height / 2), 0, 0, 0)

        if not rdtest.value_compare(picked.floatValue, [1.0, 0.0, 0.0, 1.0]):
            raise rdtest.TestFailureException("Picked value {} doesn't match expectation".format(picked.floatValue))

        rdtest.log.success("picked value is as expected")

        out.Shutdown()
Example #3
0
    def check_pixel_value(self,
                          tex: rd.ResourceId,
                          x,
                          y,
                          value,
                          eps=util.FLT_EPSILON):
        tex_details = self.get_texture(tex)
        res_details = self.get_resource(tex)

        if type(x) is float:
            x = int(tex_details.width * x)
        if type(y) is float:
            y = int(tex_details.height * y)

        if self.pickout is None:
            self.pickout: rd.ReplayOutput = self.controller.CreateOutput(
                rd.CreateHeadlessWindowingData(100, 100),
                rd.ReplayOutputType.Texture)
            self.check(self.pickout is not None)

        texdisplay = rd.TextureDisplay()
        texdisplay.resourceId = tex
        self.pickout.SetTextureDisplay(texdisplay)

        picked: rd.PixelValue = self.pickout.PickPixel(tex, False, x, y, 0, 0,
                                                       0)

        if not util.value_compare(picked.floatValue, value, eps):
            raise TestFailureException(
                "Picked value {} at {},{} doesn't match expectation of {}".
                format(picked.floatValue, x, y, value))

        log.success("Picked value at {},{} in {} is as expected".format(
            x, y, res_details.name))
Example #4
0
    def check_capture(self):
        rdtest.log.success("Got {} captures as expected".format(
            self.demos_frame_count))

        draw = self.find_draw("Draw")
        self.check(draw is not None)
        self.controller.SetFrameEvent(draw.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.overlay = rd.DebugOverlay.Drawcall
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        out.SetTextureDisplay(tex)

        out.Display()

        overlay_id = out.GetDebugOverlayTexID()

        v = pipe.GetViewport(0)

        self.check_pixel_value(overlay_id,
                               int(0.5 * v.width),
                               int(0.5 * v.height), [0.8, 0.1, 0.8, 1.0],
                               eps=1.0 / 256.0)

        out.Shutdown()

        rdtest.log.success("Overlay color is as expected")
Example #5
0
    def check_capture(self):
        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        # find the first draw
        draw = self.find_draw("Draw")

        # check the centre pixel of the viewport is white
        self.controller.SetFrameEvent(draw.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        view: rd.Viewport = pipe.GetViewport(0)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(view.width / 2),
                                              int(view.height / 2), 0, 0, 0)

        if not rdtest.value_compare(picked.floatValue, [1.0, 1.0, 1.0, 1.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Picked value for first draw is as expected")

        # find the second draw
        draw = self.find_draw("Draw", draw.eventId + 1)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        view: rd.Viewport = pipe.GetViewport(0)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(view.width / 2),
                                              int(view.height / 2), 0, 0, 0)

        if not rdtest.value_compare(picked.floatValue, [1.0, 1.0, 1.0, 1.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Picked value for second draw is as expected")
Example #6
0
    def check_capture(self):
        apiprops: rd.APIProperties = self.controller.GetAPIProperties()

        if not apiprops.rgpCapture:
            rdtest.log.print("RGP capture not tested")
            return

        path = self.controller.CreateRGPProfile(rd.CreateHeadlessWindowingData(100, 100))

        if os.path.exists(path) and os.path.getsize(path) > 100:
            rdtest.log.success("RGP capture created successfully")
        else:
            raise rdtest.TestFailureException("RGP capture failed")
Example #7
0
    def check_capture(self):
        super(VK_Overlay_Test, self).check_capture()

        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        # Don't check any pixel values, but ensure all overlays at least work with rasterizer discard and no
        # viewport/scissor bound
        sub_marker: rd.DrawcallDescription = self.find_draw("Discard Test")
        self.controller.SetFrameEvent(sub_marker.next.eventId, True)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        for overlay in rd.DebugOverlay:
            if overlay == rd.DebugOverlay.NoOverlay:
                continue

            # These overlays are just displaymodes really, not actually separate overlays
            if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                continue

            if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                continue

            rdtest.log.success(
                "Checking overlay {} with rasterizer discard".format(
                    str(overlay)))

            tex.overlay = overlay
            out.SetTextureDisplay(tex)

            out.Display()

            overlay_id: rd.ResourceId = out.GetDebugOverlayTexID()

            rdtest.log.success(
                "Overlay {} rendered with rasterizer discard".format(
                    str(overlay)))

        out.Shutdown()
    def check_capture(self):
        # Jump to the draw
        draw = self.find_draw("Draw")

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        # Loop over every test
        for test in range(draw.numInstances):
            # Pick the pixel
            picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                                  4 * test, 0, 0, 0, 0)

            # Debug the shader
            trace: rd.ShaderDebugTrace = self.controller.DebugPixel(
                4 * test, 0, rd.ReplayController.NoPreference,
                rd.ReplayController.NoPreference)

            last_state: rd.ShaderDebugState = trace.states[-1]

            if not rdtest.value_compare(picked.floatValue,
                                        last_state.outputs[0].value.fv[0:4]):
                raise rdtest.TestFailureException(
                    "Test {}: debugged output {} doesn't match actual output {}"
                    .format(test, last_state.outputs[0].value.fv[0:4],
                            picked.floatValue))

            rdtest.log.success("Test {} matched as expected".format(test))

        rdtest.log.success("All tests matched")
Example #9
0
    def check_capture(self, capture_filename: str,
                      controller: rd.ReplayController):
        self.controller = controller

        self.pipeType = self.controller.GetAPIProperties().pipelineType
        self.opengl_mode = (self.controller.GetAPIProperties().pipelineType ==
                            rd.GraphicsAPI.OpenGL)
        self.d3d_mode = rd.IsD3D(
            self.controller.GetAPIProperties().pipelineType)

        failed = False

        try:
            # First check with the local controller
            self.check_capture_with_controller('')
        except rdtest.TestFailureException as ex:
            rdtest.log.error(str(ex))
            failed = True

        # Now shut it down
        self.controller.Shutdown()
        self.controller = None

        # Launch a remote server
        rdtest.launch_remote_server()

        # Wait for it to start
        time.sleep(0.5)

        ret: Tuple[rd.ResultCode,
                   rd.RemoteServer] = rd.CreateRemoteServerConnection(
                       'localhost')
        result, remote = ret

        if result != rd.ResultCode.Succeeded:
            time.sleep(2)

            ret: Tuple[rd.ResultCode,
                       rd.RemoteServer] = rd.CreateRemoteServerConnection(
                           'localhost')
            result, remote = ret

        if result != rd.ResultCode.Succeeded:
            raise rdtest.TestFailureException(
                "Couldn't connect to remote server: {}".format(str(result)))

        proxies = remote.LocalProxies()

        try:
            # Try D3D11 and GL as proxies, D3D12/Vulkan technically don't have proxying implemented even though they
            # will be listed in proxies
            for api in ['D3D11', 'OpenGL']:
                if api not in proxies:
                    continue

                try:
                    ret: Tuple[rd.ResultCode,
                               rd.ReplayController] = remote.OpenCapture(
                                   proxies.index(api), capture_filename,
                                   rd.ReplayOptions(), None)
                    result, self.controller = ret

                    # Now check with the proxy
                    self.check_capture_with_controller(api)
                except ValueError:
                    continue
                except rdtest.TestFailureException as ex:
                    rdtest.log.error(str(ex))
                    failed = True
                finally:
                    remote.CloseCapture(self.controller)
                    self.controller = None
        finally:
            remote.ShutdownServerAndConnection()

        # Now iterate over all the temp images saved out, load them as captures, and check the texture.
        dir_path = rdtest.get_tmp_path('')

        was_opengl = self.opengl_mode

        # We iterate in filename order, so that dds files get opened before png files.
        for file in os.scandir(dir_path):
            if '.dds' not in file.name and '.png' not in file.name:
                continue

            cap = rd.OpenCaptureFile()
            result = cap.OpenFile(file.path, 'rdc', None)

            if result != rd.ResultCode.Succeeded:
                rdtest.log.error("Couldn't open {}".format(file.name))
                failed = True
                continue

            ret: Tuple[rd.ResultCode, rd.ReplayController] = cap.OpenCapture(
                rd.ReplayOptions(), None)
            result, self.controller = ret

            if result != rd.ResultCode.Succeeded:
                rdtest.log.error("Couldn't open {}".format(file.name))
                failed = True
                continue

            self.filename = file.name.replace('.dds', '').replace('.png', '')

            [a, b] = file.name.replace('.dds',
                                       ' (DDS)').replace('.png',
                                                         ' (PNG)').split('@')

            self.controller.SetFrameEvent(
                self.controller.GetRootActions()[0].eventId, True)

            try:
                self.opengl_mode = False
                fmt: rd.ResourceFormat = self.controller.GetTextures(
                )[0].format

                is_compressed = (rd.ResourceFormatType.BC1 <= fmt.type <=
                                 rd.ResourceFormatType.BC7
                                 or fmt.type == rd.ResourceFormatType.EAC
                                 or fmt.type == rd.ResourceFormatType.ETC2
                                 or fmt.type == rd.ResourceFormatType.ASTC
                                 or fmt.type == rd.ResourceFormatType.PVRTC)

                # OpenGL saves all non-compressed images to disk with a flip, since that's the expected order for
                # most formats. The effect of this is that we should apply the opengl_mode workaround for all files
                # *except* compressed textures
                if was_opengl and not is_compressed:
                    self.opengl_mode = True

                self.out: rd.ReplayOutput = self.controller.CreateOutput(
                    rd.CreateHeadlessWindowingData(100, 100),
                    rd.ReplayOutputType.Texture)

                self.check_test(
                    a, b, Texture_Zoo.TEST_DDS
                    if '.dds' in file.name else Texture_Zoo.TEST_PNG)

                self.out.Shutdown()
                self.out = None

                rdtest.log.success("{} loaded with the correct data".format(
                    file.name))
            except rdtest.TestFailureException as ex:
                rdtest.log.error(str(ex))
                failed = True

            self.controller.Shutdown()
            self.controller = None

        if failed:
            raise rdtest.TestFailureException(
                "Some tests were not as expected")
Example #10
0
    def check_capture_with_controller(self, proxy_api: str):
        self.controller: rd.ReplayController
        any_failed = False

        if proxy_api != '':
            rdtest.log.print('Running with {} local proxy'.format(proxy_api))
            self.proxied = True
        else:
            rdtest.log.print('Running on direct replay')
            self.proxied = False

        self.out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        for d in self.controller.GetRootActions():
            if 'slice tests' in d.customName:
                for sub in d.children:
                    if sub.flags & rd.ActionFlags.Drawcall:
                        self.controller.SetFrameEvent(sub.eventId, True)

                        pipe = self.controller.GetPipelineState()

                        tex_id = pipe.GetReadOnlyResources(
                            rd.ShaderStage.Pixel)[0].resources[0].resourceId

                        for mip in [0, 1]:
                            for sl in [16, 17, 18]:
                                expected = [0.0, 0.0, 1.0, 1.0]
                                if sl == 17:
                                    expected = [0.0, 1.0, 0.0, 1.0]

                                cur_sub = rd.Subresource(mip, sl)
                                comp_type = rd.CompType.Typeless

                                # test that pixel picking sees the right things
                                picked = self.controller.PickPixel(
                                    tex_id, 15, 15, cur_sub, comp_type)

                                if not rdtest.value_compare(
                                        picked.floatValue, expected):
                                    raise rdtest.TestFailureException(
                                        "Expected to pick {} at slice {} mip {}, got {}"
                                        .format(expected, sl, mip,
                                                picked.floatValue))

                                rdtest.log.success(
                                    'Picked pixel is correct at slice {} mip {}'
                                    .format(sl, mip))

                                # Render output texture a three scales - below 100%, 100%, above 100%
                                tex_display = rd.TextureDisplay()
                                tex_display.resourceId = tex_id
                                tex_display.subresource = cur_sub
                                tex_display.typeCast = comp_type

                                # convert the unorm values to byte values for comparison
                                expected = [
                                    int(a * 255) for a in expected[0:3]
                                ]

                                for scale in [0.9, 1.0, 1.1]:
                                    tex_display.scale = scale
                                    self.out.SetTextureDisplay(tex_display)
                                    self.out.Display()
                                    pixels: bytes = self.out.ReadbackOutputTexture(
                                    )

                                    actual = [int(a) for a in pixels[0:3]]

                                    if not rdtest.value_compare(
                                            actual, expected):
                                        raise rdtest.TestFailureException(
                                            "Expected to display {} at slice {} mip {} scale {}%, got {}"
                                            .format(expected, sl, mip,
                                                    int(scale * 100), actual))

                                    rdtest.log.success(
                                        'Displayed pixel is correct at scale {}% in slice {} mip {}'
                                        .format(int(scale * 100), sl, mip))
                    elif sub.flags & rd.ActionFlags.SetMarker:
                        rdtest.log.print(
                            'Checking {} for slice display'.format(
                                sub.customName))

                continue

            # Check each region for the tests within
            if d.flags & rd.ActionFlags.PushMarker:
                name = ''
                tests_run = 0

                failed = False

                # Iterate over actions in this region
                for sub in d.children:
                    sub: rd.ActionDescription

                    if sub.flags & rd.ActionFlags.SetMarker:
                        name = sub.customName

                    # Check this action
                    if sub.flags & rd.ActionFlags.Drawcall:
                        tests_run = tests_run + 1
                        try:
                            # Set this event as current
                            self.controller.SetFrameEvent(sub.eventId, True)

                            self.filename = (d.customName + '@' +
                                             name).replace('->', '_')

                            self.check_test(d.customName, name,
                                            Texture_Zoo.TEST_CAPTURE)
                        except rdtest.TestFailureException as ex:
                            failed = any_failed = True
                            rdtest.log.error(str(ex))

                if not failed:
                    rdtest.log.success(
                        "All {} texture tests for {} are OK".format(
                            tests_run, d.customName))

        self.out.Shutdown()
        self.out = None

        if not any_failed:
            if proxy_api != '':
                rdtest.log.success(
                    'All textures are OK with {} as local proxy'.format(
                        proxy_api))
            else:
                rdtest.log.success("All textures are OK on direct replay")
        else:
            raise rdtest.TestFailureException(
                "Some tests were not as expected")
Example #11
0
    def check_capture(self):
        self.check_final_backbuffer()

        for level in ["Primary", "Secondary"]:
            rdtest.log.print("Checking {} indirect calls".format(level))

            dispatches = self.find_draw("{}: Dispatches".format(level))

            # Set up a ReplayOutput and TextureSave for quickly testing the drawcall highlight overlay
            out: rd.ReplayOutput = self.controller.CreateOutput(
                rd.CreateHeadlessWindowingData(), rd.ReplayOutputType.Texture)

            self.check(out is not None)

            out.SetDimensions(100, 100)

            tex = rd.TextureDisplay()
            tex.overlay = rd.DebugOverlay.Drawcall

            save_data = rd.TextureSave()
            save_data.destType = rd.FileType.PNG

            # Rewind to the start of the capture
            draw: rd.DrawcallDescription = dispatches.children[0]
            while draw.previous is not None:
                draw = draw.previous

            # Ensure we can select all draws
            while draw is not None:
                self.controller.SetFrameEvent(draw.eventId, False)
                draw = draw.next

            rdtest.log.success("Selected all {} draws".format(level))

            self.check(dispatches and len(dispatches.children) == 3)

            self.check(dispatches.children[0].dispatchDimension == [0, 0, 0])
            self.check(dispatches.children[1].dispatchDimension == [1, 1, 1])
            self.check(dispatches.children[2].dispatchDimension == [3, 4, 5])

            rdtest.log.success(
                "{} Indirect dispatches are the correct dimensions".format(
                    level))

            self.controller.SetFrameEvent(dispatches.children[2].eventId,
                                          False)

            pipe: rd.PipeState = self.controller.GetPipelineState()

            ssbo: rd.BoundResource = pipe.GetReadWriteResources(
                rd.ShaderStage.Compute)[0].resources[0]
            data: bytes = self.controller.GetBufferData(ssbo.resourceId, 0, 0)

            rdtest.log.print("Got {} bytes of uints".format(len(data)))

            uints = [
                struct.unpack_from('=4L', data, offs)
                for offs in range(0, len(data), 16)
            ]

            for x in range(0, 6):  # 3 groups of 2 threads each
                for y in range(0, 8):  # 3 groups of 2 threads each
                    for z in range(0, 5):  # 5 groups of 1 thread each
                        idx = 100 + z * 8 * 6 + y * 6 + x
                        if not rdtest.value_compare(uints[idx],
                                                    [x, y, z, 12345]):
                            raise rdtest.TestFailureException(
                                'expected thread index data @ {},{},{}: {} is not as expected: {}'
                                .format(x, y, z, uints[idx], [x, y, z, 12345]))

            rdtest.log.success(
                "Dispatched buffer contents are as expected for {}".format(
                    level))

            empties = self.find_draw("{}: Empty draws".format(level))

            self.check(empties and len(empties.children) == 2)

            draw: rd.DrawcallDescription
            for draw in empties.children:
                self.check(draw.numIndices == 0)
                self.check(draw.numInstances == 0)

                self.controller.SetFrameEvent(draw.eventId, False)

                # Check that we have empty PostVS
                postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, 1)
                self.check(len(postvs_data) == 0)

                self.check_overlay(draw.eventId, out, tex, save_data)

            rdtest.log.success("{} empty draws are empty".format(level))

            indirects = self.find_draw("{}: Indirect draws".format(level))

            self.check('vkCmdDrawIndirect' in indirects.children[0].name)
            self.check(
                'vkCmdDrawIndexedIndirect' in indirects.children[1].name)
            self.check(len(indirects.children[1].children) == 2)

            rdtest.log.success(
                "Correct number of {} indirect draws".format(level))

            # vkCmdDrawIndirect(...)
            draw = indirects.children[0]
            self.check(draw.numIndices == 3)
            self.check(draw.numInstances == 2)

            self.controller.SetFrameEvent(draw.eventId, False)

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 0,
                    'gl_PerVertex.gl_Position': [-0.8, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 1,
                    'gl_PerVertex.gl_Position': [-0.7, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 2,
                    'gl_PerVertex.gl_Position': [-0.6, -0.5, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay(draw.eventId, out, tex, save_data)

            rdtest.log.success("{} {} is as expected".format(level, draw.name))

            # vkCmdDrawIndexedIndirect[0](...)
            draw = indirects.children[1].children[0]
            self.check(draw.numIndices == 3)
            self.check(draw.numInstances == 3)

            self.controller.SetFrameEvent(draw.eventId, False)

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

            # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
            # indices
            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 0,
                    'gl_PerVertex.gl_Position': [-0.6, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 1,
                    'gl_PerVertex.gl_Position': [-0.5, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 2,
                    'gl_PerVertex.gl_Position': [-0.4, -0.5, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay(draw.eventId, out, tex, save_data)

            rdtest.log.success("{} {} is as expected".format(level, draw.name))

            # vkCmdDrawIndexedIndirect[1](...)
            draw = indirects.children[1].children[1]
            self.check(draw.numIndices == 6)
            self.check(draw.numInstances == 2)

            self.controller.SetFrameEvent(draw.eventId, False)

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 0,
                    'gl_PerVertex.gl_Position': [-0.4, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 1,
                    'gl_PerVertex.gl_Position': [-0.3, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 2,
                    'gl_PerVertex.gl_Position': [-0.2, -0.8, 0.0, 1.0]
                },
                3: {
                    'vtx': 3,
                    'idx': 3,
                    'gl_PerVertex.gl_Position': [-0.1, -0.5, 0.0, 1.0]
                },
                4: {
                    'vtx': 4,
                    'idx': 4,
                    'gl_PerVertex.gl_Position': [0.0, -0.8, 0.0, 1.0]
                },
                5: {
                    'vtx': 5,
                    'idx': 5,
                    'gl_PerVertex.gl_Position': [0.1, -0.8, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay(draw.eventId, out, tex, save_data)

            rdtest.log.success("{} {} is as expected".format(level, draw.name))

            indirect_count_root = self.find_draw(
                "{}: KHR_draw_indirect_count".format(level))

            if indirect_count_root is not None:
                self.check(indirect_count_root.children[0].name ==
                           '{}: Empty count draws'.format(level))
                self.check(indirect_count_root.children[1].name ==
                           '{}: Indirect count draws'.format(level))

                empties = indirect_count_root.children[0]

                self.check(empties and len(empties.children) == 2)

                draw: rd.DrawcallDescription
                for draw in empties.children:
                    self.check(draw.numIndices == 0)
                    self.check(draw.numInstances == 0)

                    self.controller.SetFrameEvent(draw.eventId, False)

                    # Check that we have empty PostVS
                    postvs_data = self.get_postvs(rd.MeshDataStage.VSOut, 0, 1)
                    self.check(len(postvs_data) == 0)

                    self.check_overlay(draw.eventId, out, tex, save_data)

                # vkCmdDrawIndirectCountKHR
                draw_indirect = indirect_count_root.children[1].children[0]

                self.check(draw_indirect and len(draw_indirect.children) == 1)

                # vkCmdDrawIndirectCountKHR[0]
                draw = draw_indirect.children[0]

                self.check(draw.numIndices == 3)
                self.check(draw.numInstances == 4)

                self.controller.SetFrameEvent(draw.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 0,
                        'gl_PerVertex.gl_Position': [-0.8, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 1,
                        'gl_PerVertex.gl_Position': [-0.7, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 2,
                        'gl_PerVertex.gl_Position': [-0.6, 0.5, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay(draw.eventId, out, tex, save_data)

                rdtest.log.success("{} {} is as expected".format(
                    level, draw.name))

                # vkCmdDrawIndexedIndirectCountKHR
                draw_indirect = indirect_count_root.children[1].children[1]

                self.check(draw_indirect and len(draw_indirect.children) == 3)

                # vkCmdDrawIndirectCountKHR[0]
                draw = draw_indirect.children[0]
                self.check(draw.numIndices == 3)
                self.check(draw.numInstances == 1)

                self.controller.SetFrameEvent(draw.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 0,
                        'gl_PerVertex.gl_Position': [-0.6, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 1,
                        'gl_PerVertex.gl_Position': [-0.5, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 2,
                        'gl_PerVertex.gl_Position': [-0.4, 0.5, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay(draw.eventId, out, tex, save_data)

                rdtest.log.success("{} {} is as expected".format(
                    level, draw.name))

                # vkCmdDrawIndirectCountKHR[1]
                draw = draw_indirect.children[1]
                self.check(draw.numIndices == 0)
                self.check(draw.numInstances == 0)

                self.controller.SetFrameEvent(draw.eventId, False)

                postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

                self.check(len(postvs_data) == 0)

                self.check_overlay(draw.eventId, out, tex, save_data)

                rdtest.log.success("{} {} is as expected".format(
                    level, draw.name))

                # vkCmdDrawIndirectCountKHR[2]
                draw = draw_indirect.children[2]
                self.check(draw.numIndices == 6)
                self.check(draw.numInstances == 2)

                self.controller.SetFrameEvent(draw.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 0,
                        'gl_PerVertex.gl_Position': [-0.4, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 1,
                        'gl_PerVertex.gl_Position': [-0.3, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 2,
                        'gl_PerVertex.gl_Position': [-0.2, 0.2, 0.0, 1.0]
                    },
                    3: {
                        'vtx': 3,
                        'idx': 3,
                        'gl_PerVertex.gl_Position': [-0.1, 0.5, 0.0, 1.0]
                    },
                    4: {
                        'vtx': 4,
                        'idx': 4,
                        'gl_PerVertex.gl_Position': [0.0, 0.2, 0.0, 1.0]
                    },
                    5: {
                        'vtx': 5,
                        'idx': 5,
                        'gl_PerVertex.gl_Position': [0.1, 0.2, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay(draw.eventId, out, tex, save_data)

                rdtest.log.success("{} {} is as expected".format(
                    level, draw.name))
            else:
                rdtest.log.print("KHR_draw_indirect_count not tested")
Example #12
0
    def check_capture(self):
        action = self.find_action("Color Draw")

        self.check(action is not None)

        action = action.next

        self.controller.SetFrameEvent(action.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 0.5, 0.5,
                               [0.0, 1.0, 0.0, 1.0])

        rdtest.log.success("Captured loaded with color as expected")

        postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut, 0,
                                      action.numIndices)

        postvs_ref = {
            0: {
                'vtx': 0,
                'idx': 0,
                'SV_POSITION': [-0.5, -0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [0.0, 0.0],
            },
            1: {
                'vtx': 1,
                'idx': 1,
                'SV_POSITION': [0.0, 0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [0.0, 1.0],
            },
            2: {
                'vtx': 2,
                'idx': 2,
                'SV_POSITION': [0.5, -0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [1.0, 0.0],
            },
        }

        self.check_mesh_data(postvs_ref, postvs_data)

        rdtest.log.success("Mesh data is correct")

        tex = rd.TextureDisplay()
        tex.overlay = rd.DebugOverlay.Drawcall
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        out.SetTextureDisplay(tex)

        out.Display()

        overlay_id = out.GetDebugOverlayTexID()

        v = pipe.GetViewport(0)

        self.check_pixel_value(overlay_id,
                               int(0.5 * v.width),
                               int(0.5 * v.height), [0.8, 0.1, 0.8, 1.0],
                               eps=1.0 / 256.0)

        out.Shutdown()

        res = self.get_resource_by_name("Sampler Heap")

        sdfile = self.controller.GetStructuredFile()

        chunk = sdfile.chunks[res.initialisationChunks[-1]]

        desc1234 = chunk.GetChild(2).GetChild(1234).GetChild(3)

        rdtest.log.comment('desc1234: ' + desc1234.name)

        # filter
        self.check(
            desc1234.GetChild(0).AsString() == 'D3D12_FILTER_ANISOTROPIC')
        self.check(desc1234.GetChild(0).AsInt() == 0x55)

        # wrapping
        self.check(
            desc1234.GetChild(1).AsString() ==
            'D3D12_TEXTURE_ADDRESS_MODE_BORDER')
        self.check(desc1234.GetChild(1).AsInt() == 4)

        # MaxAnisotropy
        self.check(desc1234.GetChild(1).AsInt() == 4)

        # MinLod
        self.check(desc1234.GetChild(8).AsFloat() == 1.5)

        rdtest.log.success("Overlay color is as expected")
Example #13
0
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        stage = rd.ShaderStage.Pixel
        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetGraphicsPipelineObject(), pipe.GetShader(stage),
                pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId,
                cbuf.byteOffset))

        # For more detailed reference for the below checks, see the commented definition of the cbuffer
        # in the shader source code in the demo itself

        # float4 a;
        var_check.check('a').rows(1).cols(4).value([0.0, 1.0, 2.0, 3.0])

        # float3 b;
        var_check.check('b').rows(1).cols(3).value([4.0, 5.0, 6.0])

        # float2 c; float2 d;
        var_check.check('c').rows(1).cols(2).value([8.0, 9.0])
        var_check.check('d').rows(1).cols(2).value([10.0, 11.0])

        # float e; float3 f;
        var_check.check('e').rows(1).cols(1).value([12.0])
        var_check.check('f').rows(1).cols(3).value([13.0, 14.0, 15.0])

        # float g; float2 h; float i;
        var_check.check('g').rows(1).cols(1).value([16.0])
        var_check.check('h').rows(1).cols(2).value([17.0, 18.0])
        var_check.check('i').rows(1).cols(1).value([19.0])

        # float j; float2 k;
        var_check.check('j').rows(1).cols(1).value([20.0])
        var_check.check('k').rows(1).cols(2).value([21.0, 22.0])

        # float2 l; float m;
        var_check.check('l').rows(1).cols(2).value([24.0, 25.0])
        var_check.check('m').rows(1).cols(1).value([26.0])

        # float n[4];
        var_check.check('n').rows(0).cols(0).arraySize(4).members({
            0:
            lambda x: x.rows(1).cols(1).value([28.0]),
            1:
            lambda x: x.rows(1).cols(1).value([32.0]),
            2:
            lambda x: x.rows(1).cols(1).value([36.0]),
            3:
            lambda x: x.rows(1).cols(1).value([40.0]),
        })

        # float4 dummy1;
        var_check.check('dummy1')

        # float o[4];
        var_check.check('o').rows(0).cols(0).arraySize(4).members({
            0:
            lambda x: x.rows(1).cols(1).value([48.0]),
            1:
            lambda x: x.rows(1).cols(1).value([52.0]),
            2:
            lambda x: x.rows(1).cols(1).value([56.0]),
            3:
            lambda x: x.rows(1).cols(1).value([60.0]),
        })

        # float p;
        var_check.check('p').rows(1).cols(1).value([61.0])

        # float4 dummy2;
        var_check.check('dummy2')

        # float4 dummygl1;
        # float4 dummygl2;
        var_check.check('dummygl1')
        var_check.check('dummygl2')

        # column_major float4x4 q;
        var_check.check('q').rows(4).cols(4).column_major().value([
            76.0, 80.0, 84.0, 88.0, 77.0, 81.0, 85.0, 89.0, 78.0, 82.0, 86.0,
            90.0, 79.0, 83.0, 87.0, 91.0
        ])

        # row_major float4x4 r;
        var_check.check('r').rows(4).cols(4).row_major().value([
            92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0, 101.0,
            102.0, 103.0, 104.0, 105.0, 106.0, 107.0
        ])

        # column_major float3x4 s;
        var_check.check('s').rows(3).cols(4).column_major().value([
            108.0, 112.0, 116.0, 120.0, 109.0, 113.0, 117.0, 121.0, 110.0,
            114.0, 118.0, 122.0
        ])

        # float4 dummy3;
        var_check.check('dummy3')

        # row_major float3x4 t;
        var_check.check('t').rows(3).cols(4).row_major().value([
            128.0, 129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0,
            137.0, 138.0, 139.0
        ])

        # float4 dummy4;
        var_check.check('dummy4')

        # column_major float2x3 u;
        var_check.check('u').rows(2).cols(3).column_major().value(
            [144.0, 148.0, 152.0, 145.0, 149.0, 153.0])

        # float4 dummy5;
        var_check.check('dummy5')

        # row_major float2x3 v;
        var_check.check('v').rows(2).cols(3).row_major().value(
            [160.0, 161.0, 162.0, 164.0, 165.0, 166.0])

        # float4 dummy6;
        var_check.check('dummy6')

        # column_major float2x2 w;
        var_check.check('w').rows(2).cols(2).column_major().value(
            [172.0, 176.0, 173.0, 177.0])

        # float4 dummy7;
        var_check.check('dummy7')

        # row_major float2x2 x;
        var_check.check('x').rows(2).cols(2).row_major().value(
            [184.0, 185.0, 188.0, 189.0])

        # float4 dummy8;
        var_check.check('dummy8')

        # row_major float2x2 y;
        var_check.check('y').rows(2).cols(2).row_major().value(
            [196.0, 197.0, 200.0, 201.0])

        # float z;
        var_check.check('z').rows(1).cols(1).value([202.0])

        # float4 gldummy3;
        var_check.check('gldummy3')

        # row_major float4x1 aa;
        var_check.check('aa').rows(4).cols(1).value(
            [208.0, 212.0, 216.0, 220.0])

        # column_major float4x1 ab;
        var_check.check('ab').rows(4).cols(1).value(
            [224.0, 225.0, 226.0, 227.0])

        # float4 multiarray[3][2];
        # this is flattened to just multiarray[6]
        var_check.check('multiarray').rows(0).cols(0).arraySize(6).members({
            0:
            lambda x: x.rows(1).cols(4).value([228.0, 229.0, 230.0, 231.0]),
            1:
            lambda x: x.rows(1).cols(4).value([232.0, 233.0, 234.0, 235.0]),
            2:
            lambda x: x.rows(1).cols(4).value([236.0, 237.0, 238.0, 239.0]),
            3:
            lambda x: x.rows(1).cols(4).value([240.0, 241.0, 242.0, 243.0]),
            4:
            lambda x: x.rows(1).cols(4).value([244.0, 245.0, 246.0, 247.0]),
            5:
            lambda x: x.rows(1).cols(4).value([248.0, 249.0, 250.0, 251.0]),
        })

        # struct float3_1 { float3 a; float b; };
        # struct nested { float3_1 a; float4 b[4]; float3_1 c[4]; };
        # nested structa[2];
        var_check.check('structa').rows(0).cols(0).arraySize(2).members({
            # structa[0]
            0:
            lambda s: s.rows(0).cols(0).structSize(3).members({
                'a':
                lambda x: x.rows(0).cols(0).structSize(2).members({
                    'a':
                    lambda y: y.rows(1).cols(3).value([252.0, 253.0, 254.0]),
                    'b':
                    lambda y: y.rows(1).cols(1).value([255.0]),
                }),
                'b':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(1).cols(4).value(
                        [256.0, 257.0, 258.0, 259.0]),
                    1:
                    lambda y: y.rows(1).cols(4).value(
                        [260.0, 261.0, 262.0, 263.0]),
                    2:
                    lambda y: y.rows(1).cols(4).value(
                        [264.0, 265.0, 266.0, 267.0]),
                    3:
                    lambda y: y.rows(1).cols(4).value(
                        [268.0, 269.0, 270.0, 271.0]),
                }),
                'c':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [272.0, 273.0, 274.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([275.0]),
                    }),
                    1:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [276.0, 277.0, 278.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([279.0]),
                    }),
                    2:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [280.0, 281.0, 282.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([283.0]),
                    }),
                    3:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [284.0, 285.0, 286.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([287.0]),
                    }),
                }),
            }),
            # structa[1]
            1:
            lambda s: s.rows(0).cols(0).structSize(3).members({
                'a':
                lambda x: x.rows(0).cols(0).structSize(2).members({
                    'a':
                    lambda y: y.rows(1).cols(3).value([288.0, 289.0, 290.0]),
                    'b':
                    lambda y: y.rows(1).cols(1).value([291.0]),
                }),
                'b':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(1).cols(4).value(
                        [292.0, 293.0, 294.0, 295.0]),
                    1:
                    lambda y: y.rows(1).cols(4).value(
                        [296.0, 297.0, 298.0, 299.0]),
                    2:
                    lambda y: y.rows(1).cols(4).value(
                        [300.0, 301.0, 302.0, 303.0]),
                    3:
                    lambda y: y.rows(1).cols(4).value(
                        [304.0, 305.0, 306.0, 307.0]),
                }),
                'c':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [308.0, 309.0, 310.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([311.0]),
                    }),
                    1:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [312.0, 313.0, 314.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([315.0]),
                    }),
                    2:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [316.0, 317.0, 318.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([319.0]),
                    }),
                    3:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [320.0, 321.0, 322.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([323.0]),
                    }),
                }),
            }),
        })

        # column_major float3x2 ac;
        var_check.check('ac').rows(3).cols(2).column_major().value(
            [324.0, 328.0, 325.0, 329.0, 326.0, 330.0])

        # row_major float3x2 ad;
        var_check.check('ad').rows(3).cols(2).row_major().value(
            [332.0, 333.0, 336.0, 337.0, 340.0, 341.0])

        # column_major float3x2 ae[2];
        var_check.check('ae').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(3).cols(2).column_major().value(
                [344.0, 348.0, 345.0, 349.0, 346.0, 350.0]),
            1:
            lambda x: x.rows(3).cols(2).column_major().value(
                [352.0, 356.0, 353.0, 357.0, 354.0, 358.0]),
        })

        # row_major float3x2 af[2];
        var_check.check('af').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(3).cols(2).row_major().value(
                [360.0, 361.0, 364.0, 365.0, 368.0, 369.0]),
            1:
            lambda x: x.rows(3).cols(2).row_major().value(
                [372.0, 373.0, 376.0, 377.0, 380.0, 381.0]),
        })

        # float2 dummy9;
        var_check.check('dummy9')

        # float2 dummy10;
        var_check.check('dummy10')

        # row_major float2x2 ag;
        var_check.check('ag').rows(2).cols(2).row_major().value(
            [388.0, 389.0, 392.0, 393.0])

        # float2 dummy11;
        var_check.check('dummy11')

        # float2 dummy12;
        var_check.check('dummy12')

        # column_major float2x2 ah;
        var_check.check('ah').rows(2).cols(2).column_major().value(
            [400.0, 404.0, 401.0, 405.0])

        # row_major float2x2 ai[2];
        var_check.check('ai').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(2).row_major().value(
                [408.0, 409.0, 412.0, 413.0]),
            1:
            lambda x: x.rows(2).cols(2).row_major().value(
                [416.0, 417.0, 420.0, 421.0]),
        })

        # column_major float2x2 aj[2];
        var_check.check('aj').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(2).column_major().value(
                [424.0, 428.0, 425.0, 429.0]),
            1:
            lambda x: x.rows(2).cols(2).column_major().value(
                [432.0, 436.0, 433.0, 437.0]),
        })

        # float4 test;
        var_check.check('test').rows(1).cols(4).value(
            [440.0, 441.0, 442.0, 443.0])

        var_check.done()

        rdtest.log.success("CBuffer variables are as expected")

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        if not rdtest.value_compare(picked.floatValue,
                                    [440.1, 441.0, 442.0, 443.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Picked value is as expected")

        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetGraphicsPipelineObject(), pipe.GetShader(stage),
                pipe.GetShaderEntryPoint(stage), 1, cbuf.resourceId,
                cbuf.byteOffset))

        # float4 zero;
        var_check.check('root_zero').rows(1).cols(4).value(
            [0.0, 0.0, 0.0, 0.0])

        # float4 a;
        var_check.check('root_a').rows(1).cols(4).value(
            [10.0, 20.0, 30.0, 40.0])

        # float2 b;
        var_check.check('root_b').rows(1).cols(2).value([50.0, 60.0])

        # float2 c;
        var_check.check('root_c').rows(1).cols(2).value([70.0, 80.0])

        # float3_1 d;
        var_check.check('root_d').rows(0).cols(0).structSize(2).members({
            'a':
            lambda y: y.rows(1).cols(3).value([90.0, 100.0, 110.0]),
            'b':
            lambda y: y.rows(1).cols(1).value([120.0]),
        })

        var_check.done()

        rdtest.log.success("Root signature variables are as expected")

        out.Shutdown()
Example #14
0
    def check_capture(self):
        self.check_final_backbuffer()

        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(), rd.ReplayOutputType.Texture)

        self.check(out is not None)

        out.SetDimensions(100, 100)

        test_marker: rd.DrawcallDescription = self.find_draw("Test")

        self.controller.SetFrameEvent(test_marker.next.eventId, True)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        for overlay in rd.DebugOverlay:
            if overlay == rd.DebugOverlay.NoOverlay:
                continue

            # These overlays are just displaymodes really, not actually separate overlays
            if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                continue

            # Unfortunately line-fill rendering seems to vary too much by IHV, so gives inconsistent results
            if overlay == rd.DebugOverlay.Wireframe:
                continue

            tex.overlay = overlay
            out.SetTextureDisplay(tex)

            overlay_path = rdtest.get_tmp_path(str(overlay) + '.png')
            ref_path = self.get_ref_path(str(overlay) + '.png')

            save_data = rd.TextureSave()
            save_data.resourceId = out.GetDebugOverlayTexID()
            save_data.destType = rd.FileType.PNG

            save_data.comp.blackPoint = 0.0
            save_data.comp.whitePoint = 1.0

            tolerance = 2

            # These overlays return grayscale above 1, so rescale to an expected range.
            if (overlay == rd.DebugOverlay.QuadOverdrawDraw or overlay == rd.DebugOverlay.QuadOverdrawPass or
                    overlay == rd.DebugOverlay.TriangleSizeDraw or overlay == rd.DebugOverlay.TriangleSizePass):
                save_data.comp.whitePoint = 10.0

            # These overlays modify the underlying texture, so we need to save it out instead of the overlay
            if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                save_data.resourceId = tex.resourceId

            self.controller.SaveTexture(save_data, overlay_path)

            if not rdtest.image_compare(overlay_path, ref_path, tolerance):
                raise rdtest.TestFailureException("Reference and output image differ for overlay {}".format(str(overlay)), overlay_path, ref_path)

            rdtest.log.success("Reference and output image are identical for {}".format(str(overlay)))

        save_data = rd.TextureSave()
        save_data.resourceId = pipe.GetDepthTarget().resourceId
        save_data.destType = rd.FileType.PNG
        save_data.channelExtract = 0

        tmp_path = rdtest.get_tmp_path('depth.png')
        ref_path = self.get_ref_path('depth.png')

        self.controller.SaveTexture(save_data, tmp_path)

        if not rdtest.image_compare(tmp_path, ref_path):
            raise rdtest.TestFailureException("Reference and output image differ for depth {}", tmp_path, ref_path)

        rdtest.log.success("Reference and output image are identical for depth")

        save_data.channelExtract = 1

        tmp_path = rdtest.get_tmp_path('stencil.png')
        ref_path = self.get_ref_path('stencil.png')

        self.controller.SaveTexture(save_data, tmp_path)

        if not rdtest.image_compare(tmp_path, ref_path):
            raise rdtest.TestFailureException("Reference and output image differ for stencil {}", tmp_path, ref_path)

        rdtest.log.success("Reference and output image are identical for stencil")

        out.Shutdown()
Example #15
0
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture)

        self.check(out is not None)

        ref = {
            0: {
                'SNorm': [1.0, -1.0, 1.0, -1.0],
                'UNorm': [12345.0/65535.0, 6789.0/65535.0, 1234.0/65535.0, 567.0/65535.0],
                'UScaled': [12345.0, 6789.0, 1234.0, 567.0],
                'UInt': [12345, 6789, 1234, 567],
                'Double': [9.8765432109, -5.6789012345],
                'Array[0]': [1.0, 2.0],
                'Array[1]': [3.0, 4.0],
                'Array[2]': [5.0, 6.0],
                'Matrix:row0': [7.0, 8.0],
                'Matrix:row1': [9.0, 10.0],
            },
            1: {
                'SNorm': [32766.0/32767.0, -32766.0/32767.0, 16000.0/32767.0, -16000.0/32767.0],
                'UNorm': [56.0/65535.0, 7890.0/65535.0, 123.0/65535.0, 4567.0/65535.0],
                'UScaled': [56.0, 7890.0, 123.0, 4567.0],
                'UInt': [56, 7890, 123, 4567],
                'Double': [-7.89012345678, 6.54321098765],
                'Array[0]': [11.0, 12.0],
                'Array[1]': [13.0, 14.0],
                'Array[2]': [15.0, 16.0],
                'Matrix:row0': [17.0, 18.0],
                'Matrix:row1': [19.0, 20.0],
            },
            2: {
                'SNorm': [5.0/32767.0, -5.0/32767.0, 0.0, 0.0],
                'UNorm': [8765.0/65535.0, 43210.0/65535.0, 987.0/65535.0, 65432.0/65535.0],
                'UScaled': [8765.0, 43210.0, 987.0, 65432.0],
                'UInt': [8765, 43210, 987, 65432],
                'Double': [0.1234567890123, 4.5678901234],
                'Array[0]': [21.0, 22.0],
                'Array[1]': [23.0, 24.0],
                'Array[2]': [25.0, 26.0],
                'Matrix:row0': [27.0, 28.0],
                'Matrix:row1': [29.0, 30.0],
            },
        }

        # Copy the ref values and prepend 'In'
        in_ref = {}
        for idx in ref:
            in_ref[idx] = {}
            for key in ref[idx]:
                in_ref[idx]['In' + key] = ref[idx][key]

        # Copy the ref values and prepend 'Out'
        out_ref = {}
        for idx in ref:
            out_ref[idx] = {}
            for key in ref[idx]:
                out_ref[idx]['Out' + key] = ref[idx][key]

        vsout_ref = copy.deepcopy(out_ref)
        gsout_ref = out_ref

        vsout_ref[0]['gl_PerVertex.gl_Position'] = [-0.5, 0.5, 0.0, 1.0]
        gsout_ref[0]['gl_PerVertex.gl_Position'] = [0.5, -0.5, 0.4, 1.2]

        vsout_ref[1]['gl_PerVertex.gl_Position'] = [0.0, -0.5, 0.0, 1.0]
        gsout_ref[1]['gl_PerVertex.gl_Position'] = [-0.5, 0.0, 0.4, 1.2]

        vsout_ref[2]['gl_PerVertex.gl_Position'] = [0.5, 0.5, 0.0, 1.0]
        gsout_ref[2]['gl_PerVertex.gl_Position'] = [0.5, 0.5, 0.4, 1.2]

        self.check_mesh_data(in_ref, self.get_vsin(draw))
        rdtest.log.success("Vertex input data is as expected")

        self.check_mesh_data(vsout_ref, self.get_postvs(rd.MeshDataStage.VSOut))

        rdtest.log.success("Vertex output data is as expected")

        # This is optional to account for drivers without XFB
        postgs_data = self.get_postvs(rd.MeshDataStage.GSOut)
        if len(postgs_data) > 0:
            self.check_mesh_data(gsout_ref, postgs_data)

            rdtest.log.success("Geometry output data is as expected")
        else:
            rdtest.log.print("Geometry output not tested")

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2), int(texdetails.height / 2), 0, 0, 0)

        if not rdtest.value_compare(picked.floatValue, [0.0, 1.0, 0.0, 1.0]):
            raise rdtest.TestFailureException("Picked value {} doesn't match expectation".format(picked.floatValue))

        rdtest.log.success("Triangle picked value is as expected")

        out.Shutdown()
Example #16
0
    def check_capture(self):
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        test_marker: rd.DrawcallDescription = self.find_draw("Test")

        self.controller.SetFrameEvent(test_marker.next.eventId, True)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        # Check the actual output is as expected first.

        # Background around the outside
        self.check_pixel_value(tex.resourceId, 0.1, 0.1, [0.2, 0.2, 0.2, 1.0])
        self.check_pixel_value(tex.resourceId, 0.8, 0.1, [0.2, 0.2, 0.2, 1.0])
        self.check_pixel_value(tex.resourceId, 0.5, 0.95, [0.2, 0.2, 0.2, 1.0])

        # Large dark grey triangle
        self.check_pixel_value(tex.resourceId, 0.5, 0.1, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(tex.resourceId, 0.5, 0.9, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(tex.resourceId, 0.2, 0.9, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(tex.resourceId, 0.8, 0.9, [0.1, 0.1, 0.1, 1.0])

        # Red upper half triangle
        self.check_pixel_value(tex.resourceId, 0.3, 0.4, [1.0, 0.0, 0.0, 1.0])
        # Blue lower half triangle
        self.check_pixel_value(tex.resourceId, 0.3, 0.6, [0.0, 0.0, 1.0, 1.0])

        # Floating clipped triangle
        self.check_pixel_value(tex.resourceId, 335, 140, [0.0, 0.0, 0.0, 1.0])
        self.check_pixel_value(tex.resourceId, 340, 140, [0.2, 0.2, 0.2, 1.0])

        # Triangle size triangles
        self.check_pixel_value(tex.resourceId, 200, 51, [1.0, 0.5, 1.0, 1.0])
        self.check_pixel_value(tex.resourceId, 200, 65, [1.0, 1.0, 0.0, 1.0])
        self.check_pixel_value(tex.resourceId, 200, 79, [0.0, 1.0, 1.0, 1.0])
        self.check_pixel_value(tex.resourceId, 200, 93, [0.0, 1.0, 0.0, 1.0])

        for overlay in rd.DebugOverlay:
            if overlay == rd.DebugOverlay.NoOverlay:
                continue

            # These overlays are just displaymodes really, not actually separate overlays
            if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                continue

            # Unfortunately line-fill rendering seems to vary too much by IHV, so gives inconsistent results
            if overlay == rd.DebugOverlay.Wireframe:
                continue

            tex.overlay = overlay
            out.SetTextureDisplay(tex)

            overlay_path = rdtest.get_tmp_path(str(overlay) + '.png')
            ref_path = self.get_ref_path(str(overlay) + '.png')

            save_data = rd.TextureSave()
            save_data.resourceId = out.GetDebugOverlayTexID()
            save_data.destType = rd.FileType.PNG
            save_data.typeCast = rd.CompType.Typeless

            save_data.comp.blackPoint = 0.0
            save_data.comp.whitePoint = 1.0

            tolerance = 2

            # These overlays return grayscale above 1, so rescale to an expected range.
            if (overlay == rd.DebugOverlay.QuadOverdrawDraw
                    or overlay == rd.DebugOverlay.QuadOverdrawPass
                    or overlay == rd.DebugOverlay.TriangleSizeDraw
                    or overlay == rd.DebugOverlay.TriangleSizePass):
                save_data.comp.whitePoint = 10.0

            # These overlays modify the underlying texture, so we need to save it out instead of the overlay
            if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                save_data.resourceId = tex.resourceId
                save_data.typeCast = rd.CompType.UNormSRGB

            self.controller.SaveTexture(save_data, overlay_path)

            if not rdtest.png_compare(overlay_path, ref_path, tolerance):
                raise rdtest.TestFailureException(
                    "Reference and output image differ for overlay {}".format(
                        str(overlay)), overlay_path, ref_path)

            rdtest.log.success(
                "Reference and output image are identical for {}".format(
                    str(overlay)))

        save_data = rd.TextureSave()
        save_data.resourceId = pipe.GetDepthTarget().resourceId
        save_data.destType = rd.FileType.PNG
        save_data.channelExtract = 0

        tmp_path = rdtest.get_tmp_path('depth.png')
        ref_path = self.get_ref_path('depth.png')

        self.controller.SaveTexture(save_data, tmp_path)

        if not rdtest.png_compare(tmp_path, ref_path):
            raise rdtest.TestFailureException(
                "Reference and output image differ for depth {}", tmp_path,
                ref_path)

        rdtest.log.success(
            "Reference and output image are identical for depth")

        save_data.channelExtract = 1

        tmp_path = rdtest.get_tmp_path('stencil.png')
        ref_path = self.get_ref_path('stencil.png')

        self.controller.SaveTexture(save_data, tmp_path)

        if not rdtest.png_compare(tmp_path, ref_path):
            raise rdtest.TestFailureException(
                "Reference and output image differ for stencil {}", tmp_path,
                ref_path)

        rdtest.log.success(
            "Reference and output image are identical for stencil")

        self.check_clearbeforedraw_depth(out, pipe.GetDepthTarget().resourceId)

        out.Shutdown()
Example #17
0
    def check_capture_with_controller(self, proxy_api: str):
        any_failed = False

        if proxy_api != '':
            rdtest.log.print('Running with {} local proxy'.format(proxy_api))
            self.proxied = True
        else:
            rdtest.log.print('Running on direct replay')
            self.proxied = False

        self.out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        for d in self.controller.GetDrawcalls():

            # Check each region for the tests within
            if d.flags & rd.DrawFlags.PushMarker:
                name = ''
                tests_run = 0

                failed = False

                # Iterate over drawcalls in this region
                for sub in d.children:
                    sub: rd.DrawcallDescription

                    if sub.flags & rd.DrawFlags.SetMarker:
                        name = sub.name

                    # Check this draw
                    if sub.flags & rd.DrawFlags.Drawcall:
                        tests_run = tests_run + 1
                        try:
                            # Set this event as current
                            self.controller.SetFrameEvent(sub.eventId, True)

                            self.filename = (d.name + '@' + name).replace(
                                '->', '_')

                            self.check_test(d.name, name,
                                            Texture_Zoo.TEST_CAPTURE)
                        except rdtest.TestFailureException as ex:
                            failed = any_failed = True
                            rdtest.log.error(str(ex))

                if not failed:
                    rdtest.log.success(
                        "All {} texture tests for {} are OK".format(
                            tests_run, d.name))

        self.out.Shutdown()
        self.out = None

        if not any_failed:
            if proxy_api != '':
                rdtest.log.success(
                    'All textures are OK with {} as local proxy'.format(
                        proxy_api))
            else:
                rdtest.log.success("All textures are OK on direct replay")
        else:
            raise rdtest.TestFailureException(
                "Some tests were not as expected")
Example #18
0
    def check_capture(self):
        id = self.get_last_draw().copyDestination

        tex_details = self.get_texture(id)

        self.controller.SetFrameEvent(self.get_last_draw().eventId, True)

        data = self.controller.GetTextureData(id, rd.Subresource(0, 0, 0))
        first_pixel = struct.unpack_from("BBBB", data, 0)

        val = [255, 0, 255, 255]
        if not rdtest.value_compare(first_pixel, val):
            raise rdtest.TestFailureException(
                "First pixel should be clear color {}, not {}".format(
                    val, first_pixel))

        magic_pixel = struct.unpack_from("BBBB", data,
                                         (50 * tex_details.width + 320) * 4)

        # allow 127 or 128 for alpha
        val = [0, 0, 255, magic_pixel[3]]
        if not rdtest.value_compare(magic_pixel,
                                    val) or magic_pixel[3] not in [127, 128]:
            raise rdtest.TestFailureException(
                "Pixel @ 320,50 should be blue: {}, not {}".format(
                    val, magic_pixel))

        rdtest.log.success("Decoded pixels from texture data are correct")

        img_path = rdtest.get_tmp_path('preserved_alpha.png')

        self.controller.SetFrameEvent(self.get_last_draw().eventId, True)

        save_data = rd.TextureSave()
        save_data.resourceId = id
        save_data.destType = rd.FileType.PNG
        save_data.alpha = rd.AlphaMapping.Discard  # this should not discard the alpha

        self.controller.SaveTexture(save_data, img_path)

        data = rdtest.png_load_data(img_path)

        magic_pixel = struct.unpack_from("BBBB", data[-1 - 50], 320 * 4)

        val = [0, 0, 255, magic_pixel[3]]
        if not rdtest.value_compare(magic_pixel,
                                    val) or magic_pixel[3] not in [127, 128]:
            raise rdtest.TestFailureException(
                "Pixel @ 320,50 should be blue: {}, not {}".format(
                    val, magic_pixel))

        draw = self.find_draw("Draw")

        self.controller.SetFrameEvent(draw.eventId, False)

        postvs_data = self.get_postvs(draw, rd.MeshDataStage.VSOut, 0,
                                      draw.numIndices)

        postvs_ref = {
            0: {
                'vtx': 0,
                'idx': 0,
                'gl_Position': [-0.5, -0.5, 0.0, 1.0],
                'v2fcol': [0.0, 1.0, 0.0, 1.0],
            },
            1: {
                'vtx': 1,
                'idx': 1,
                'gl_Position': [0.0, 0.5, 0.0, 1.0],
                'v2fcol': [0.0, 1.0, 0.0, 1.0],
            },
            2: {
                'vtx': 2,
                'idx': 2,
                'gl_Position': [0.5, -0.5, 0.0, 1.0],
                'v2fcol': [0.0, 1.0, 0.0, 1.0],
            },
        }

        self.check_mesh_data(postvs_ref, postvs_data)

        results = self.controller.FetchCounters([
            rd.GPUCounter.RasterizedPrimitives, rd.GPUCounter.VSInvocations,
            rd.GPUCounter.FSInvocations
        ])

        results = [r for r in results if r.eventId == draw.eventId]

        if len(results) != 3:
            raise rdtest.TestFailureException(
                "Expected 3 results, got {} results".format(len(results)))

        for r in results:
            r: rd.CounterResult
            val = r.value.u32
            if r.counter == rd.GPUCounter.RasterizedPrimitives:
                if not rdtest.value_compare(val, 1):
                    raise rdtest.TestFailureException(
                        "RasterizedPrimitives result {} is not as expected".
                        format(val))
                else:
                    rdtest.log.success(
                        "RasterizedPrimitives result is as expected")
            elif r.counter == rd.GPUCounter.VSInvocations:
                if not rdtest.value_compare(val, 3):
                    raise rdtest.TestFailureException(
                        "VSInvocations result {} is not as expected".format(
                            val))
                else:
                    rdtest.log.success("VSInvocations result is as expected")
            elif r.counter == rd.GPUCounter.FSInvocations:
                if val < int(0.1 * tex_details.width * tex_details.height):
                    raise rdtest.TestFailureException(
                        "FSInvocations result {} is not as expected".format(
                            val))
                else:
                    rdtest.log.success("FSInvocations result is as expected")
            else:
                raise rdtest.TestFailureException(
                    "Unexpected counter result {}".format(r.counter))

        rdtest.log.success("Counter data retrieved successfully")

        draw = self.find_draw("NoScissor")

        self.check(draw is not None)
        draw = draw.next
        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.overlay = rd.DebugOverlay.Drawcall
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        out.SetTextureDisplay(tex)

        out.Display()

        overlay_id = out.GetDebugOverlayTexID()

        v = pipe.GetViewport(0)

        self.check_pixel_value(overlay_id,
                               int(0.5 * v.width),
                               int(0.5 * v.height), [0.8, 0.1, 0.8, 1.0],
                               eps=1.0 / 256.0)

        out.Shutdown()

        rdtest.log.success("Overlay color is as expected")
Example #19
0
    def check_capture(self):
        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        tex = rd.TextureDisplay()

        # At each draw, the centre pixel of the viewport should be green
        draw = self.get_first_draw()
        while draw is not None:
            self.controller.SetFrameEvent(draw.eventId, False)

            if draw.flags & rd.DrawFlags.Drawcall:
                tex.resourceId = self.controller.GetPipelineState(
                ).GetOutputTargets()[0].resourceId
                out.SetTextureDisplay(tex)

                view: rd.Viewport = self.controller.GetPipelineState(
                ).GetViewport(0)

                x, y = int(view.x + view.width / 2), int(view.y +
                                                         view.height / 2)

                # convert to top-left co-ordinates for use with PickPixel
                y = self.get_texture(tex.resourceId).height - y

                picked: rd.PixelValue = out.PickPixel(tex.resourceId, False, x,
                                                      y, 0, 0, 0)

                if not rdtest.value_compare(picked.floatValue,
                                            [0.0, 1.0, 0.0, 1.0]):
                    raise rdtest.TestFailureException(
                        "Picked value {} at {} doesn't match expected green".
                        format(picked.floatValue, (x, y)))

            draw = draw.next

        rdtest.log.success("Draws are all green")

        # Now save the backbuffer to disk
        ref_path = rdtest.get_tmp_path('backbuffer.png')

        save_data = rd.TextureSave()
        save_data.resourceId = tex.resourceId
        save_data.destType = rd.FileType.PNG

        self.controller.SaveTexture(save_data, ref_path)

        # Open the capture and grab the thumbnail, check that it is all green too (dirty way of verifying we didn't
        # break in-app updates but somehow end up with the right data)
        cap = rd.OpenCaptureFile()

        # Open a particular file
        status = cap.OpenFile(self.capture_filename, '', None)

        # Make sure the file opened successfully
        if status != rd.ReplayStatus.Succeeded:
            cap.Shutdown()
            raise rdtest.TestFailureException("Couldn't open '{}': {}".format(
                self.capture_filename, str(status)))

        thumb: rd.Thumbnail = cap.GetThumbnail(rd.FileType.PNG, 0)

        tmp_path = rdtest.get_tmp_path('thumbnail.png')

        with open(tmp_path, 'wb') as f:
            f.write(thumb.data)

        # The original thumbnail should also be identical, since we have the uncompressed extended thumbnail.

        if not rdtest.png_compare(tmp_path, ref_path):
            raise rdtest.TestFailureException(
                "Reference backbuffer and thumbnail image differ", tmp_path,
                ref_path)

        rdtest.log.success("Thumbnail is identical to reference")

        out.Shutdown()
Example #20
0
    def check_capture(self):
        last_action: rd.ActionDescription = self.get_last_action()

        self.controller.SetFrameEvent(last_action.eventId, True)

        self.check_triangle(out=last_action.copyDestination)

        rdtest.log.success("Triangle output looks correct")

        action = self.find_action('CmdDraw')

        self.controller.SetFrameEvent(action.eventId, True)

        pipe: rd.PipeState = self.controller.GetPipelineState()
        vkpipe = self.controller.GetVulkanPipelineState()

        binding = vkpipe.graphics.descriptorSets[0].bindings[0]

        if binding.dynamicallyUsedCount != 1:
            raise rdtest.TestFailureException(
                "Bind 0 doesn't have the right used count {}".format(
                    binding.dynamicallyUsedCount))

        if not binding.binds[15].dynamicallyUsed:
            raise rdtest.TestFailureException(
                "Graphics bind 0[15] isn't dynamically used")

        refl: rd.ShaderReflection = pipe.GetShaderReflection(
            rd.ShaderStage.Vertex)

        self.check(len(refl.readOnlyResources) == 0)

        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)

        history = self.controller.PixelHistory(
            pipe.GetOutputTargets()[0].resourceId, 200, 150,
            rd.Subresource(0, 0, 0), rd.CompType.Typeless)

        # should be a clear then a draw
        self.check(len(history) == 2)

        self.check(
            self.find_action('', history[0].eventId).flags
            & rd.ActionFlags.Clear)

        self.check(
            self.find_action('', history[1].eventId).eventId == action.eventId)
        self.check(history[1].Passed())

        if not rdtest.value_compare(history[1].shaderOut.col.floatValue,
                                    (0.0, 1.0, 0.0, 1.0)):
            raise rdtest.TestFailureException(
                "History for drawcall output is wrong: {}".format(
                    history[1].shaderOut.col.floatValue))

        trace = self.controller.DebugPixel(200, 150, 0, 0)

        refl: rd.ShaderReflection = pipe.GetShaderReflection(
            rd.ShaderStage.Pixel)

        self.check(len(refl.readOnlyResources) == 1)

        if trace.debugger is None:
            raise rdtest.TestFailureException("No pixel debug result")

        cycles, variables = self.process_trace(trace)

        output_sourcevar = self.find_output_source_var(
            trace, rd.ShaderBuiltin.ColorOutput, 0)

        if output_sourcevar is None:
            raise rdtest.TestFailureException(
                "Couldn't get colour output value")

        debugged = self.evaluate_source_var(output_sourcevar, variables)

        self.controller.FreeTrace(trace)

        debuggedValue = list(debugged.value.f32v[0:4])

        is_eq, diff_amt = rdtest.value_compare_diff(
            history[1].shaderOut.col.floatValue, debuggedValue, eps=5.0E-06)
        if not is_eq:
            rdtest.log.error(
                "Debugged pixel value {}: {} difference. {} doesn't exactly match history shader output {}"
                .format(debugged.name, diff_amt, debuggedValue,
                        history[1].shaderOut.col.floatValue))

        rdtest.log.success(
            'Successfully debugged pixel in {} cycles, result matches'.format(
                cycles))

        out = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        tex.overlay = rd.DebugOverlay.TriangleSizeDraw
        out.SetTextureDisplay(tex)

        out.Display()

        overlay_id = out.GetDebugOverlayTexID()

        self.check_pixel_value(overlay_id, 200, 150,
                               [14992.0, 14992.0, 14992.0, 1.0])

        rdtest.log.success("Triangle size overlay gave correct output")

        out.Shutdown()
Example #21
0
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        stage = rd.ShaderStage.Pixel
        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetGraphicsPipelineObject(), pipe.GetShader(stage),
                pipe.GetShaderEntryPoint(stage), 0, cbuf.resourceId,
                cbuf.byteOffset))

        # For more detailed reference for the below checks, see the commented definition of the cbuffer
        # in the shader source code in the demo itself

        # vec4 a;
        var_check.check('a').cols(4).rows(1).value([0.0, 1.0, 2.0, 3.0])

        # vec3 b;
        var_check.check('b').cols(3).rows(1).value([4.0, 5.0, 6.0])

        # vec2 c; vec2 d;
        var_check.check('c').cols(2).rows(1).value([8.0, 9.0])
        var_check.check('d').cols(2).rows(1).value([10.0, 11.0])

        # float e; vec3 f;
        var_check.check('e').cols(1).rows(1).value([12.0])
        var_check.check('f').cols(3).rows(1).value([16.0, 17.0, 18.0])

        # vec4 dummy0;
        var_check.check('dummy0')

        # float j; vec2 k;
        var_check.check('j').cols(1).rows(1).value([24.0])
        var_check.check('k').cols(2).rows(1).value([26.0, 27.0])

        # vec2 l; float m;
        var_check.check('l').cols(2).rows(1).value([28.0, 29.0])
        var_check.check('m').cols(1).rows(1).value([30.0])

        # float n[4];
        var_check.check('n').cols(0).rows(0).arraySize(4).members({
            0:
            lambda x: x.cols(1).rows(1).value([32.0]),
            1:
            lambda x: x.cols(1).rows(1).value([36.0]),
            2:
            lambda x: x.cols(1).rows(1).value([40.0]),
            3:
            lambda x: x.cols(1).rows(1).value([44.0]),
        })

        # vec4 dummy1;
        var_check.check('dummy1')

        # float o[4];
        var_check.check('o').cols(0).rows(0).arraySize(4).members({
            0:
            lambda x: x.cols(1).rows(1).value([52.0]),
            1:
            lambda x: x.cols(1).rows(1).value([56.0]),
            2:
            lambda x: x.cols(1).rows(1).value([60.0]),
            3:
            lambda x: x.cols(1).rows(1).value([64.0]),
        })

        # float p;
        var_check.check('p').cols(1).rows(1).value([68.0])

        # vec4 dummy2;
        var_check.check('dummy2')

        # column_major vec4x4 q;
        var_check.check('q').cols(4).rows(4).column_major().value([
            76.0, 80.0, 84.0, 88.0, 77.0, 81.0, 85.0, 89.0, 78.0, 82.0, 86.0,
            90.0, 79.0, 83.0, 87.0, 91.0
        ])

        # row_major vec4x4 r;
        var_check.check('r').cols(4).rows(4).row_major().value([
            92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0, 101.0,
            102.0, 103.0
        ])

        # column_major vec4x3 s;
        var_check.check('s').cols(4).rows(3).column_major().value([
            108.0, 112.0, 116.0, 120.0, 109.0, 113.0, 117.0, 121.0, 110.0,
            114.0, 118.0, 122.0
        ])

        # vec4 dummy3;
        var_check.check('dummy3')

        # row_major vec4x3 t;
        var_check.check('t').cols(4).rows(3).row_major().value([
            128.0, 129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0,
            137.0, 138.0, 139.0
        ])

        # vec4 dummy4;
        var_check.check('dummy4')

        # column_major vec2x3 u;
        var_check.check('u').cols(3).rows(2).column_major().value(
            [144.0, 148.0, 152.0, 145.0, 149.0, 153.0])

        # vec4 dummy5;
        var_check.check('dummy5')

        # row_major vec3x2 v;
        var_check.check('v').cols(3).rows(2).row_major().value(
            [160.0, 161.0, 162.0, 164.0, 165.0, 166.0])

        # vec4 dummy6;
        var_check.check('dummy6')

        # column_major vec3x2 w;
        var_check.check('w').cols(2).rows(2).column_major().value(
            [172.0, 176.0, 173.0, 177.0])

        # vec4 dummy7;
        var_check.check('dummy7')

        # row_major vec3x2 x;
        var_check.check('x').cols(2).rows(2).row_major().value(
            [184.0, 185.0, 188.0, 189.0])

        # vec4 dummy8;
        var_check.check('dummy8')

        # row_major vec2x2 y;
        var_check.check('y').cols(2).rows(2).row_major().value(
            [196.0, 197.0, 200.0, 201.0])

        # float z;
        var_check.check('z').cols(1).rows(1).value([204.0])

        # vec4 dummy9;
        var_check.check('dummy9')

        # vec4 multiarray[3][2];
        var_check.check('multiarray').cols(0).rows(0).arraySize(3).members({
            0:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([228.0, 229.0, 230.0, 231.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([232.0, 233.0, 234.0, 235.0]
                                                  ),
            }),
            1:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([236.0, 237.0, 238.0, 239.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([240.0, 241.0, 242.0, 243.0]
                                                  ),
            }),
            2:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([244.0, 245.0, 246.0, 247.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([248.0, 249.0, 250.0, 251.0]
                                                  ),
            }),
        })

        # struct vec3_1 { vec3 a; float b; };
        # struct nested { vec3_1 a; vec4 b[4]; vec3_1 c[4]; };
        # nested structa[2];
        var_check.check('structa').cols(0).rows(0).arraySize(2).members({
            # structa[0]
            0:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).structSize(2).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([252.0, 253.0, 254.0]),
                    'b':
                    lambda y: y.cols(1).rows(1).value([255.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(
                        [256.0, 257.0, 258.0, 259.0]),
                    1:
                    lambda y: y.cols(4).rows(1).value(
                        [260.0, 261.0, 262.0, 263.0]),
                    2:
                    lambda y: y.cols(4).rows(1).value(
                        [264.0, 265.0, 266.0, 267.0]),
                    3:
                    lambda y: y.cols(4).rows(1).value(
                        [268.0, 269.0, 270.0, 271.0]),
                }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [272.0, 273.0, 274.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([275.0]),
                    }),
                    1:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [276.0, 277.0, 278.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([279.0]),
                    }),
                    2:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [280.0, 281.0, 282.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([283.0]),
                    }),
                    3:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [284.0, 285.0, 286.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([287.0]),
                    }),
                }),
            }),
            # structa[1]
            1:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).structSize(2).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([288.0, 289.0, 290.0]),
                    'b':
                    lambda y: y.cols(1).rows(1).value([291.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(
                        [292.0, 293.0, 294.0, 295.0]),
                    1:
                    lambda y: y.cols(4).rows(1).value(
                        [296.0, 297.0, 298.0, 299.0]),
                    2:
                    lambda y: y.cols(4).rows(1).value(
                        [300.0, 301.0, 302.0, 303.0]),
                    3:
                    lambda y: y.cols(4).rows(1).value(
                        [304.0, 305.0, 306.0, 307.0]),
                }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [308.0, 309.0, 310.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([311.0]),
                    }),
                    1:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [312.0, 313.0, 314.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([315.0]),
                    }),
                    2:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [316.0, 317.0, 318.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([319.0]),
                    }),
                    3:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [320.0, 321.0, 322.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([323.0]),
                    }),
                }),
            }),
        })

        # column_major mat2x3 ac;
        var_check.check('ac').cols(2).rows(3).column_major().value(
            [324.0, 328.0, 325.0, 329.0, 326.0, 330.0])

        # row_major mat2x3 ad;
        var_check.check('ad').cols(2).rows(3).row_major().value(
            [332.0, 333.0, 336.0, 337.0, 340.0, 341.0])

        # column_major mat2x3 ae[2];
        var_check.check('ae').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(3).column_major().value(
                [344.0, 348.0, 345.0, 349.0, 346.0, 350.0]),
            1:
            lambda x: x.cols(2).rows(3).column_major().value(
                [352.0, 356.0, 353.0, 357.0, 354.0, 358.0]),
        })

        # row_major mat2x3 af[2];
        var_check.check('af').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(3).row_major().value(
                [360.0, 361.0, 364.0, 365.0, 368.0, 369.0]),
            1:
            lambda x: x.cols(2).rows(3).row_major().value(
                [372.0, 373.0, 376.0, 377.0, 380.0, 381.0]),
        })

        # vec2 dummy10;
        var_check.check('dummy10')

        # row_major mat2x2 ag;
        var_check.check('ag').cols(2).rows(2).row_major().value(
            [388.0, 389.0, 392.0, 393.0])

        # vec2 dummy12;
        var_check.check('dummy11')

        # column_major float2x2 ah;
        var_check.check('ah').cols(2).rows(2).column_major().value(
            [400.0, 404.0, 401.0, 405.0])

        # row_major mat2x2 ai[2];
        var_check.check('ai').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(2).row_major().value(
                [408.0, 409.0, 412.0, 413.0]),
            1:
            lambda x: x.cols(2).rows(2).row_major().value(
                [416.0, 417.0, 420.0, 421.0]),
        })

        # column_major mat2x2 aj[2];
        var_check.check('aj').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(2).column_major().value(
                [424.0, 428.0, 425.0, 429.0]),
            1:
            lambda x: x.cols(2).rows(2).column_major().value(
                [432.0, 436.0, 433.0, 437.0]),
        })

        # vec4 test;
        var_check.check('test').rows(1).cols(4).value(
            [440.0, 441.0, 442.0, 443.0])

        # to save duplicating if this array changes, we calculate out from the start, as the array is tightly packed
        base = 444.0

        exp_vals = lambda wi, yi, xi: [
            base + wi * 24.0 + yi * 8.0 + xi * 4.0 + c * 1.0
            for c in range(0, 4)
        ]

        # vec4 multiarray2[4][3][2];
        var_check.check('multiarray2').cols(0).rows(0).arraySize(4).members({
            0:
            lambda w: w.cols(0).rows(0).arraySize(3).members({
                0:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 0, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 0, 1)),
                }),
                1:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 1, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 1, 1)),
                }),
                2:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 2, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(0, 2, 1)),
                }),
            }),
            1:
            lambda w: w.cols(0).rows(0).arraySize(3).members({
                0:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 0, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 0, 1)),
                }),
                1:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 1, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 1, 1)),
                }),
                2:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 2, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(1, 2, 1)),
                }),
            }),
            2:
            lambda w: w.cols(0).rows(0).arraySize(3).members({
                0:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 0, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 0, 1)),
                }),
                1:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 1, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 1, 1)),
                }),
                2:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 2, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(2, 2, 1)),
                }),
            }),
            3:
            lambda w: w.cols(0).rows(0).arraySize(3).members({
                0:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 0, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 0, 1)),
                }),
                1:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 1, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 1, 1)),
                }),
                2:
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 2, 0)),
                    1:
                    lambda y: y.cols(4).rows(1).value(exp_vals(3, 2, 1)),
                }),
            }),
        })

        var_check.done()

        rdtest.log.success("CBuffer variables are as expected")

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        if not rdtest.value_compare(picked.floatValue,
                                    [440.1, 441.0, 442.0, 443.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Picked value is as expected")

        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetGraphicsPipelineObject(), pipe.GetShader(stage),
                pipe.GetShaderEntryPoint(stage), 1, cbuf.resourceId,
                cbuf.byteOffset))

        # For bare uniforms we have partial data - only values used in the shader need to get assigned locations and
        # some drivers are aggressive about stripping any others. Only uniforms with locations get upload values.
        # Hence some of these checks don't check for the value - that means they might not be present

        # vec4 A;
        var_check.check('A').cols(4).rows(1).value([10.0, 20.0, 30.0, 40.0])

        # vec2 B;
        var_check.check('B').cols(2).rows(1).value([50.0, 60.0])

        # vec3 C;
        var_check.check('C').cols(3).rows(1).value([70.0, 80.0, 90.0])

        # mat2x3 D;
        var_check.check('D').cols(2).rows(3).value(
            [100.0, 130.0, 110.0, 140.0, 120.0, 150.0])

        # float E[3];
        var_check.check('E').cols(0).rows(0).arraySize(3).members({
            0:
            lambda x: x.cols(1).rows(1).value([160.0]),
            1:
            lambda x: x.cols(1).rows(1).value([170.0]),
            2:
            lambda x: x.cols(1).rows(1).value([180.0]),
        })

        # vec4 F[3][2][2];
        # Multidimensional arrays are represented as structs with N members
        # Due to lacking reflection, we skip structSize() checks, but check everything else if it's present (if it's
        # not, it will be skipped)
        var_check.check('F').cols(0).rows(0).members({
            '[0]':
            lambda x: x.cols(0).rows(0).members({
                '[0]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [190.0, 200.0, 210.0, 220.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [230.0, 240.0, 250.0, 260.0]),
                }),
                '[1]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [270.0, 280.0, 290.0, 300.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [310.0, 320.0, 330.0, 340.0]),
                }),
            }),
            '[1]':
            lambda x: x.cols(0).rows(0).members({
                '[0]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [350.0, 360.0, 370.0, 380.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [390.0, 400.0, 410.0, 420.0]),
                }),
                '[1]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [430.0, 440.0, 450.0, 460.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [470.0, 480.0, 490.0, 500.0]),
                }),
            }),
            '[2]':
            lambda x: x.cols(0).rows(0).members({
                '[0]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [510.0, 520.0, 530.0, 540.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [550.0, 560.0, 570.0, 580.0]),
                }),
                '[1]':
                lambda x: x.cols(0).rows(0).arraySize(2).members({
                    0:
                    lambda x: x.cols(4).rows(1).value(
                        [590.0, 600.0, 610.0, 620.0]),
                    1:
                    lambda x: x.cols(4).rows(1).value(
                        [630.0, 640.0, 650.0, 660.0]),
                }),
            }),
        })

        # struct vec3_1 { vec3 a; float b; };
        # struct nested { vec3_1 a; vec4 b[4]; vec3_1 c[4]; };
        # nested G[2];
        # Due to lacking reflection, we don't know that G[x].a.a exists, as we only reference G[n].a.b
        # Similarly we don't know that G[x].c[y].b exists
        var_check.check('G').cols(0).rows(0).arraySize(2).members({
            # G[0]
            0:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([680.0, 690.0, 700.0]),
                    'b':
                    lambda y: y.cols(1).rows(1).value([710.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members(
                    {
                        0: lambda y: y.cols(4).rows(1),
                        1: lambda y: y.cols(4).rows(1),
                        2: lambda y: y.cols(4).rows(1),
                        3: lambda y: y.cols(4).rows(1),
                    }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    1:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    2:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    3:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                }),
            }),
            # G[1]
            1:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([1040.0, 1050.0, 1060.0]
                                                      ),
                    'b':
                    lambda y: y.cols(1).rows(1).value([1070.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(
                        [1080.0, 1090.0, 1100.0, 1110.0]),
                    1:
                    lambda y: y.cols(4).rows(1).value(
                        [1120.0, 1130.0, 1140.0, 1150.0]),
                    2:
                    lambda y: y.cols(4).rows(1).value(
                        [1160.0, 1170.0, 1180.0, 1190.0]),
                    3:
                    lambda y: y.cols(4).rows(1).value(
                        [1200.0, 1210.0, 1220.0, 1230.0]),
                }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    1:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    2:
                    lambda y: y.cols(0).rows(0).members(
                        {
                            'a': lambda z: z.cols(3).rows(1),
                            'b': lambda z: z.cols(1).rows(1),
                        }),
                    3:
                    lambda y: y.cols(0).rows(0).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [1360.0, 1370.0, 1380.0]),
                        'b':
                        lambda z: z.cols(1).rows(1),
                    }),
                }),
            }),
        })

        var_check.done()

        rdtest.log.success("Bare uniform variables are as expected")

        out.Shutdown()
Example #22
0
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        stage = rd.ShaderStage.Vertex

        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0,
                cbuf.resourceId, cbuf.byteOffset))

        # For more detailed reference for the below checks, see the commented definition of the cbuffer
        # in the shader source code in the demo itself

        # float a;
        var_check.check('a').cols(1).rows(1).type(rd.VarType.Float).value(
            [1.0])

        # vec2 b;
        var_check.check('b').cols(2).rows(1).type(rd.VarType.Float).value(
            [2.0, 0.0])

        # vec3 c;
        var_check.check('c').cols(3).rows(1).type(rd.VarType.Float).value(
            [0.0, 3.0])

        # float d[2];
        var_check.check('d').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([4.0]),
            1:
            lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([5.0]),
        })

        # mat2x3 e;
        var_check.check('e').cols(2).rows(3).column_major().type(
            rd.VarType.Float).value([6.0, 999.0, 7.0, 0.0, 0.0, 0.0])

        # mat2x3 f[2];
        var_check.check('f').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(3).column_major().type(
                rd.VarType.Float).value([8.0, 999.0, 9.0, 0.0, 0.0, 0.0]),
            1:
            lambda x: x.cols(2).rows(3).column_major().type(rd.VarType.Float).
            value([10.0, 999.0, 11.0, 0.0, 0.0, 0.0]),
        })

        # float g;
        var_check.check('g').cols(1).rows(1).type(rd.VarType.Float).value(
            [12.0])

        # struct S
        # {
        #   float a;
        #   vec2 b;
        #   double c;
        #   float d;
        #   vec3 e;
        #   float f;
        # };
        # S h;

        var_check.check('h').cols(0).rows(0).structSize(6).members({
            'a':
            lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]),
            'b':
            lambda x: x.cols(2).rows(1).type(rd.VarType.Float).value([0.0]),
            'c':
            lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue(
                [13.0]),
            'd':
            lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([14.0]),
            'e':
            lambda x: x.cols(3).rows(1).type(rd.VarType.Float).value([0.0]),
            'f':
            lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]),
        })

        # S i[2];
        var_check.check('i').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(0).rows(0).structSize(6).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'b':
                lambda x: x.cols(2).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'c':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue(
                    [15.0]),
                'd':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'e':
                lambda x: x.cols(3).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'f':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
            }),
            1:
            lambda x: x.cols(0).rows(0).structSize(6).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'b':
                lambda x: x.cols(2).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'c':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Double).longvalue(
                    [0.0]),
                'd':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value(
                    [16.0]),
                'e':
                lambda x: x.cols(3).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
                'f':
                lambda x: x.cols(1).rows(1).type(rd.VarType.Float).value([0.0]
                                                                         ),
            }),
        })

        # i8vec4 pad1;
        var_check.check('pad1')

        # int8_t j;
        var_check.check('j').cols(1).rows(1).type(rd.VarType.SByte).value([17])

        # struct S8
        # {
        #   int8_t a;
        #   i8vec4 b;
        #   i8vec2 c[4];
        # };
        # S8 k;
        var_check.check('k').cols(0).rows(0).structSize(3).members({
            'a':
            lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([0]),
            'b':
            lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value(
                [0, 0, 0, 0]),
            'c':
            lambda x: x.cols(0).rows(0).arraySize(4).members({
                0:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                    [0, 0]),
                1:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                    [0, 18]),
                2:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                    [0, 0]),
                3:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                    [0, 0]),
            }),
        })

        # S8 l[2];
        var_check.check('l').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([19]),
                'b':
                lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value(
                    [0, 0, 0, 0]),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                    1:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 20]),
                    2:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                    3:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                }),
            }),
            1:
            lambda x: x.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([21]),
                'b':
                lambda x: x.cols(4).rows(1).type(rd.VarType.SByte).value(
                    [0, 0, 0, 0]),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 22]),
                    1:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                    2:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                    3:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SByte).value(
                        [0, 0]),
                }),
            })
        })

        # int8_t m;
        var_check.check('m').cols(1).rows(1).type(rd.VarType.SByte).value(
            [-23])

        # struct S16
        # {
        #   uint16_t a;
        #   i16vec4 b;
        #   i16vec2 c[4];
        #   int8_t d;
        # };
        # S16 n;
        var_check.check('n').cols(0).rows(0).structSize(4).members({
            'a':
            lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([65524]),
            'b':
            lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value(
                [0, 0, 0, -2424]),
            'c':
            lambda x: x.cols(0).rows(0).arraySize(4).members({
                0:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                    [0, 0]),
                1:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                    [0, 0]),
                2:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                    [0, 0]),
                3:
                lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                    [0, 0]),
            }),
            'd':
            lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([25]),
        })

        # i8vec4 pad2;
        var_check.check('pad2')

        # uint8_t o;
        var_check.check('o').cols(1).rows(1).type(rd.VarType.UByte).value(
            [226])

        # S16 p[2];
        var_check.check('p').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(0).rows(0).structSize(4).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([0]),
                'b':
                lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value(
                    [0, 0, 2727, 0]),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    1:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    2:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    3:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                }),
                'd':
                lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([28]),
            }),
            1:
            lambda x: x.cols(0).rows(0).structSize(4).members({
                'a':
                lambda x: x.cols(1).rows(1).type(rd.VarType.UShort).value([0]),
                'b':
                lambda x: x.cols(4).rows(1).type(rd.VarType.SShort).value(
                    [0, 0, 0, 2929]),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    1:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    2:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                    3:
                    lambda x: x.cols(2).rows(1).type(rd.VarType.SShort).value(
                        [0, 0]),
                }),
                'd':
                lambda x: x.cols(1).rows(1).type(rd.VarType.SByte).value([0]),
            })
        })

        # i8vec4 pad3;
        var_check.check('pad3')

        # uint64_t q;
        var_check.check('q').cols(1).rows(1).type(rd.VarType.ULong).longvalue(
            [30303030303030])

        # int64_t r;
        var_check.check('r').cols(1).rows(1).type(rd.VarType.SLong).longvalue(
            [-31313131313131])

        # half s;
        var_check.check('s').cols(1).rows(1).type(rd.VarType.Half).value(
            [16.25])

        # int8_t test;
        var_check.check('test').cols(1).rows(1).type(rd.VarType.SByte).value(
            [42])

        var_check.done()

        rdtest.log.success("CBuffer variables are as expected")

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        # We just output green from the shader when the value is as expected
        if not rdtest.value_compare(picked.floatValue, [0.0, 1.0, 0.0, 0.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Picked value is as expected")

        out.Shutdown()
Example #23
0
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        stage = rd.ShaderStage.Pixel

        # Verify that the GLSL draw is first
        disasm = self.controller.DisassembleShader(
            pipe.GetGraphicsPipelineObject(), pipe.GetShaderReflection(stage),
            '')

        self.check('GLSL' in disasm)

        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0,
                cbuf.resourceId, cbuf.byteOffset))

        # For more detailed reference for the below checks, see the commented definition of the cbuffer
        # in the shader source code in the demo itself

        # vec4 a;
        var_check.check('a').cols(4).rows(1).value([0.0, 1.0, 2.0, 3.0])

        # vec3 b;
        var_check.check('b').cols(3).rows(1).value([4.0, 5.0, 6.0])

        # vec2 c; vec2 d;
        var_check.check('c').cols(2).rows(1).value([8.0, 9.0])
        var_check.check('d').cols(2).rows(1).value([10.0, 11.0])

        # float e; vec3 f;
        var_check.check('e').cols(1).rows(1).value([12.0])
        var_check.check('f').cols(3).rows(1).value([16.0, 17.0, 18.0])

        # vec4 dummy0;
        var_check.check('dummy0')

        # float j; vec2 k;
        var_check.check('j').cols(1).rows(1).value([24.0])
        var_check.check('k').cols(2).rows(1).value([26.0, 27.0])

        # vec2 l; float m;
        var_check.check('l').cols(2).rows(1).value([28.0, 29.0])
        var_check.check('m').cols(1).rows(1).value([30.0])

        # float n[4];
        var_check.check('n').cols(0).rows(0).arraySize(4).members({
            0:
            lambda x: x.cols(1).rows(1).value([32.0]),
            1:
            lambda x: x.cols(1).rows(1).value([36.0]),
            2:
            lambda x: x.cols(1).rows(1).value([40.0]),
            3:
            lambda x: x.cols(1).rows(1).value([44.0]),
        })

        # vec4 dummy1;
        var_check.check('dummy1')

        # float o[4];
        var_check.check('o').cols(0).rows(0).arraySize(4).members({
            0:
            lambda x: x.cols(1).rows(1).value([52.0]),
            1:
            lambda x: x.cols(1).rows(1).value([56.0]),
            2:
            lambda x: x.cols(1).rows(1).value([60.0]),
            3:
            lambda x: x.cols(1).rows(1).value([64.0]),
        })

        # float p;
        var_check.check('p').cols(1).rows(1).value([68.0])

        # vec4 dummy2;
        var_check.check('dummy2')

        # column_major vec4x4 q;
        var_check.check('q').cols(4).rows(4).column_major().value([
            76.0, 80.0, 84.0, 88.0, 77.0, 81.0, 85.0, 89.0, 78.0, 82.0, 86.0,
            90.0, 79.0, 83.0, 87.0, 91.0
        ])

        # row_major vec4x4 r;
        var_check.check('r').cols(4).rows(4).row_major().value([
            92.0, 93.0, 94.0, 95.0, 96.0, 97.0, 98.0, 99.0, 100.0, 101.0,
            102.0, 103.0
        ])

        # column_major vec4x3 s;
        var_check.check('s').cols(4).rows(3).column_major().value([
            108.0, 112.0, 116.0, 120.0, 109.0, 113.0, 117.0, 121.0, 110.0,
            114.0, 118.0, 122.0
        ])

        # vec4 dummy3;
        var_check.check('dummy3')

        # row_major vec4x3 t;
        var_check.check('t').cols(4).rows(3).row_major().value([
            128.0, 129.0, 130.0, 131.0, 132.0, 133.0, 134.0, 135.0, 136.0,
            137.0, 138.0, 139.0
        ])

        # vec4 dummy4;
        var_check.check('dummy4')

        # column_major vec2x3 u;
        var_check.check('u').cols(3).rows(2).column_major().value(
            [144.0, 148.0, 152.0, 145.0, 149.0, 153.0])

        # vec4 dummy5;
        var_check.check('dummy5')

        # row_major vec3x2 v;
        var_check.check('v').cols(3).rows(2).row_major().value(
            [160.0, 161.0, 162.0, 164.0, 165.0, 166.0])

        # vec4 dummy6;
        var_check.check('dummy6')

        # column_major vec3x2 w;
        var_check.check('w').cols(2).rows(2).column_major().value(
            [172.0, 176.0, 173.0, 177.0])

        # vec4 dummy7;
        var_check.check('dummy7')

        # row_major vec3x2 x;
        var_check.check('x').cols(2).rows(2).row_major().value(
            [184.0, 185.0, 188.0, 189.0])

        # vec4 dummy8;
        var_check.check('dummy8')

        # row_major vec2x2 y;
        var_check.check('y').cols(2).rows(2).row_major().value(
            [196.0, 197.0, 200.0, 201.0])

        # float z;
        var_check.check('z').cols(1).rows(1).value([204.0])

        # vec4 dummy9;
        var_check.check('dummy9')

        # vec4 multiarray[3][2];
        var_check.check('multiarray').cols(0).rows(0).arraySize(3).members({
            0:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([228.0, 229.0, 230.0, 231.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([232.0, 233.0, 234.0, 235.0]
                                                  ),
            }),
            1:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([236.0, 237.0, 238.0, 239.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([240.0, 241.0, 242.0, 243.0]
                                                  ),
            }),
            2:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([244.0, 245.0, 246.0, 247.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([248.0, 249.0, 250.0, 251.0]
                                                  ),
            }),
        })

        # struct vec3_1 { vec3 a; float b; };
        # struct nested { vec3_1 a; vec4 b[4]; vec3_1 c[4]; };
        # nested structa[2];
        var_check.check('structa').cols(0).rows(0).arraySize(2).members({
            # structa[0]
            0:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).structSize(2).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([252.0, 253.0, 254.0]),
                    'b':
                    lambda y: y.cols(1).rows(1).value([255.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(
                        [256.0, 257.0, 258.0, 259.0]),
                    1:
                    lambda y: y.cols(4).rows(1).value(
                        [260.0, 261.0, 262.0, 263.0]),
                    2:
                    lambda y: y.cols(4).rows(1).value(
                        [264.0, 265.0, 266.0, 267.0]),
                    3:
                    lambda y: y.cols(4).rows(1).value(
                        [268.0, 269.0, 270.0, 271.0]),
                }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [272.0, 273.0, 274.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([275.0]),
                    }),
                    1:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [276.0, 277.0, 278.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([279.0]),
                    }),
                    2:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [280.0, 281.0, 282.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([283.0]),
                    }),
                    3:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [284.0, 285.0, 286.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([287.0]),
                    }),
                }),
            }),
            # structa[1]
            1:
            lambda s: s.cols(0).rows(0).structSize(3).members({
                'a':
                lambda x: x.cols(0).rows(0).structSize(2).members({
                    'a':
                    lambda y: y.cols(3).rows(1).value([288.0, 289.0, 290.0]),
                    'b':
                    lambda y: y.cols(1).rows(1).value([291.0]),
                }),
                'b':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(4).rows(1).value(
                        [292.0, 293.0, 294.0, 295.0]),
                    1:
                    lambda y: y.cols(4).rows(1).value(
                        [296.0, 297.0, 298.0, 299.0]),
                    2:
                    lambda y: y.cols(4).rows(1).value(
                        [300.0, 301.0, 302.0, 303.0]),
                    3:
                    lambda y: y.cols(4).rows(1).value(
                        [304.0, 305.0, 306.0, 307.0]),
                }),
                'c':
                lambda x: x.cols(0).rows(0).arraySize(4).members({
                    0:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [308.0, 309.0, 310.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([311.0]),
                    }),
                    1:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [312.0, 313.0, 314.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([315.0]),
                    }),
                    2:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [316.0, 317.0, 318.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([319.0]),
                    }),
                    3:
                    lambda y: y.cols(0).rows(0).structSize(2).members({
                        'a':
                        lambda z: z.cols(3).rows(1).value(
                            [320.0, 321.0, 322.0]),
                        'b':
                        lambda z: z.cols(1).rows(1).value([323.0]),
                    }),
                }),
            }),
        })

        # column_major mat2x3 ac;
        var_check.check('ac').cols(2).rows(3).column_major().value(
            [324.0, 328.0, 325.0, 329.0, 326.0, 330.0])

        # row_major mat2x3 ad;
        var_check.check('ad').cols(2).rows(3).row_major().value(
            [332.0, 333.0, 336.0, 337.0, 340.0, 341.0])

        # column_major mat2x3 ae[2];
        var_check.check('ae').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(3).column_major().value(
                [344.0, 348.0, 345.0, 349.0, 346.0, 350.0]),
            1:
            lambda x: x.cols(2).rows(3).column_major().value(
                [352.0, 356.0, 353.0, 357.0, 354.0, 358.0]),
        })

        # row_major mat2x3 af[2];
        var_check.check('af').cols(0).rows(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(3).row_major().value(
                [360.0, 361.0, 364.0, 365.0, 368.0, 369.0]),
            1:
            lambda x: x.cols(2).rows(3).row_major().value(
                [372.0, 373.0, 376.0, 377.0, 380.0, 381.0]),
        })

        # vec2 dummy10;
        var_check.check('dummy10')

        # row_major mat2x2 ag;
        var_check.check('ag').cols(2).rows(2).row_major().value(
            [388.0, 389.0, 392.0, 393.0])

        # vec2 dummy11;
        var_check.check('dummy11')

        # column_major mat2x2 ah;
        var_check.check('ah').cols(2).rows(2).column_major().value(
            [400.0, 404.0, 401.0, 405.0])

        # row_major mat2x2 ai[2];
        var_check.check('ai').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(2).row_major().value(
                [408.0, 409.0, 412.0, 413.0]),
            1:
            lambda x: x.cols(2).rows(2).row_major().value(
                [416.0, 417.0, 420.0, 421.0]),
        })

        # column_major mat2x2 aj[2];
        var_check.check('aj').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.cols(2).rows(2).column_major().value(
                [424.0, 428.0, 425.0, 429.0]),
            1:
            lambda x: x.cols(2).rows(2).column_major().value(
                [432.0, 436.0, 433.0, 437.0]),
        })

        # vec4 test;
        var_check.check('test').rows(1).cols(4).value(
            [440.0, 441.0, 442.0, 443.0])

        var_check.done()

        rdtest.log.success("GLSL CBuffer variables are as expected")

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        if not rdtest.value_compare(picked.floatValue,
                                    [440.1, 441.0, 442.0, 443.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("GLSL picked value is as expected")

        # Check the specialization constants
        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 1, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 1,
                cbuf.resourceId, cbuf.byteOffset))

        # int A;
        # Default value 10, untouched
        var_check.check('A').type(rd.VarType.SInt).rows(1).cols(1).value([10])

        # float B;
        # Value 20 from spec constants
        var_check.check('B').type(rd.VarType.Float).rows(1).cols(1).value(
            [20.0])

        # bool C;
        # Value True from spec constants
        var_check.check('C').type(rd.VarType.UInt).rows(1).cols(1).value([1])

        var_check.done()

        rdtest.log.success("Specialization constants are as expected")

        # Move to the HLSL draw
        draw = draw.next

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        # Verify that this is the HLSL draw
        disasm = self.controller.DisassembleShader(
            pipe.GetGraphicsPipelineObject(), pipe.GetShaderReflection(stage),
            '')

        self.check('HLSL' in disasm)

        cbuf: rd.BoundCBuffer = pipe.GetConstantBuffer(stage, 0, 0)

        var_check = rdtest.ConstantBufferChecker(
            self.controller.GetCBufferVariableContents(
                pipe.GetShader(stage), pipe.GetShaderEntryPoint(stage), 0,
                cbuf.resourceId, cbuf.byteOffset))

        # For more detailed reference for the below checks, see the commented definition of the cbuffer
        # in the shader source code in the demo itself

        # float4 a;
        var_check.check('a').rows(1).cols(4).value([0.0, 1.0, 2.0, 3.0])

        # float3 b;
        var_check.check('b').rows(1).cols(3).value([4.0, 5.0, 6.0])

        # float2 c; float2 d;
        var_check.check('c').rows(1).cols(2).value([8.0, 9.0])
        var_check.check('d').rows(1).cols(2).value([10.0, 11.0])

        # float e; float3 f;
        var_check.check('e').rows(1).cols(1).value([12.0])
        var_check.check('f').rows(1).cols(3).value([13.0, 14.0, 15.0])

        # float g; float2 h; float i;
        var_check.check('g').rows(1).cols(1).value([16.0])
        var_check.check('h').rows(1).cols(2).value([17.0, 18.0])
        var_check.check('i').rows(1).cols(1).value([19.0])

        # float j; float2 k;
        var_check.check('j').rows(1).cols(1).value([20.0])
        var_check.check('k').rows(1).cols(2).value([21.0, 22.0])

        # float2 l; float m;
        var_check.check('l').rows(1).cols(2).value([24.0, 25.0])
        var_check.check('m').rows(1).cols(1).value([26.0])

        # float n[4];
        var_check.check('n').rows(0).cols(0).arraySize(4).members({
            0:
            lambda x: x.rows(1).cols(1).value([28.0]),
            1:
            lambda x: x.rows(1).cols(1).value([32.0]),
            2:
            lambda x: x.rows(1).cols(1).value([36.0]),
            3:
            lambda x: x.rows(1).cols(1).value([40.0]),
        })

        # float4 dummy1;
        var_check.check('dummy1')

        # float o[4];
        var_check.check('o').rows(0).cols(0).arraySize(4).members({
            0:
            lambda x: x.rows(1).cols(1).value([48.0]),
            1:
            lambda x: x.rows(1).cols(1).value([52.0]),
            2:
            lambda x: x.rows(1).cols(1).value([56.0]),
            3:
            lambda x: x.rows(1).cols(1).value([60.0]),
        })

        # float p; with std140 vulkan packing
        var_check.check('p').rows(1).cols(1).value([64.0])

        # float4 dummy2;
        var_check.check('dummy2')

        # float4 gldummy;
        var_check.check('gldummy')

        # HLSL majorness is flipped to match column-major SPIR-V with row-major HLSL.
        # This means column major declared matrices will show up as row major in any reflection and SPIR-V
        # it also means that dimensions are flipped, so a float3x4 is declared as a float4x3, and a 'row'
        # is really a column, and vice-versa a 'column' is really a row.

        # column_major float4x4 q;
        var_check.check('q').rows(4).cols(4).row_major().value([
            76.0, 77.0, 78.0, 79.0, 80.0, 81.0, 82.0, 83.0, 84.0, 85.0, 86.0,
            87.0, 88.0, 89.0, 90.0, 91.0
        ])

        # row_major float4x4 r;
        var_check.check('r').rows(4).cols(4).column_major().value([
            92.0, 96.0, 100.0, 104.0, 93.0, 97.0, 101.0, 105.0, 94.0, 98.0,
            102.0, 106.0, 95.0, 99.0, 103.0, 107.0
        ])

        # column_major float3x4 s;
        var_check.check('s').rows(4).cols(3).row_major().value([
            108.0, 109.0, 110.0, 112.0, 113.0, 114.0, 116.0, 117.0, 118.0,
            120.0, 121.0, 122.0
        ])

        # float4 dummy3;
        var_check.check('dummy3')

        # row_major float3x4 t;
        var_check.check('t').rows(4).cols(3).column_major().value([
            128.0, 132.0, 136.0, 129.0, 133.0, 137.0, 130.0, 134.0, 138.0,
            131.0, 135.0, 139.0
        ])

        # float4 dummy4;
        var_check.check('dummy4')

        # column_major float2x3 u;
        var_check.check('u').rows(3).cols(2).row_major().value(
            [144.0, 145.0, 148.0, 149.0, 152.0, 153.0])

        # float4 dummy5;
        var_check.check('dummy5')

        # row_major float2x3 v;
        var_check.check('v').rows(3).cols(2).column_major().value(
            [160.0, 164.0, 161.0, 165.0, 162.0, 166.0])

        # float4 dummy6;
        var_check.check('dummy6')

        # column_major float2x2 w;
        var_check.check('w').rows(2).cols(2).row_major().value(
            [172.0, 173.0, 176.0, 177.0])

        # float4 dummy7;
        var_check.check('dummy7')

        # row_major float2x2 x;
        var_check.check('x').rows(2).cols(2).column_major().value(
            [184.0, 188.0, 185.0, 189.0])

        # float4 dummy8;
        var_check.check('dummy8')

        # row_major float2x2 y;
        var_check.check('y').rows(2).cols(2).column_major().value(
            [196.0, 200.0, 197.0, 201.0])

        # float z;
        var_check.check('z').rows(1).cols(1).value([204.0])

        # Temporarily until SPIR-V support for degenerate HLSL matrices is determined
        var_check.check('dummy9')

        # row_major float4x1 aa;
        #var_check.check('aa').rows(1).cols(4).value([208.0, 212.0, 216.0, 220.0])

        # column_major float4x1 ab;
        #var_check.check('ab').rows(1).cols(4).value([224.0, 225.0, 226.0, 227.0])

        # float4 multiarray[3][2];
        var_check.check('multiarray').cols(0).rows(0).arraySize(3).members({
            0:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([228.0, 229.0, 230.0, 231.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([232.0, 233.0, 234.0, 235.0]
                                                  ),
            }),
            1:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([236.0, 237.0, 238.0, 239.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([240.0, 241.0, 242.0, 243.0]
                                                  ),
            }),
            2:
            lambda x: x.cols(0).rows(0).arraySize(2).members({
                0:
                lambda y: y.cols(4).rows(1).value([244.0, 245.0, 246.0, 247.0]
                                                  ),
                1:
                lambda y: y.cols(4).rows(1).value([248.0, 249.0, 250.0, 251.0]
                                                  ),
            }),
        })

        # struct float3_1 { float3 a; float b; };
        # struct nested { float3_1 a; float4 b[4]; float3_1 c[4]; };
        # nested structa[2];
        var_check.check('structa').rows(0).cols(0).arraySize(2).members({
            # structa[0]
            0:
            lambda s: s.rows(0).cols(0).structSize(3).members({
                'a':
                lambda x: x.rows(0).cols(0).structSize(2).members({
                    'a':
                    lambda y: y.rows(1).cols(3).value([252.0, 253.0, 254.0]),
                    'b':
                    lambda y: y.rows(1).cols(1).value([255.0]),
                }),
                'b':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(1).cols(4).value(
                        [256.0, 257.0, 258.0, 259.0]),
                    1:
                    lambda y: y.rows(1).cols(4).value(
                        [260.0, 261.0, 262.0, 263.0]),
                    2:
                    lambda y: y.rows(1).cols(4).value(
                        [264.0, 265.0, 266.0, 267.0]),
                    3:
                    lambda y: y.rows(1).cols(4).value(
                        [268.0, 269.0, 270.0, 271.0]),
                }),
                'c':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [272.0, 273.0, 274.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([275.0]),
                    }),
                    1:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [276.0, 277.0, 278.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([279.0]),
                    }),
                    2:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [280.0, 281.0, 282.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([283.0]),
                    }),
                    3:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [284.0, 285.0, 286.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([287.0]),
                    }),
                }),
            }),
            # structa[1]
            1:
            lambda s: s.rows(0).cols(0).structSize(3).members({
                'a':
                lambda x: x.rows(0).cols(0).structSize(2).members({
                    'a':
                    lambda y: y.rows(1).cols(3).value([288.0, 289.0, 290.0]),
                    'b':
                    lambda y: y.rows(1).cols(1).value([291.0]),
                }),
                'b':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(1).cols(4).value(
                        [292.0, 293.0, 294.0, 295.0]),
                    1:
                    lambda y: y.rows(1).cols(4).value(
                        [296.0, 297.0, 298.0, 299.0]),
                    2:
                    lambda y: y.rows(1).cols(4).value(
                        [300.0, 301.0, 302.0, 303.0]),
                    3:
                    lambda y: y.rows(1).cols(4).value(
                        [304.0, 305.0, 306.0, 307.0]),
                }),
                'c':
                lambda x: x.rows(0).cols(0).arraySize(4).members({
                    0:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [308.0, 309.0, 310.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([311.0]),
                    }),
                    1:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [312.0, 313.0, 314.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([315.0]),
                    }),
                    2:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [316.0, 317.0, 318.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([319.0]),
                    }),
                    3:
                    lambda y: y.rows(0).cols(0).structSize(2).members({
                        'a':
                        lambda z: z.rows(1).cols(3).value(
                            [320.0, 321.0, 322.0]),
                        'b':
                        lambda z: z.rows(1).cols(1).value([323.0]),
                    }),
                }),
            }),
        })

        # column_major float3x2 ac;
        var_check.check('ac').rows(2).cols(3).row_major().value(
            [324.0, 325.0, 326.0, 328.0, 329.0, 330.0])

        # row_major float3x2 ad;
        var_check.check('ad').rows(2).cols(3).column_major().value(
            [332.0, 336.0, 340.0, 333.0, 337.0, 341.0])

        # column_major float3x2 ae[2];
        var_check.check('ae').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(3).row_major().value(
                [344.0, 345.0, 346.0, 348.0, 349.0, 350.0]),
            1:
            lambda x: x.rows(2).cols(3).row_major().value(
                [352.0, 353.0, 354.0, 356.0, 357.0, 358.0]),
        })

        # row_major float3x2 af[2];
        var_check.check('af').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(3).column_major().value(
                [360.0, 364.0, 368.0, 361.0, 365.0, 369.0]),
            1:
            lambda x: x.rows(2).cols(3).column_major().value(
                [372.0, 376.0, 380.0, 373.0, 377.0, 381.0]),
        })

        # float2 dummy10;
        var_check.check('dummy10')

        # float2 dummy11;
        var_check.check('dummy11')

        # row_major float2x2 ag;
        var_check.check('ag').rows(2).cols(2).column_major().value(
            [388.0, 392.0, 389.0, 393.0])

        # float2 dummy12;
        var_check.check('dummy12')

        # float2 dummy13;
        var_check.check('dummy13')

        # column_major float2x2 ah;
        var_check.check('ah').rows(2).cols(2).row_major().value(
            [400.0, 401.0, 404.0, 405.0])

        # row_major float2x2 ai[2];
        var_check.check('ai').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(2).column_major().value(
                [408.0, 412.0, 409.0, 413.0]),
            1:
            lambda x: x.rows(2).cols(2).column_major().value(
                [416.0, 420.0, 417.0, 421.0]),
        })

        # column_major float2x2 aj[2];
        var_check.check('aj').rows(0).cols(0).arraySize(2).members({
            0:
            lambda x: x.rows(2).cols(2).row_major().value(
                [424.0, 425.0, 428.0, 429.0]),
            1:
            lambda x: x.rows(2).cols(2).row_major().value(
                [432.0, 433.0, 436.0, 437.0]),
        })

        # float4 test;
        var_check.check('test').rows(1).cols(4).value(
            [440.0, 441.0, 442.0, 443.0])

        var_check.done()

        rdtest.log.success("HLSL CBuffer variables are as expected")

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        if not rdtest.value_compare(picked.floatValue,
                                    [440.1, 441.0, 442.0, 443.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("HLSL picked value is as expected")

        out.Shutdown()
Example #24
0
    def check_capture(self):
        draw = self.get_last_draw()

        self.check(draw is not None)

        draw = draw.previous

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        self.out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        self.tex = rd.TextureDisplay()
        self.tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        self.out.SetTextureDisplay(self.tex)

        texdetails = self.get_texture(self.tex.resourceId)

        # Top left we expect a regular line segment.
        s = self.sample(0, 0)

        # All points should be the line color
        if not rdtest.value_compare(s, [True, True, True]):
            raise rdtest.TestFailureException(
                "Normal line picked values {} doesn't match expectation".
                format(s))

        # Next row is unstippled. The lines should either be all present, or not present
        names = ["Rectangle", "Bresenham", "Rectangle Round"]
        for col in [0, 1, 2]:
            s = self.sample(1, col)

            n = "Unstippled {}".format(names[col])

            if s[0]:
                if not rdtest.value_compare(s, [True, True, True]):
                    raise rdtest.TestFailureException(
                        "{} picked values {} doesn't match expectation".format(
                            n, s))
                rdtest.log.success("{} line looks as expected".format(n))
            else:
                if not rdtest.value_compare(s, [False, False, False]):
                    raise rdtest.TestFailureException(
                        "{} picked values {} doesn't match expectation".format(
                            n, s))
                rdtest.log.success("{} line not supported".format(n))

        # Final row is stippled. The lines should be present on each end, and not present in the middle
        # (or not present at all)
        for col in [0, 1, 2]:
            s = self.sample(2, col)

            n = "Stippled {}".format(names[col])

            if s[0]:
                if not rdtest.value_compare(s, [True, False, True]):
                    raise rdtest.TestFailureException(
                        "{} picked values {} doesn't match expectation".format(
                            n, s))
                rdtest.log.success("{} line looks as expected".format(n))
            else:
                if not rdtest.value_compare(s, [False, False, False]):
                    raise rdtest.TestFailureException(
                        "{} picked values {} doesn't match expectation".format(
                            n, s))
                rdtest.log.success("{} line not supported".format(n))

        rdtest.log.success("All lines look as expected")

        self.out.Shutdown()
Example #25
0
    def check_capture(self, capture_filename: str,
                      controller: rd.ReplayController):
        self.controller = controller

        self.controller.SetFrameEvent(
            self.find_draw("Quad").next.eventId, False)

        self.out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(200, 200), rd.ReplayOutputType.Mesh)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        self.cfg = rd.MeshDisplay()

        cam: rd.Camera = rd.InitCamera(rd.CameraType.FPSLook)

        cam.SetPosition(0, 0, 0)
        cam.SetFPSRotation(0, 0, 0)

        self.cfg.type = rd.MeshDataStage.VSOut
        self.cfg.cam = cam

        # Position is always first, so getting the postvs data will give us
        inst0: rd.MeshFormat = self.controller.GetPostVSData(
            0, 0, self.cfg.type)
        self.cfg.position = inst0

        # after position we have float2 Color2 then float4 Color4
        self.cfg.second = self.cfg.position
        self.cfg.second.vertexByteOffset += 16
        self.cfg.second.vertexByteOffset += 8
        if pipe.HasAlignedPostVSData(self.cfg.type):
            self.cfg.second.vertexByteOffset += 8

        # Configure an ortho camera, even though we don't really have a camera
        self.cfg.ortho = True
        self.cfg.position.nearPlane = 1.0
        self.cfg.position.farPlane = 100.0
        self.cfg.aspect = 1.0
        self.cfg.wireframeDraw = True
        self.cfg.position.meshColor = rd.FloatVector(1.0, 0.0, 1.0, 1.0)

        self.cache_output()

        # We should have a single quad, check each outside edge and the inside diagonal.
        # All these line segments should have some colors (not including the background checkerboard or the frustum)
        self.check_region((55, 95, 65, 95), lambda x: x != [])  # Left edge
        self.check_region((85, 60, 85, 70), lambda x: x != [])  # Top edge
        self.check_region((105, 100, 115, 100),
                          lambda x: x != [])  # Right edge
        self.check_region((90, 130, 90, 140), lambda x: x != [])  # Bottom edge
        self.check_region((65, 120, 75, 120),
                          lambda x: x != [])  # Bottom-Left of diagonal
        self.check_region((105, 70, 110, 70),
                          lambda x: x != [])  # Top-right of diagonal

        rdtest.log.success("Base rendering is as expected")

        self.cfg.solidShadeMode = rd.SolidShade.Secondary
        self.cfg.wireframeDraw = False

        # allow for blending with white for the frustum
        isred = lambda col: col[0] > col[1] and col[1] == col[2]
        isgreen = lambda col: col[1] > col[0] and col[0] == col[2]
        isblue = lambda col: col[2] > col[0] and col[0] == col[1]

        isredgreen = lambda col: isred(col) or isgreen(col) or col[2] == 0

        isyellow = lambda col: col[0] == col[1] and col[2] < col[1]

        self.cache_output()

        # The secondary color should be completely green
        self.check_region((85, 70, 85, 125),
                          lambda x: all([isgreen(i) for i in x]))
        self.check_region((65, 100, 105, 100),
                          lambda x: all([isgreen(i) for i in x]))
        # this line segment isn't in the first instance
        self.check_region((65, 55, 105, 55), lambda x: x == [])
        # this line segment isn't in the second instance
        self.check_region((65, 125, 105, 125),
                          lambda x: all([isgreen(i) for i in x]))

        rdtest.log.success("Secondary rendering of instance 0 is as expected")

        # Out of bounds should look the same as without highlighting at all, check the corners are all still green
        self.cfg.highlightVert = 9

        self.cache_output()

        self.check_region((55, 60, 65, 70),
                          lambda x: all([isgreen(i) for i in x]))
        self.check_region((105, 60, 115, 70),
                          lambda x: all([isgreen(i) for i in x]))
        self.check_region((55, 130, 65, 140),
                          lambda x: all([isgreen(i) for i in x]))
        self.check_region((105, 130, 115, 140),
                          lambda x: all([isgreen(i) for i in x]))

        vert_regions = [
            (55, 60, 65, 70),
            (110, 60, 120, 70),
            (55, 130, 65, 140),
            (110, 60, 120, 70),
            (110, 130, 120, 140),
            (55, 130, 65, 140),
        ]

        for vert in range(6):
            self.cfg.highlightVert = vert

            self.cache_output()

            tri = int(vert / 3)

            # Check that the triangle we're highlighting is red and the other is green
            if tri == 0:
                self.check_region((65, 75, 75, 85),
                                  lambda x: all([isred(i) for i in x]))
                self.check_region((100, 115, 110, 125),
                                  lambda x: all([isgreen(i) for i in x]))
            else:
                self.check_region((65, 75, 75, 85),
                                  lambda x: all([isgreen(i) for i in x]))
                self.check_region((100, 115, 110, 125),
                                  lambda x: all([isred(i) for i in x]))

            # The corners that touch should be red and green - that is no other colours but red and green, but at least
            # some red and some green
            self.check_region(
                (65, 115, 75, 125),
                lambda x: all([isredgreen(i) for i in x]) and any(
                    [isred(i) for i in x]) and any([isgreen(i) for i in x]))

            # check that there's blue in this vertex's region
            self.check_region(vert_regions[vert],
                              lambda x: any([isblue(i) for i in x]))

        rdtest.log.success("Rendering of highlighted vertices is as expected")

        self.cfg.highlightVert = rd.MeshDisplay.NoHighlight

        # If we render from the float2 color we shouldn't get any blue
        self.cfg.second.vertexByteOffset = self.cfg.position.vertexByteOffset = inst0.vertexByteOffset
        self.cfg.second.vertexByteOffset += 16
        self.cfg.second.format.compCount = 2

        self.cache_output()

        # If we render from the float2 color we shouldn't get any blue since it's only a two-component value
        self.check_region((85, 70, 85, 125),
                          lambda x: all([isredgreen(i) for i in x]))
        self.check_region((65, 100, 105, 100),
                          lambda x: all([isredgreen(i) for i in x]))
        self.check_region((65, 55, 105, 55), lambda x: x == [])
        self.check_region((65, 125, 105, 125),
                          lambda x: all([isredgreen(i) for i in x]))

        rdtest.log.success(
            "Rendering of float2 color secondary in instance 0 is as expected")

        self.cfg.highlightVert = rd.MeshDisplay.NoHighlight
        inst1: rd.MeshFormat = self.controller.GetPostVSData(
            1, 0, self.cfg.type)

        self.cfg.curInstance = 1
        self.cfg.second.vertexResourceId = self.cfg.position.vertexResourceId = inst1.vertexResourceId
        self.cfg.second.vertexByteOffset = self.cfg.position.vertexByteOffset = inst1.vertexByteOffset
        self.cfg.second.vertexByteOffset += 16
        self.cfg.second.vertexByteOffset += 8
        if pipe.HasAlignedPostVSData(self.cfg.type):
            self.cfg.second.vertexByteOffset += 8

        self.cache_output()

        # The secondary color should be completely yellow
        self.check_region((85, 70, 85, 125),
                          lambda x: all([isyellow(i) for i in x]))
        self.check_region((65, 100, 105, 100),
                          lambda x: all([isyellow(i) for i in x]))
        # this line segment isn't in the first instance
        self.check_region((65, 55, 105, 55),
                          lambda x: all([isyellow(i) for i in x]))
        # this line segment isn't in the second instance
        self.check_region((65, 125, 105, 125), lambda x: x == [])

        rdtest.log.success("Secondary rendering of instance 1 is as expected")

        # If we render from the float2 color we shouldn't get any blue
        self.cfg.second.vertexByteOffset = self.cfg.position.vertexByteOffset = inst1.vertexByteOffset
        self.cfg.second.vertexByteOffset += 16
        self.cfg.second.format.compCount = 2

        self.cache_output()

        # If we render from the float2 color we shouldn't get any blue since it's only a two-component value
        self.check_region((85, 70, 85, 125),
                          lambda x: all([isredgreen(i) for i in x]))
        self.check_region((65, 100, 105, 100),
                          lambda x: all([isredgreen(i) for i in x]))
        self.check_region((65, 55, 105, 55),
                          lambda x: all([isredgreen(i) for i in x]))
        self.check_region((65, 125, 105, 125), lambda x: x == [])

        rdtest.log.success(
            "Rendering of float2 color secondary in instance 1 is as expected")

        self.cfg.solidShadeMode = rd.SolidShade.NoSolid
        self.cfg.showAllInstances = True

        self.cache_output()

        # wireframe for original quad should still be present
        self.check_region((55, 95, 65, 95), lambda x: x != [])
        self.check_region((85, 60, 85, 70), lambda x: x != [])
        self.check_region((105, 100, 115, 100), lambda x: x != [])
        self.check_region((90, 130, 90, 140), lambda x: x != [])
        self.check_region((65, 120, 75, 120), lambda x: x != [])
        self.check_region((105, 70, 110, 70), lambda x: x != [])

        # But now we'll have an additional instance
        self.check_region((75, 55, 85, 55), lambda x: x != [])
        self.check_region((125, 85, 135, 85), lambda x: x != [])
        self.check_region((105, 110, 105, 120), lambda x: x != [])

        self.cfg.showWholePass = True

        self.cache_output()

        # same again
        self.check_region((55, 95, 65, 95), lambda x: x != [])
        self.check_region((85, 60, 85, 70), lambda x: x != [])
        self.check_region((105, 100, 115, 100), lambda x: x != [])
        self.check_region((90, 130, 90, 140), lambda x: x != [])
        self.check_region((65, 120, 75, 120), lambda x: x != [])
        self.check_region((105, 70, 110, 70), lambda x: x != [])
        self.check_region((75, 55, 85, 55), lambda x: x != [])
        self.check_region((125, 85, 135, 85), lambda x: x != [])
        self.check_region((105, 110, 105, 120), lambda x: x != [])

        # But now an extra previous draw
        self.check_region((30, 105, 40, 105), lambda x: x != [])
        self.check_region((50, 80, 50, 90), lambda x: x != [])
        self.check_region((45, 130, 55, 130), lambda x: x != [])
        self.check_region((30, 150, 40, 150), lambda x: x != [])

        rdtest.log.success("Mesh rendering is as expected")

        self.cfg.showWholePass = False
        self.cfg.showAllInstances = False

        # Go back to instance 0. We can ignore cfg.second now
        self.cfg.curInstance = 0
        self.cfg.position.vertexResourceId = inst0.vertexResourceId
        self.cfg.position.vertexByteOffset = inst0.vertexByteOffset

        self.cache_output()

        # Just above top-left, no result
        self.check_vertex(55, 60,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        # Just inside top-left, first vertex
        self.check_vertex(65, 70, (0, 0))
        # Outside top-right, inside the second instance, but because we only have one instance showing should return
        # no result
        self.check_vertex(115, 60,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        self.check_vertex(80, 60,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        # In the first triangle near the top right
        self.check_vertex(105, 70, (1, 0))
        # In the second triangle near the top right
        self.check_vertex(110, 70, (3, 0))
        # In the second triangle near the middle, would be in the second instance
        self.check_vertex(95, 110, (4, 0))
        # In the second triangle near the bottom right
        self.check_vertex(110, 130, (4, 0))

        rdtest.log.success("Instance 0 picking is as expected")

        # if we look at only instance 1, the results should change
        self.cfg.curInstance = 1
        self.cfg.position.vertexResourceId = inst1.vertexResourceId
        self.cfg.position.vertexByteOffset = inst1.vertexByteOffset

        self.cache_output()

        self.check_vertex(55, 60,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        self.check_vertex(65, 70,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        self.check_vertex(115, 60, (1, 1))
        self.check_vertex(80, 60, (0, 1))
        self.check_vertex(105, 70, (1, 1))
        self.check_vertex(110, 70, (1, 1))
        self.check_vertex(95, 110, (5, 1))
        self.check_vertex(110, 130,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))

        rdtest.log.success("Instance 1 picking is as expected")

        # Now look at both instances together, this goes 'in order' so if there is overlap the first instance wins
        self.cfg.showAllInstances = True

        self.cache_output()

        self.check_vertex(55, 60,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        self.check_vertex(65, 70, (0, 0))
        self.check_vertex(115, 60, (1, 1))
        self.check_vertex(80, 60, (0, 1))
        self.check_vertex(105, 70, (1, 0))
        self.check_vertex(110, 70, (3, 0))
        self.check_vertex(95, 110, (4, 0))
        self.check_vertex(110, 130, (4, 0))

        rdtest.log.success("Both instance picking is as expected")

        self.controller.SetFrameEvent(
            self.find_draw("Points").next.eventId, False)

        # Only one instance, just check we can see the points
        self.cfg.curInstance = 0
        self.cfg.position = self.controller.GetPostVSData(0, 0, self.cfg.type)
        self.cfg.position.nearPlane = 1.0
        self.cfg.position.farPlane = 100.0

        self.cache_output()

        # Picking points doesn't have any primitive, it should pick as long as it's close to the point
        self.check_vertex(55, 60, (0, 0))
        self.check_vertex(65, 70, (0, 0))

        self.check_vertex(105, 65, (1, 0))
        self.check_vertex(115, 135, (2, 0))
        self.check_vertex(65, 130, (3, 0))
        self.check_vertex(60, 125, (3, 0))

        rdtest.log.success("Point picking is as expected")

        self.controller.SetFrameEvent(
            self.find_draw("Stride 0").next.eventId, False)

        self.cfg.position = self.controller.GetPostVSData(0, 0, self.cfg.type)
        self.cfg.position.nearPlane = 1.0
        self.cfg.position.farPlane = 100.0

        self.cache_output()

        # Stride of 0 is unusual but valid, ensure vertex picking still works
        self.check_vertex(55, 60, (0, 0))
        self.check_vertex(65, 70, (0, 0))

        self.check_vertex(105, 65,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
        self.check_vertex(115, 135,
                          (rd.ReplayOutput.NoResult, rd.ReplayOutput.NoResult))
Example #26
0
    def check_capture(self):
        draw = self.find_draw("Color Draw")

        self.check(draw is not None)

        draw = draw.next

        self.controller.SetFrameEvent(draw.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        self.check_pixel_value(pipe.GetOutputTargets()[0].resourceId, 0.5, 0.5,
                               [0.0, 1.0, 0.0, 1.0])

        rdtest.log.success("Captured loaded with color as expected")

        postvs_data = self.get_postvs(draw, rd.MeshDataStage.VSOut, 0,
                                      draw.numIndices)

        postvs_ref = {
            0: {
                'vtx': 0,
                'idx': 0,
                'SV_POSITION': [-0.5, -0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [0.0, 0.0],
            },
            1: {
                'vtx': 1,
                'idx': 1,
                'SV_POSITION': [0.0, 0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [0.0, 1.0],
            },
            2: {
                'vtx': 2,
                'idx': 2,
                'SV_POSITION': [0.5, -0.5, 0.0, 1.0],
                'COLOR': [0.0, 1.0, 0.0, 1.0],
                'TEXCOORD': [1.0, 0.0],
            },
        }

        self.check_mesh_data(postvs_ref, postvs_data)

        rdtest.log.success("Mesh data is correct")

        tex = rd.TextureDisplay()
        tex.overlay = rd.DebugOverlay.Drawcall
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId

        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        out.SetTextureDisplay(tex)

        out.Display()

        overlay_id = out.GetDebugOverlayTexID()

        v = pipe.GetViewport(0)

        self.check_pixel_value(overlay_id,
                               int(0.5 * v.width),
                               int(0.5 * v.height), [0.8, 0.1, 0.8, 1.0],
                               eps=1.0 / 256.0)

        out.Shutdown()

        rdtest.log.success("Overlay color is as expected")
    def check_capture(self):
        draw = self.find_draw("Draw")

        self.check(draw is not None)

        self.controller.SetFrameEvent(draw.eventId, False)

        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(
            rd.CreateHeadlessWindowingData(100, 100),
            rd.ReplayOutputType.Texture)

        self.check(out is not None)

        ref = {
            0: {
                'SNORM': [1.0, -1.0, 1.0, -1.0],
                'UNORM': [
                    12345.0 / 65535.0, 6789.0 / 65535.0, 1234.0 / 65535.0,
                    567.0 / 65535.0
                ],
                'UINT': [12345, 6789, 1234, 567],
                'ARRAY0': [1.0, 2.0],
                'ARRAY1': [3.0, 4.0],
                'ARRAY2': [5.0, 6.0],
                'MATRIX0': [7.0, 8.0],
                'MATRIX1': [9.0, 10.0],
            },
            1: {
                'SNORM': [
                    32766.0 / 32767.0, -32766.0 / 32767.0, 16000.0 / 32767.0,
                    -16000.0 / 32767.0
                ],
                'UNORM': [
                    56.0 / 65535.0, 7890.0 / 65535.0, 123.0 / 65535.0,
                    4567.0 / 65535.0
                ],
                'UINT': [56, 7890, 123, 4567],
                'ARRAY0': [11.0, 12.0],
                'ARRAY1': [13.0, 14.0],
                'ARRAY2': [15.0, 16.0],
                'MATRIX0': [17.0, 18.0],
                'MATRIX1': [19.0, 20.0],
            },
            2: {
                'SNORM': [5.0 / 32767.0, -5.0 / 32767.0, 0.0, 0.0],
                'UNORM': [
                    8765.0 / 65535.0, 43210.0 / 65535.0, 987.0 / 65535.0,
                    65432.0 / 65535.0
                ],
                'UINT': [8765, 43210, 987, 65432],
                'ARRAY0': [21.0, 22.0],
                'ARRAY1': [23.0, 24.0],
                'ARRAY2': [25.0, 26.0],
                'MATRIX0': [27.0, 28.0],
                'MATRIX1': [29.0, 30.0],
            },
        }

        in_ref = copy.deepcopy(ref)
        vsout_ref = copy.deepcopy(ref)
        gsout_ref = ref

        vsout_ref[0]['SV_Position'] = [-0.5, 0.5, 0.0, 1.0]
        gsout_ref[0]['SV_Position'] = [0.5, -0.5, 0.4, 1.2]

        vsout_ref[1]['SV_Position'] = [0.0, -0.5, 0.0, 1.0]
        gsout_ref[1]['SV_Position'] = [-0.5, 0.0, 0.4, 1.2]

        vsout_ref[2]['SV_Position'] = [0.5, 0.5, 0.0, 1.0]
        gsout_ref[2]['SV_Position'] = [0.5, 0.5, 0.4, 1.2]

        self.check_mesh_data(in_ref, self.get_vsin(draw))
        rdtest.log.success("Vertex input data is as expected")

        self.check_mesh_data(vsout_ref,
                             self.get_postvs(rd.MeshDataStage.VSOut))

        rdtest.log.success("Vertex output data is as expected")

        self.check_mesh_data(gsout_ref,
                             self.get_postvs(rd.MeshDataStage.GSOut))

        rdtest.log.success("Geometry output data is as expected")

        pipe: rd.PipeState = self.controller.GetPipelineState()

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        texdetails = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(texdetails.width / 2),
                                              int(texdetails.height / 2), 0, 0,
                                              0)

        if not rdtest.value_compare(picked.floatValue, [0.0, 1.0, 0.0, 1.0]):
            raise rdtest.TestFailureException(
                "Picked value {} doesn't match expectation".format(
                    picked.floatValue))

        rdtest.log.success("Triangle picked value is as expected")

        out.Shutdown()
Example #28
0
    def check_capture(self):
        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture)

        self.check(out is not None)

        test_marker: rd.DrawcallDescription = self.find_draw("Test")

        self.controller.SetFrameEvent(test_marker.next.eventId, True)

        pipe: rd.PipeState = self.controller.GetPipelineState()
        
        col_tex: rd.ResourceId = pipe.GetOutputTargets()[0].resourceId

        tex = rd.TextureDisplay()
        tex.resourceId = col_tex

        # Check the actual output is as expected first.

        # Background around the outside
        self.check_pixel_value(col_tex, 0.1, 0.1, [0.2, 0.2, 0.2, 1.0])
        self.check_pixel_value(col_tex, 0.8, 0.1, [0.2, 0.2, 0.2, 1.0])
        self.check_pixel_value(col_tex, 0.5, 0.95, [0.2, 0.2, 0.2, 1.0])

        # Large dark grey triangle
        self.check_pixel_value(col_tex, 0.5, 0.1, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(col_tex, 0.5, 0.9, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(col_tex, 0.2, 0.9, [0.1, 0.1, 0.1, 1.0])
        self.check_pixel_value(col_tex, 0.8, 0.9, [0.1, 0.1, 0.1, 1.0])

        # Red upper half triangle
        self.check_pixel_value(col_tex, 0.3, 0.4, [1.0, 0.0, 0.0, 1.0])
        # Blue lower half triangle
        self.check_pixel_value(col_tex, 0.3, 0.6, [0.0, 0.0, 1.0, 1.0])

        # Floating clipped triangle
        self.check_pixel_value(col_tex, 335, 140, [0.0, 0.0, 0.0, 1.0])
        self.check_pixel_value(col_tex, 340, 140, [0.2, 0.2, 0.2, 1.0])

        # Triangle size triangles
        self.check_pixel_value(col_tex, 200, 51, [1.0, 0.5, 1.0, 1.0])
        self.check_pixel_value(col_tex, 200, 65, [1.0, 1.0, 0.0, 1.0])
        self.check_pixel_value(col_tex, 200, 79, [0.0, 1.0, 1.0, 1.0])
        self.check_pixel_value(col_tex, 200, 93, [0.0, 1.0, 0.0, 1.0])

        for overlay in rd.DebugOverlay:
            if overlay == rd.DebugOverlay.NoOverlay:
                continue

            # These overlays are just displaymodes really, not actually separate overlays
            if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                continue

            # We'll test the clear-before-X overlays seperately, for both colour and depth
            if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                continue

            rdtest.log.print("Checking overlay {} in main draw".format(str(overlay)))

            tex.overlay = overlay
            out.SetTextureDisplay(tex)

            out.Display()

            eps = 1.0 / 256.0

            overlay_id: rd.ResourceId = out.GetDebugOverlayTexID()

            # We test a few key spots:
            #  4 points along the left side of the big triangle, above/in/below its intersection with the tri behind
            #  4 points outside all triangles
            #  The overlap between the big tri and the bottom tri, and between it and the right backface culled tri
            #  The bottom tri's part that sticks out
            #  The two parts of the backface culled tri that stick out
            #  The depth clipped tri, in and out of clipping
            #  The 4 triangle size test triangles

            if overlay == rd.DebugOverlay.Drawcall:
                self.check_pixel_value(overlay_id, 150, 90, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 0.0, 0.0, 0.5], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 0.0, 0.0, 0.5], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 0.0, 0.0, 0.5], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 0.0, 0.0, 0.5], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 285, 135, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [0.8, 0.1, 0.8, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.Wireframe:
                # Wireframe we only test a limited set to avoid hitting implementation variations of line raster
                # We also have to fudge a little because the lines might land on adjacent pixels

                x = 142
                picked: rd.PixelValue = self.controller.PickPixel(overlay_id, x, 150, rd.Subresource(), rd.CompType.Typeless)
                if picked.floatValue[3] == 0.0:
                    x = 141

                self.check_pixel_value(overlay_id, x, 90, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, x, 130, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, x, 160, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, x, 200, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [200.0/255.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [200.0/255.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [200.0/255.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [200.0/255.0, 1.0, 0.0, 0.0], eps=eps)

                y = 149
                picked: rd.PixelValue = self.controller.PickPixel(overlay_id, 325, y, rd.Subresource(), rd.CompType.Typeless)
                if picked.floatValue[3] == 0.0:
                    y = 150

                self.check_pixel_value(overlay_id, 325, y, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, y, [200.0/255.0, 1.0, 0.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.Depth:
                self.check_pixel_value(overlay_id, 150, 90, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [0.0, 1.0, 0.0, 1.0], eps=eps)
                # Intersection with lesser depth - depth fail
                self.check_pixel_value(overlay_id, 150, 160, [1.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [0.0, 1.0, 0.0, 1.0], eps=eps)

                # Backface culled triangle
                self.check_pixel_value(overlay_id, 285, 135, [1.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [1.0, 0.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [0.0, 1.0, 0.0, 1.0], eps=eps)
                # Depth clipped part of tri
                self.check_pixel_value(overlay_id, 340, 145, [1.0, 0.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.Stencil:
                self.check_pixel_value(overlay_id, 150, 90, [0.0, 1.0, 0.0, 1.0], eps=eps)
                # Intersection with different stencil - stencil fail
                self.check_pixel_value(overlay_id, 150, 130, [1.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [0.0, 1.0, 0.0, 1.0], eps=eps)

                # Backface culled triangle
                self.check_pixel_value(overlay_id, 285, 135, [1.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [1.0, 0.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [0.0, 1.0, 0.0, 1.0], eps=eps)
                # Depth clipped part of tri
                self.check_pixel_value(overlay_id, 340, 145, [1.0, 0.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.BackfaceCull:
                self.check_pixel_value(overlay_id, 150, 90, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 1.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [0.0, 1.0, 0.0, 1.0], eps=eps)

                # Backface culled triangle
                self.check_pixel_value(overlay_id, 285, 135, [1.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [1.0, 0.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.ViewportScissor:
                # Inside viewport
                self.check_pixel_value(overlay_id, 50, 50, [0.2*0.7, 0.2*0.7, 0.9*0.7, 0.7*0.7], eps=eps)
                self.check_pixel_value(overlay_id, 350, 50, [0.2*0.7, 0.2*0.7, 0.9*0.7, 0.7*0.7], eps=eps)
                self.check_pixel_value(overlay_id, 50, 250, [0.2*0.7, 0.2*0.7, 0.9*0.7, 0.7*0.7], eps=eps)
                self.check_pixel_value(overlay_id, 350, 250, [0.2*0.7, 0.2*0.7, 0.9*0.7, 0.7*0.7], eps=eps)

                # Viewport border
                self.check_pixel_value(overlay_id, 12, 12, [0.1, 0.1, 0.1, 1.0], eps=eps)

                # Outside viewport (not on scissor border)
                self.check_pixel_value(overlay_id, 6, 6, [0.0, 0.0, 0.0, 0.0], eps=eps)

                # Scissor border
                self.check_pixel_value(overlay_id, 0, 0, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 20, 0, [0.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 40, 0, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 60, 0, [0.0, 0.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 60, 0, [0.0, 0.0, 0.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.QuadOverdrawDraw:
                self.check_pixel_value(overlay_id, 150, 90, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [1.0, 1.0, 1.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [2.0, 2.0, 2.0, 2.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [1.0, 1.0, 1.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [1.0, 1.0, 1.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 285, 135, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [1.0, 1.0, 1.0, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.QuadOverdrawPass:
                self.check_pixel_value(overlay_id, 150, 90, [1.0, 1.0, 1.0, 1.0], eps=eps)

                # Do an extra tap here where we overlap with the extreme-background largest triangle, to show that
                # overdraw
                self.check_pixel_value(overlay_id, 150, 100, [2.0, 2.0, 2.0, 2.0], eps=eps)

                self.check_pixel_value(overlay_id, 150, 130, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [2.0, 2.0, 2.0, 2.0], eps=eps)

                # Two of these have overdraw from the pass due to the large background triangle
                self.check_pixel_value(overlay_id, 125, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [1.0, 1.0, 1.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [3.0, 3.0, 3.0, 3.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [2.0, 2.0, 2.0, 2.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [2.0, 2.0, 2.0, 2.0], eps=eps)

                self.check_pixel_value(overlay_id, 285, 135, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [1.0, 1.0, 1.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 51, [2.0, 2.0, 2.0, 2.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [2.0, 2.0, 2.0, 2.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [2.0, 2.0, 2.0, 2.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [2.0, 2.0, 2.0, 2.0], eps=eps)
            elif overlay == rd.DebugOverlay.TriangleSizeDraw:
                eps = 1.0

                self.check_pixel_value(overlay_id, 150, 90, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [2128.0, 2128.0, 2128.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [2128.0, 2128.0, 2128.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 285, 135, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [531.0, 531.0, 531.0, 531.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.0, 0.0, 0.0, 0.0], eps=eps)

                eps = 0.01

                self.check_pixel_value(overlay_id, 200, 51, [8.305, 8.305, 8.305, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [5.316, 5.316, 5.316, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [3.0, 3.0, 3.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [1.33, 1.33, 1.33, 1.0], eps=eps)
            elif overlay == rd.DebugOverlay.TriangleSizePass:
                eps = 1.0

                self.check_pixel_value(overlay_id, 150, 90, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 130, [3324.0, 3324.0, 3324.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 160, [3324.0, 3324.0, 3324.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 150, 200, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 125, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 125, 250, [43072.0, 43072.0, 43072.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 60, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 250, [43072.0, 43072.0, 43072.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 175, [2128.0, 2128.0, 2128.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 250, 150, [10632.0, 10632.0, 10632.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 220, 190, [2128.0, 2128.0, 2128.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 285, 135, [0.0, 0.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 285, 165, [43072.0, 43072.0, 43072.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 330, 145, [531.0, 531.0, 531.0, 531.0], eps=eps)
                self.check_pixel_value(overlay_id, 340, 145, [0.0, 0.0, 0.0, 0.0], eps=eps)

                eps = 0.01

                self.check_pixel_value(overlay_id, 200, 51, [8.305, 8.305, 8.305, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 65, [5.316, 5.316, 5.316, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 79, [3.0, 3.0, 3.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 93, [1.33, 1.33, 1.33, 1.0], eps=eps)

            rdtest.log.success("Picked pixels are as expected for {}".format(str(overlay)))

        # Now check clear-before-X by hand, for colour and for depth

        depth_tex: rd.ResourceId = pipe.GetDepthTarget().resourceId

        eps = 1.0/256.0

        # Check colour and depth before-hand
        self.check_pixel_value(col_tex, 250, 250, [0.1, 0.1, 0.1, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 125, [1.0, 0.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 175, [0.0, 0.0, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 50, 50, [0.2, 0.2, 0.2, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 291, 150, [0.977, 0.977, 0.977, 1.0], eps=0.075)
        self.check_pixel_value(col_tex, 200, 51, [1.0, 0.5, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 65, [1.0, 1.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 79, [0.0, 1.0, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)

        eps = 0.001

        self.check_pixel_value(depth_tex, 160, 135, [0.9, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 160, 165, [0.0, 0.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 150, [0.5, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 250, [0.95, 0.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 50, 50, [1.0, 0.0/255.0, 0.0, 1.0], eps=eps)

        # Check clear before pass
        tex.resourceId = col_tex
        tex.overlay = rd.DebugOverlay.ClearBeforePass
        out.SetTextureDisplay(tex)
        out.Display()

        eps = 1.0/256.0

        self.check_pixel_value(col_tex, 250, 250, [0.1, 0.1, 0.1, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 125, [1.0, 0.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 175, [0.0, 0.0, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 50, 50, [0.0, 0.0, 0.0, 0.0], eps=eps)
        self.check_pixel_value(col_tex, 291, 150, [0.977, 0.977, 0.977, 1.0], eps=0.075)
        self.check_pixel_value(col_tex, 200, 51, [1.0, 0.5, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 65, [1.0, 1.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 79, [0.0, 1.0, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)

        tex.resourceId = depth_tex
        tex.overlay = rd.DebugOverlay.ClearBeforePass
        out.SetTextureDisplay(tex)
        out.Display()

        eps = 0.001

        self.check_pixel_value(depth_tex, 160, 135, [0.9, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 160, 165, [0.0, 0.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 150, [0.5, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 250, [0.95, 0.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 50, 50, [1.0, 0.0/255.0, 0.0, 1.0], eps=eps)

        rdtest.log.success("Clear before pass colour and depth values as expected")

        # Check clear before draw
        tex.resourceId = col_tex
        tex.overlay = rd.DebugOverlay.ClearBeforeDraw
        out.SetTextureDisplay(tex)
        out.Display()

        eps = 1.0/256.0

        # These are all pass triangles, should be cleared
        self.check_pixel_value(col_tex, 250, 250, [0.0, 0.0, 0.0, 0.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 125, [0.0, 0.0, 0.0, 0.0], eps=eps)
        self.check_pixel_value(col_tex, 125, 175, [0.0, 0.0, 0.0, 0.0], eps=eps)
        self.check_pixel_value(col_tex, 50, 50, [0.0, 0.0, 0.0, 0.0], eps=eps)

        # These should be identical
        self.check_pixel_value(col_tex, 291, 150, [0.977, 0.977, 0.977, 1.0], eps=0.075)
        self.check_pixel_value(col_tex, 200, 51, [1.0, 0.5, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 65, [1.0, 1.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 79, [0.0, 1.0, 1.0, 1.0], eps=eps)
        self.check_pixel_value(col_tex, 200, 93, [0.0, 1.0, 0.0, 1.0], eps=eps)

        tex.resourceId = depth_tex
        tex.overlay = rd.DebugOverlay.ClearBeforeDraw
        out.SetTextureDisplay(tex)
        out.Display()

        eps = 0.001

        # Without the pass, depth/stencil results are different
        self.check_pixel_value(depth_tex, 160, 135, [0.5, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 160, 165, [0.5, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 150, [0.5, 85.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 250, 250, [1.0, 0.0/255.0, 0.0, 1.0], eps=eps)
        self.check_pixel_value(depth_tex, 50, 50, [1.0, 0.0/255.0, 0.0, 1.0], eps=eps)

        rdtest.log.success("Clear before draw colour and depth values as expected")

        rdtest.log.success("All overlays as expected for main draw")

        # Now test overlays on a render-to-slice/mip case
        for mip in [2, 3]:
            sub_marker: rd.DrawcallDescription = self.find_draw("Subresources mip {}".format(mip))

            self.controller.SetFrameEvent(sub_marker.next.eventId, True)

            pipe: rd.PipeState = self.controller.GetPipelineState()

            col_tex = pipe.GetOutputTargets()[0].resourceId
            sub = rd.Subresource(pipe.GetOutputTargets()[0].firstMip, 0, 0)

            for overlay in rd.DebugOverlay:
                if overlay == rd.DebugOverlay.NoOverlay:
                    continue

                # These overlays are just displaymodes really, not actually separate overlays
                if overlay == rd.DebugOverlay.NaN or overlay == rd.DebugOverlay.Clipping:
                    continue

                if overlay == rd.DebugOverlay.ClearBeforeDraw or overlay == rd.DebugOverlay.ClearBeforePass:
                    continue

                rdtest.log.success("Checking overlay {} with mip/slice rendering".format(str(overlay)))

                tex.resourceId = col_tex
                tex.overlay = overlay
                tex.subresource = sub
                out.SetTextureDisplay(tex)

                out.Display()

                overlay_id: rd.ResourceId = out.GetDebugOverlayTexID()

                shift = 0
                if mip == 3:
                    shift = 1

                # All values in mip 0 should be 0 for all overlays
                self.check_pixel_value(overlay_id, 200, 150, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))
                self.check_pixel_value(overlay_id, 197, 147, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))
                self.check_pixel_value(overlay_id, 203, 153, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))

                rdtest.log.success("Other mips are empty as expected for overlay {}".format(str(overlay)))

                if overlay == rd.DebugOverlay.Drawcall:
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [0.8, 0.1, 0.8, 1.0], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift, [0.0, 0.0, 0.0, 0.5], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 70 >> shift, 34 >> shift, [0.8, 0.1, 0.8, 1.0], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift, [0.0, 0.0, 0.0, 0.5], sub=sub, eps=eps)
                elif overlay == rd.DebugOverlay.Wireframe:
                    self.check_pixel_value(overlay_id, 36 >> shift, 36 >> shift, [200.0 / 255.0, 1.0, 0.0, 1.0],
                                           sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 36 >> shift, 50 >> shift, [200.0 / 255.0, 1.0, 0.0, 1.0],
                                           sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [200.0 / 255.0, 1.0, 0.0, 0.0],
                                           sub=sub, eps=eps)
                elif overlay == rd.DebugOverlay.Depth or overlay == rd.DebugOverlay.Stencil:
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [0.0, 1.0, 0.0, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift, [0.0, 1.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 34 >> shift, [1.0, 0.0, 0.0, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift, [0.0, 1.0, 0.0, 0.0], sub=sub)
                elif overlay == rd.DebugOverlay.BackfaceCull:
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [0.0, 1.0, 0.0, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift, [0.0, 1.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 34 >> shift, [1.0, 0.0, 0.0, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift, [0.0, 1.0, 0.0, 0.0], sub=sub)
                elif overlay == rd.DebugOverlay.ViewportScissor:
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift,
                                           [0.2 * 0.7, 0.2 * 0.7, 0.9 * 0.7, 0.7 * 0.7], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift,
                                           [0.2 * 0.7, 0.2 * 0.7, 0.9 * 0.7, 0.7 * 0.7], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 70 >> shift, 34 >> shift,
                                           [0.2 * 0.7, 0.2 * 0.7, 0.9 * 0.7, 0.7 * 0.7], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift,
                                           [0.2 * 0.7, 0.2 * 0.7, 0.9 * 0.7, 0.7 * 0.7], sub=sub, eps=eps)

                    if mip == 2:
                        self.check_pixel_value(overlay_id, 6, 6, [0.1, 0.1, 0.1, 1.0], sub=sub, eps=eps)
                        self.check_pixel_value(overlay_id, 4, 4, [0.0, 0.0, 0.0, 0.0], sub=sub)
                        self.check_pixel_value(overlay_id, 0, 0, [1.0, 1.0, 1.0, 1.0], sub=sub)
                        self.check_pixel_value(overlay_id, 20, 0, [0.0, 0.0, 0.0, 1.0], sub=sub)
                        self.check_pixel_value(overlay_id, 40, 0, [1.0, 1.0, 1.0, 1.0], sub=sub)
                        self.check_pixel_value(overlay_id, 60, 0, [0.0, 0.0, 0.0, 1.0], sub=sub)
                    else:
                        self.check_pixel_value(overlay_id, 4, 4, [0.1, 0.1, 0.1, 1.0], sub=sub, eps=eps)
                        self.check_pixel_value(overlay_id, 0, 0, [1.0, 1.0, 1.0, 1.0], sub=sub)
                        self.check_pixel_value(overlay_id, 20, 0, [0.0, 0.0, 0.0, 1.0], sub=sub)
                        self.check_pixel_value(overlay_id, 40, 0, [1.0, 1.0, 1.0, 1.0], sub=sub)
                elif overlay == rd.DebugOverlay.QuadOverdrawDraw or overlay == rd.DebugOverlay.QuadOverdrawPass:
                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [1.0, 1.0, 1.0, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift, [0.0, 0.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift, [0.0, 0.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 50 >> shift, 45 >> shift, [2.0, 2.0, 2.0, 2.0], sub=sub)
                elif overlay == rd.DebugOverlay.TriangleSizeDraw or overlay == rd.DebugOverlay.TriangleSizePass:
                    if mip == 2:
                        self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [585.0, 585.0, 585.0, 1.0], sub=sub)
                    else:
                        self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift, [151.75, 151.75, 151.75, 1.0], sub=sub)
                    self.check_pixel_value(overlay_id, 30 >> shift, 36 >> shift, [0.0, 0.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 34 >> shift, [0.0, 0.0, 0.0, 0.0], sub=sub)
                    self.check_pixel_value(overlay_id, 70 >> shift, 20 >> shift, [0.0, 0.0, 0.0, 0.0], sub=sub)
                    if mip == 2:
                        self.check_pixel_value(overlay_id, 50 >> shift, 45 >> shift, [117.0, 117.0, 117.0, 1.0], sub=sub)
                    else:
                        self.check_pixel_value(overlay_id, 50 >> shift, 45 >> shift, [30.359375, 30.359375, 30.359375, 1.0], sub=sub)

                rdtest.log.success("Picked values are correct for mip {} overlay {}".format(sub.mip, str(overlay)))

        out.Shutdown()
Example #29
0
    def check_capture(self):
        fill = self.find_action("vkCmdFillBuffer")

        self.check(fill is not None)

        buffer_usage = {}

        for usage in self.controller.GetUsage(fill.copyDestination):
            usage: rd.EventUsage
            if usage.eventId not in buffer_usage:
                buffer_usage[usage.eventId] = []
            buffer_usage[usage.eventId].append(usage.usage)

        # The texture is the backbuffer
        tex = self.get_last_action().copyDestination

        for level in ["Primary", "Secondary"]:
            rdtest.log.print("Checking {} indirect calls".format(level))

            final = self.find_action("{}: Final".format(level))

            indirect_count_root = self.find_action(
                "{}: KHR_action_indirect_count".format(level))

            self.controller.SetFrameEvent(final.eventId, False)

            # Check the top row, non indirect count and always present
            self.check_pixel_value(tex, 60, 60, [1.0, 0.0, 0.0, 1.0])
            self.check_pixel_value(tex, 100, 60, [0.0, 0.0, 1.0, 1.0])
            self.check_pixel_value(tex, 145, 35, [1.0, 1.0, 0.0, 1.0])
            self.check_pixel_value(tex, 205, 35, [0.0, 1.0, 1.0, 1.0])

            # if present, check bottom row of indirect count as well as post-count calls
            if indirect_count_root is not None:
                self.check_pixel_value(tex, 60, 220, [0.0, 1.0, 0.0, 1.0])
                self.check_pixel_value(tex, 100, 220, [1.0, 0.0, 1.0, 1.0])
                self.check_pixel_value(tex, 145, 185, [0.5, 1.0, 0.0, 1.0])
                self.check_pixel_value(tex, 205, 185, [0.5, 0.0, 1.0, 1.0])

                self.check_pixel_value(tex, 340, 40, [1.0, 0.5, 0.0, 1.0])
                self.check_pixel_value(tex, 340, 115, [1.0, 0.5, 0.5, 1.0])
                self.check_pixel_value(tex, 340, 190, [1.0, 0.0, 0.5, 1.0])

            dispatches = self.find_action("{}: Dispatches".format(level))

            # Set up a ReplayOutput and TextureSave for quickly testing the action highlight overlay
            self.out: rd.ReplayOutput = self.controller.CreateOutput(
                rd.CreateHeadlessWindowingData(100, 100),
                rd.ReplayOutputType.Texture)

            self.check(self.out is not None)

            # Rewind to the start of the capture
            action: rd.ActionDescription = dispatches.children[0]
            while action.previous is not None:
                action = action.previous

            # Ensure we can select all actions
            while action is not None:
                self.controller.SetFrameEvent(action.eventId, False)
                action = action.next

            rdtest.log.success("Selected all {} actions".format(level))

            self.check(dispatches
                       and len(real_action_children(dispatches)) == 3)

            self.check(dispatches.children[0].dispatchDimension == (0, 0, 0))
            self.check(dispatches.children[1].dispatchDimension == (1, 1, 1))
            self.check(dispatches.children[2].dispatchDimension == (3, 4, 5))

            rdtest.log.success(
                "{} Indirect dispatches are the correct dimensions".format(
                    level))

            self.controller.SetFrameEvent(dispatches.children[2].eventId,
                                          False)

            pipe: rd.PipeState = self.controller.GetPipelineState()

            ssbo: rd.BoundResource = pipe.GetReadWriteResources(
                rd.ShaderStage.Compute)[0].resources[0]
            data: bytes = self.controller.GetBufferData(ssbo.resourceId, 0, 0)

            rdtest.log.print("Got {} bytes of uints".format(len(data)))

            uints = [
                struct.unpack_from('=4L', data, offs)
                for offs in range(0, len(data), 16)
            ]

            for x in range(0, 6):  # 3 groups of 2 threads each
                for y in range(0, 8):  # 3 groups of 2 threads each
                    for z in range(0, 5):  # 5 groups of 1 thread each
                        idx = 100 + z * 8 * 6 + y * 6 + x
                        if not rdtest.value_compare(uints[idx],
                                                    [x, y, z, 12345]):
                            raise rdtest.TestFailureException(
                                'expected thread index data @ {},{},{}: {} is not as expected: {}'
                                .format(x, y, z, uints[idx], [x, y, z, 12345]))

            rdtest.log.success(
                "Dispatched buffer contents are as expected for {}".format(
                    level))

            empties = self.find_action("{}: Empty draws".format(level))

            self.check(empties and len(real_action_children(empties)) == 2)

            action: rd.ActionDescription
            for action in real_action_children(empties):
                self.check(action.numIndices == 0)
                self.check(action.numInstances == 0)

                self.controller.SetFrameEvent(action.eventId, False)

                # Check that we have empty PostVS
                postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut,
                                              0, 1)
                self.check(len(postvs_data) == 0)

                # No samples should be passing in the empties
                self.check_overlay([])

            rdtest.log.success("{} empty actions are empty".format(level))

            indirects = self.find_action("{}: Indirect draws".format(level))

            self.check('vkCmdDrawIndirect' in indirects.children[0].customName)
            self.check(
                'vkCmdDrawIndexedIndirect' in indirects.children[1].customName)
            self.check(len(real_action_children(indirects.children[1])) == 2)

            rdtest.log.success(
                "Correct number of {} indirect draws".format(level))

            # vkCmdDrawIndirect(...)
            action = indirects.children[0]
            self.check(action.numIndices == 3)
            self.check(action.numInstances == 2)

            self.controller.SetFrameEvent(action.eventId, False)

            self.check(
                rd.ResourceUsage.Indirect in buffer_usage[action.eventId])

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 0,
                    'gl_PerVertex_var.gl_Position': [-0.8, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 1,
                    'gl_PerVertex_var.gl_Position': [-0.7, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 2,
                    'gl_PerVertex_var.gl_Position': [-0.6, -0.5, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay([(60, 40)])

            rdtest.log.success("{} {} is as expected".format(
                level, action.customName))

            self.check(rd.ResourceUsage.Indirect in buffer_usage[
                indirects.children[1].eventId])

            # vkCmdDrawIndexedIndirect[0](...)
            action = indirects.children[1].children[0]
            self.check(action.numIndices == 3)
            self.check(action.numInstances == 3)

            self.controller.SetFrameEvent(action.eventId, False)

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

            # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
            # indices
            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 6,
                    'gl_PerVertex_var.gl_Position': [-0.6, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 7,
                    'gl_PerVertex_var.gl_Position': [-0.5, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 8,
                    'gl_PerVertex_var.gl_Position': [-0.4, -0.5, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay([(100, 40)])

            rdtest.log.success("{} {} is as expected".format(
                level, action.customName))

            # vkCmdDrawIndexedIndirect[1](...)
            action = indirects.children[1].children[1]
            self.check(action.numIndices == 6)
            self.check(action.numInstances == 2)

            self.controller.SetFrameEvent(action.eventId, False)

            # Check that we have PostVS as expected
            postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

            postvs_ref = {
                0: {
                    'vtx': 0,
                    'idx': 9,
                    'gl_PerVertex_var.gl_Position': [-0.4, -0.5, 0.0, 1.0]
                },
                1: {
                    'vtx': 1,
                    'idx': 10,
                    'gl_PerVertex_var.gl_Position': [-0.3, -0.8, 0.0, 1.0]
                },
                2: {
                    'vtx': 2,
                    'idx': 11,
                    'gl_PerVertex_var.gl_Position': [-0.2, -0.8, 0.0, 1.0]
                },
                3: {
                    'vtx': 3,
                    'idx': 12,
                    'gl_PerVertex_var.gl_Position': [-0.1, -0.5, 0.0, 1.0]
                },
                4: {
                    'vtx': 4,
                    'idx': 13,
                    'gl_PerVertex_var.gl_Position': [0.0, -0.8, 0.0, 1.0]
                },
                5: {
                    'vtx': 5,
                    'idx': 14,
                    'gl_PerVertex_var.gl_Position': [0.1, -0.8, 0.0, 1.0]
                },
            }

            self.check_mesh_data(postvs_ref, postvs_data)
            self.check(len(postvs_data) == len(
                postvs_ref))  # We shouldn't have any extra vertices

            self.check_overlay([(140, 40), (200, 40)])

            rdtest.log.success("{} {} is as expected".format(
                level, action.customName))

            if indirect_count_root is not None:
                self.check(indirect_count_root.children[0].customName ==
                           '{}: Empty count draws'.format(level))
                self.check(indirect_count_root.children[1].customName ==
                           '{}: Indirect count draws'.format(level))

                empties = indirect_count_root.children[0]

                self.check(empties and len(real_action_children(empties)) == 3)

                action: rd.ActionDescription
                for action in real_action_children(empties.children):
                    self.check(action.numIndices == 0)
                    self.check(action.numInstances == 0)

                    self.controller.SetFrameEvent(action.eventId, False)

                    # Check that we have empty PostVS
                    postvs_data = self.get_postvs(action,
                                                  rd.MeshDataStage.VSOut, 0, 1)
                    self.check(len(postvs_data) == 0)

                    self.check_overlay([], no_overlay=True)

                # vkCmdDrawIndirectCountKHR
                action_indirect = indirect_count_root.children[1].children[0]

                self.check(rd.ResourceUsage.Indirect in buffer_usage[
                    action_indirect.eventId])

                self.check(action_indirect
                           and len(real_action_children(action_indirect)) == 1)

                # vkCmdDrawIndirectCountKHR[0]
                action = action_indirect.children[0]

                self.check(action.numIndices == 3)
                self.check(action.numInstances == 4)

                self.controller.SetFrameEvent(action.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 0,
                        'gl_PerVertex_var.gl_Position': [-0.8, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 1,
                        'gl_PerVertex_var.gl_Position': [-0.7, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 2,
                        'gl_PerVertex_var.gl_Position': [-0.6, 0.5, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay([(60, 190)])

                rdtest.log.success("{} {} is as expected".format(
                    level, action.customName))

                # vkCmdDrawIndexedIndirectCountKHR
                action_indirect = indirect_count_root.children[1].children[1]

                self.check(action_indirect
                           and len(real_action_children(action_indirect)) == 3)

                # vkCmdDrawIndirectCountKHR[0]
                action = action_indirect.children[0]
                self.check(action.numIndices == 3)
                self.check(action.numInstances == 1)

                self.controller.SetFrameEvent(action.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 15,
                        'gl_PerVertex_var.gl_Position': [-0.6, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 16,
                        'gl_PerVertex_var.gl_Position': [-0.5, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 17,
                        'gl_PerVertex_var.gl_Position': [-0.4, 0.5, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay([(100, 190)])

                rdtest.log.success("{} {} is as expected".format(
                    level, action.customName))

                # vkCmdDrawIndirectCountKHR[1]
                action = action_indirect.children[1]
                self.check(action.numIndices == 0)
                self.check(action.numInstances == 0)

                self.controller.SetFrameEvent(action.eventId, False)

                postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

                self.check(len(postvs_data) == 0)

                self.check_overlay([])

                rdtest.log.success("{} {} is as expected".format(
                    level, action.customName))

                # vkCmdDrawIndirectCountKHR[2]
                action = action_indirect.children[2]
                self.check(action.numIndices == 6)
                self.check(action.numInstances == 2)

                self.controller.SetFrameEvent(action.eventId, False)

                # Check that we have PostVS as expected
                postvs_data = self.get_postvs(action, rd.MeshDataStage.VSOut)

                # These indices are the *output* indices, which have been rebased/remapped, so are not the same as the input
                # indices
                postvs_ref = {
                    0: {
                        'vtx': 0,
                        'idx': 18,
                        'gl_PerVertex_var.gl_Position': [-0.4, 0.5, 0.0, 1.0]
                    },
                    1: {
                        'vtx': 1,
                        'idx': 19,
                        'gl_PerVertex_var.gl_Position': [-0.3, 0.2, 0.0, 1.0]
                    },
                    2: {
                        'vtx': 2,
                        'idx': 20,
                        'gl_PerVertex_var.gl_Position': [-0.2, 0.2, 0.0, 1.0]
                    },
                    3: {
                        'vtx': 3,
                        'idx': 21,
                        'gl_PerVertex_var.gl_Position': [-0.1, 0.5, 0.0, 1.0]
                    },
                    4: {
                        'vtx': 4,
                        'idx': 22,
                        'gl_PerVertex_var.gl_Position': [0.0, 0.2, 0.0, 1.0]
                    },
                    5: {
                        'vtx': 5,
                        'idx': 23,
                        'gl_PerVertex_var.gl_Position': [0.1, 0.2, 0.0, 1.0]
                    },
                }

                self.check_mesh_data(postvs_ref, postvs_data)
                self.check(len(postvs_data) == len(
                    postvs_ref))  # We shouldn't have any extra vertices

                self.check_overlay([(140, 190), (200, 190)])

                rdtest.log.success("{} {} is as expected".format(
                    level, action.customName))

                # Now check that the draws post-count are correctly highlighted
                self.controller.SetFrameEvent(
                    self.find_action(
                        "{}: Post-count 1".format(level)).children[0].eventId,
                    False)
                self.check_overlay([(340, 40)])
                self.controller.SetFrameEvent(
                    self.find_action(
                        "{}: Post-count 2".format(level)).children[0].eventId,
                    False)
                self.check_overlay([(340, 190)])
                self.controller.SetFrameEvent(
                    self.find_action(
                        "{}: Post-count 3".format(level)).children[0].eventId,
                    False)
                self.check_overlay([(340, 115)])
            else:
                rdtest.log.print("KHR_action_indirect_count not tested")
    def check_capture(self):
        # Make an output so we can pick pixels
        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture)

        self.check(out is not None)

        draw = self.find_draw("Draw")

        self.controller.SetFrameEvent(draw.eventId, False)

        pipe: rd.PipeState = self.controller.GetPipelineState()

        bind: rd.ShaderBindpointMapping = pipe.GetBindpointMapping(rd.ShaderStage.Fragment)
        texs: List[rd.BoundResourceArray] = pipe.GetReadOnlyResources(rd.ShaderStage.Fragment)

        if len(bind.readOnlyResources) != 2:
            raise rdtest.TestFailureException(
                "Expected 2 textures bound, not {}".format(len(bind.readOnlyResources)))

        if bind.readOnlyResources[0].bind != 2:
            raise rdtest.TestFailureException(
                "First texture should be on slot 2, not {}".format(bind.readOnlyResources[0].bind))

        id = texs[2].resources[0].resourceId

        tex_details = self.get_texture(id)
        res_details = self.get_resource(id)

        if res_details.name != "Red 2D":
            raise rdtest.TestFailureException("First texture should be Red 2D texture, not {}".format(res_details.name))

        if tex_details.dimension != 2:
            raise rdtest.TestFailureException(
                "First texture should be 2D texture, not {}".format(tex_details.dimension))

        if tex_details.width != 8 or tex_details.height != 8:
            raise rdtest.TestFailureException(
                "First texture should be 8x8, not {}x{}".format(tex_details.width, tex_details.height))

        data = self.controller.GetTextureData(id, 0, 0)
        first_pixel = struct.unpack_from("BBBB", data, 0)

        if not rdtest.value_compare(first_pixel, (255, 0, 0, 255)):
            raise rdtest.TestFailureException("Texture should contain red, not {}".format(first_pixel))

        rdtest.log.success("First texture is as expected")

        if bind.readOnlyResources[1].bind != 3:
            raise rdtest.TestFailureException(
                "First texture should be on slot 3, not {}".format(texs[0].bindPoint.bind))

        id = texs[3].resources[0].resourceId

        tex_details = self.get_texture(id)
        res_details = self.get_resource(id)

        if res_details.name != "Green 3D":
            raise rdtest.TestFailureException(
                "First texture should be Green 3D texture, not {}".format(res_details.name))

        if tex_details.dimension != 3:
            raise rdtest.TestFailureException(
                "First texture should be 3D texture, not {}".format(tex_details.dimension))

        if tex_details.width != 4 or tex_details.height != 4 or tex_details.depth != 4:
            raise rdtest.TestFailureException(
                "First texture should be 4x4x4, not {}x{}x{}".format(tex_details.width, tex_details.height,
                                                                     tex_details.depth))

        data = self.controller.GetTextureData(id, 0, 0)
        first_pixel = struct.unpack_from("BBBB", data, 0)

        if not rdtest.value_compare(first_pixel, (0, 255, 0, 255)):
            raise rdtest.TestFailureException("Texture should contain green, not {}".format(first_pixel))

        rdtest.log.success("Second texture is as expected")

        tex = rd.TextureDisplay()
        tex.resourceId = pipe.GetOutputTargets()[0].resourceId
        out.SetTextureDisplay(tex)

        tex_details = self.get_texture(tex.resourceId)

        picked: rd.PixelValue = out.PickPixel(tex.resourceId, False,
                                              int(tex_details.width / 2), int(tex_details.height / 2), 0, 0, 0)

        if not rdtest.value_compare(picked.floatValue, [1.0, 1.0, 0.0, 0.2]):
            raise rdtest.TestFailureException("Picked value {} doesn't match expectation".format(picked.floatValue))

        rdtest.log.success("Picked value is as expected")

        out.Shutdown()