Ejemplo n.º 1
0
    def check_pixel_value(self,
                          tex: rd.ResourceId,
                          x,
                          y,
                          value,
                          *,
                          sub=None,
                          cast=None,
                          eps=util.FLT_EPSILON):
        tex_details = self.get_texture(tex)
        res_details = self.get_resource(tex)

        if sub is None:
            sub = rd.Subresource(0, 0, 0)
        if cast is None:
            cast = rd.CompType.Typeless

        if tex_details is not None:
            if type(x) is float:
                x = int(((tex_details.width >> sub.mip) - 1) * x)
            if type(y) is float:
                y = int(((tex_details.height >> sub.mip) - 1) * y)

            if cast == rd.CompType.Typeless and tex_details.creationFlags & rd.TextureCategory.SwapBuffer:
                cast = rd.CompType.UNormSRGB

            # Reduce epsilon for RGBA8 textures if it's not already reduced
            if tex_details.format.compByteWidth == 1 and eps == util.FLT_EPSILON:
                eps = (1.0 / 255.0)

        picked: rd.PixelValue = self.controller.PickPixel(tex, x, y, sub, cast)

        picked_value = picked.floatValue
        if cast == rd.CompType.UInt:
            picked_value = picked.uintValue
        elif cast == rd.CompType.SInt:
            picked_value = picked.intValue

        if not util.value_compare(picked_value, value, eps):
            save_data = rd.TextureSave()
            save_data.resourceId = tex
            save_data.destType = rd.FileType.PNG
            save_data.slice.sliceIndex = sub.slice
            save_data.mip = sub.mip
            save_data.sample.sampleIndex = sub.sample

            img_path = util.get_tmp_path('output.png')

            self.controller.SaveTexture(save_data, img_path)

            raise TestFailureException(
                "Picked value {} at {},{} doesn't match expectation of {}".
                format(picked_value, x, y, value), img_path)

        name = "Texture"
        if res_details is not None:
            name = res_details.name

        log.success("Picked value at {},{} in {} is as expected".format(
            x, y, name))
def dumpImage(controller, eventId, outputDir, tracefile):
    draw = findDrawWithEventId(controller, eventId)
    if draw is None:
        raise RuntimeError("Couldn't find draw call with eventId " +
                           str(eventId))

    controller.SetFrameEvent(draw.eventId, True)

    texsave = rd.TextureSave()

    # Select the first color output
    texsave.resourceId = draw.outputs[0]

    if texsave.resourceId == rd.ResourceId.Null():
        return

    filepath = Path(outputDir)
    filepath.mkdir(parents=True, exist_ok=True)
    filepath = filepath / (tracefile + "-" + str(int(draw.eventId)) + ".png")

    print("Saving image at eventId %d: %s to %s" %
          (draw.eventId, draw.name, filepath))

    # Most formats can only display a single image per file, so we select the
    # first mip and first slice
    texsave.mip = 0
    texsave.slice.sliceIndex = 0

    # For formats with an alpha channel, preserve it
    texsave.alpha = rd.AlphaMapping.Preserve
    texsave.destType = rd.FileType.PNG
    controller.SaveTexture(texsave, str(filepath))
Ejemplo n.º 3
0
    def check_clearbeforedraw_depth(self, out, depthId):
        # Test ClearBeforeDraw with a depth target
        tex = rd.TextureDisplay()
        tex.overlay = rd.DebugOverlay.ClearBeforeDraw
        tex.resourceId = depthId

        out.SetTextureDisplay(tex)
        out.GetDebugOverlayTexID() # Called to refresh the overlay
        
        overlay = rd.DebugOverlay.ClearBeforeDraw
        test_name = str(overlay) + '.Depth'

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

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

        tolerance = 2

        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(test_name), overlay_path, ref_path)

        rdtest.log.success("Reference and output image are identical for {}".format(test_name))
Ejemplo n.º 4
0
    def check_capture(self):
        tex = rd.TextureDisplay()

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

            if action.flags & rd.ActionFlags.Drawcall:
                pipe = self.controller.GetPipelineState()
                tex = self.controller.GetPipelineState().GetOutputTargets()[0].resourceId

                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).height - y

                self.check_pixel_value(tex, x, y, [0.0, 1.0, 0.0, 1.0])

            action = action.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
        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")
    def saveTexture(self, ResourceId, saveFile):
        if not self.isFileOpened():
            print('open log file first.')
            return False

        if ResourceId is None:
            return False

        saveData = rd.TextureSave()
        saveData.resourceId = ResourceId
        # saveData.comp = rd.CompType.UNorm
        saveData.typeHint = rd.CompType.UNorm
        saveData.channelExtract = -1
        saveData.comp.blackPoint = 0.0
        saveData.comp.whitePoint = 1.0
        saveData.alpha = rd.AlphaMapping.Discard

        fileExt = saveFile.split('.')[-1]

        if fileExt == 'dds' or fileExt == 'DDS':
            saveData.destType = rd.FileType.DDS

        elif fileExt == 'png' or fileExt == 'PNG':
            # saveData.alpha = rd.AlphaMapping.Preserve
            saveData.destType = rd.FileType.PNG

        elif fileExt == 'jpg' or fileExt == 'JPG':
            saveData.jpegQuality = 100
            saveData.destType = rd.FileType.JPG

        elif fileExt == 'bmp' or fileExt == 'BMP':
            saveData.destType = rd.FileType.BMP

        elif fileExt == 'tga' or fileExt == 'TGA':
            saveData.destType = rd.FileType.TGA

        elif fileExt == 'hdr' or fileExt == 'HDR':
            saveData.destType = rd.FileType.HDR

        elif fileExt == 'exr' or fileExt == 'EXR':
            saveData.typeHint = rd.CompType.Depth
            saveData.destType = rd.FileType.EXR

        elif fileExt == 'raw' or fileExt == 'RAW':
            saveData.destType = rd.FileType.RAW

        else:
            print('Cannot handle %s file' % fileExt)
            return False

        self.controller.SaveTexture(saveData, saveFile)
        return True
Ejemplo n.º 6
0
def sampleCode(controller):
	# Find the biggest drawcall in the whole capture
	draw = None
	for d in controller.GetDrawcalls():
		draw = biggestDraw(draw, d)

	# Move to that draw
	controller.SetFrameEvent(draw.eventId, True)

	texsave = rd.TextureSave()

	# Select the first color output
	texsave.resourceId = draw.outputs[0]

	if texsave.resourceId == rd.ResourceId.Null():
		return
	
	filename = str(int(texsave.resourceId))

	print("Saving images of %s at %d: %s" % (filename, draw.eventId, draw.name))

	# Save different types of texture

	# Blend alpha to a checkerboard pattern for formats without alpha support
	texsave.alpha = rd.AlphaMapping.BlendToCheckerboard

	# Most formats can only display a single image per file, so we select the
	# first mip and first slice
	texsave.mip = 0
	texsave.slice.sliceIndex = 0

	texsave.destType = rd.FileType.JPG
	controller.SaveTexture(texsave, filename + ".jpg")

	texsave.destType = rd.FileType.HDR
	controller.SaveTexture(texsave, filename + ".hdr")

	# For formats with an alpha channel, preserve it
	texsave.alpha = rd.AlphaMapping.Preserve

	texsave.destType = rd.FileType.PNG
	controller.SaveTexture(texsave, filename + ".png")

	# DDS textures can save multiple mips and array slices, so instead
	# of the default behaviour of saving mip 0 and slice 0, we set -1
	# which saves *all* mips and slices
	texsave.mip = -1
	texsave.slice.sliceIndex = -1

	texsave.destType = rd.FileType.DDS
	controller.SaveTexture(texsave, filename + ".dds")
Ejemplo n.º 7
0
    def image_save(self, draw: rd.DrawcallDescription):
        pipe: rd.PipeState = self.controller.GetPipelineState()

        texsave = rd.TextureSave()

        for res in pipe.GetOutputTargets():
            texsave.resourceId = res.resourceId
            texsave.mip = res.firstMip
            self.save_texture(texsave)

        depth = pipe.GetDepthTarget()
        texsave.resourceId = depth.resourceId
        texsave.mip = depth.firstMip
        self.save_texture(texsave)

        rdtest.log.success('Successfully saved images at {}: {}'.format(draw.eventId, draw.name))
Ejemplo n.º 8
0
 def extractTexture(self, drawcallId, state):
     """Save the texture in a png file (A bit dirty)"""
     bindpoints = state.GetBindpointMapping(rd.ShaderStage.Fragment)
     if not bindpoints.samplers:
         print(f"Warning: No texture found for drawcall {drawcallId}")
         return
     texture_bind = bindpoints.samplers[-1].bind
     resources = state.GetReadOnlyResources(rd.ShaderStage.Fragment)
     rid = resources[texture_bind].resources[0].resourceId
 
     texsave = rd.TextureSave()
     texsave.resourceId = rid
     texsave.mip = 0
     texsave.slice.sliceIndex = 0
     texsave.alpha = rd.AlphaMapping.Preserve
     texsave.destType = rd.FileType.PNG
     controller.SaveTexture(texsave, "{}{:05d}-texture.png".format(FILEPREFIX, drawcallId))
Ejemplo n.º 9
0
    def check_final_backbuffer(self):
        img_path = util.get_tmp_path('backbuffer.png')
        ref_path = self.get_ref_path('backbuffer.png')

        last_draw: rd.DrawcallDescription = self.get_last_draw()

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

        save_data = rd.TextureSave()
        save_data.resourceId = last_draw.copyDestination
        save_data.destType = rd.FileType.PNG

        self.controller.SaveTexture(save_data, img_path)

        if not util.png_compare(img_path, ref_path):
            raise TestFailureException("Reference and output backbuffer image differ", ref_path, img_path)

        log.success("Backbuffer is identical to reference")
Ejemplo n.º 10
0
def dump_resource(controller, draw):
    controller.SetFrameEvent(draw.eventId, True)
    texsave = rd.TextureSave()

    if DUMP_RENDER_TARGET:
        # dump render targtes (aka outputs)
        for idx, output in enumerate(draw.outputs):
            texsave.resourceId = output
            if texsave.resourceId != rd.ResourceId.Null():
                filename = "draw_%04d_c%d" % (draw.eventId, idx)
                print("Saving images of %s at %d: %s" % (filename, draw.eventId, draw.name))
                texsave.alpha = rd.AlphaMapping.BlendToCheckerboard
                texsave.mip = 0
                texsave.slice.sliceIndex = 0

                # For formats with an alpha channel, preserve it
                texsave.alpha = rd.AlphaMapping.Preserve

                texsave.destType = rd.FileType.PNG
                controller.SaveTexture(texsave, filename + ".png")
Ejemplo n.º 11
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()
Ejemplo n.º 12
0
    def check_capture(self):
        action: rd.ActionDescription = self.find_action('glDraw')

        while action is not None:
            self.controller.SetFrameEvent(action.eventId, True)

            self.check_triangle(fore=[0.2, 0.75, 0.2, 1.0])

            pipe = self.controller.GetPipelineState()
            depth = pipe.GetDepthTarget()
            vp = pipe.GetViewport(0)

            id = pipe.GetOutputTargets()[0].resourceId

            mn, mx = self.controller.GetMinMax(id, rd.Subresource(), rd.CompType.Typeless)

            if not rdtest.value_compare(mn.floatValue, [0.2, 0.2, 0.2, 1.0], eps=1.0/255.0):
                raise rdtest.TestFailureException(
                    "Minimum color values {} are not as expected".format(mn.floatValue))

            if not rdtest.value_compare(mx.floatValue, [0.2, 0.75, 0.2, 1.0], eps=1.0/255.0):
                raise rdtest.TestFailureException(
                    "Maximum color values {} are not as expected".format(mx.floatValue))

            hist = self.controller.GetHistogram(id, rd.Subresource(), rd.CompType.Typeless, 0.199, 0.75,
                                                (False, True, False, False))

            if hist[0] == 0 or hist[-1] == 0 or any([x > 0 for x in hist[1:-1]]):
                raise rdtest.TestFailureException(
                    "Green histogram didn't return expected values, values should have landed in first or last bucket")

            rdtest.log.success('Color Renderbuffer at action {} is working as expected'.format(action.eventId))

            if depth.resourceId != rd.ResourceId():
                val = self.controller.PickPixel(depth.resourceId, int(0.5 * vp.width), int(0.5 * vp.height),
                                                rd.Subresource(), rd.CompType.Typeless)

                if not rdtest.value_compare(val.floatValue[0], 0.75):
                    raise rdtest.TestFailureException(
                        "Picked value {} in triangle for depth doesn't match expectation".format(val))

                mn, mx = self.controller.GetMinMax(depth.resourceId, rd.Subresource(), rd.CompType.Typeless)
                hist = self.controller.GetHistogram(depth.resourceId, rd.Subresource(),
                                                    rd.CompType.Typeless, 0.75, 0.9, (True, False, False, False))

                if not rdtest.value_compare(mn.floatValue[0], 0.75):
                    raise rdtest.TestFailureException(
                        "Minimum depth values {} are not as expected".format(mn.floatValue))

                if not rdtest.value_compare(mx.floatValue[0], 0.9):
                    raise rdtest.TestFailureException(
                        "Maximum depth values {} are not as expected".format(mx.floatValue))

                if hist[0] == 0 or hist[-1] == 0 or any([x > 0 for x in hist[1:-1]]):
                    raise rdtest.TestFailureException(
                        "Depth histogram didn't return expected values, values should have landed in first or last bucket")

                rdtest.log.success('Depth Renderbuffer at action {} is working as expected'.format(action.eventId))

            tex_details = self.get_texture(id)

            if tex_details.msSamp > 1:
                samples = []
                for i in range(tex_details.msSamp):
                    samples.append(self.controller.GetTextureData(id, rd.Subresource(0, 0, i)))

                for i in range(tex_details.msSamp):
                    for j in range(tex_details.msSamp):
                        if i == j:
                            continue

                        if samples[i] == samples[j]:
                            save_data = rd.TextureSave()
                            save_data.resourceId = id
                            save_data.destType = rd.FileType.PNG
                            save_data.slice.sliceIndex = 0
                            save_data.mip = 0

                            img_path0 = rdtest.get_tmp_path('sample{}.png'.format(i))
                            img_path1 = rdtest.get_tmp_path('sample{}.png'.format(j))

                            save_data.sample.sampleIndex = i
                            self.controller.SaveTexture(save_data, img_path0)
                            save_data.sample.sampleIndex = j
                            self.controller.SaveTexture(save_data, img_path1)

                            raise rdtest.TestFailureException("Two MSAA samples returned the same data", img_path0, img_path1)

            action: rd.ActionDescription = self.find_action('glDraw', action.eventId+1)

        rdtest.log.success('All renderbuffers checked and rendered correctly')
Ejemplo n.º 13
0
    def check_capture(self):
        draw: rd.DrawcallDescription = self.find_draw("Degenerate")
        self.controller.SetFrameEvent(draw.next.eventId, True)
        pipe: rd.VKState = self.controller.GetVulkanPipelineState()

        if pipe.multisample.rasterSamples != 4:
            raise rdtest.TestFailureException(
                "MSAA sample count is {}, not 1".format(
                    pipe.multisample.rasterSamples))

        sampleLoc: rd.VKSampleLocations = pipe.multisample.sampleLocations

        if sampleLoc.gridWidth != 1:
            raise rdtest.TestFailureException(
                "Sample locations grid width is {}, not 1".format(
                    sampleLoc.gridWidth))
        if sampleLoc.gridHeight != 1:
            raise rdtest.TestFailureException(
                "Sample locations grid height is {}, not 1".format(
                    sampleLoc.gridHeight))

        # [0] and [1] should be identical, as should [2] and [3], but they should be different from each other
        if not sampleLoc.customLocations[0] == sampleLoc.customLocations[1]:
            raise rdtest.TestFailureException(
                "In degenerate case, sample locations [0] and [1] don't match: {} vs {}"
                .format(sampleLoc.customLocations[0],
                        sampleLoc.customLocations[1]))

        if not sampleLoc.customLocations[2] == sampleLoc.customLocations[3]:
            raise rdtest.TestFailureException(
                "In degenerate case, sample locations [2] and [3] don't match: {} vs {}"
                .format(sampleLoc.customLocations[2],
                        sampleLoc.customLocations[3]))

        if sampleLoc.customLocations[1] == sampleLoc.customLocations[2]:
            raise rdtest.TestFailureException(
                "In degenerate case, sample locations [1] and [2] DO match: {} vs {}"
                .format(sampleLoc.customLocations[1],
                        sampleLoc.customLocations[2]))

        draw: rd.DrawcallDescription = self.find_draw("Rotated")
        self.controller.SetFrameEvent(draw.next.eventId, True)
        pipe: rd.VKState = self.controller.GetVulkanPipelineState()

        if pipe.multisample.rasterSamples != 4:
            raise rdtest.TestFailureException(
                "MSAA sample count is {}, not 1".format(
                    pipe.multisample.rasterSamples))

        sampleLoc: rd.VKSampleLocations = pipe.multisample.sampleLocations

        if sampleLoc.gridWidth != 1:
            raise rdtest.TestFailureException(
                "Sample locations grid width is {}, not 1".format(
                    sampleLoc.gridWidth))
        if sampleLoc.gridHeight != 1:
            raise rdtest.TestFailureException(
                "Sample locations grid height is {}, not 1".format(
                    sampleLoc.gridHeight))

        # All sample locations should be unique
        if sampleLoc.customLocations[0] == sampleLoc.customLocations[1]:
            raise rdtest.TestFailureException(
                "In rotated case, sample locations [0] and [1] DO match: {} vs {}"
                .format(sampleLoc.customLocations[0],
                        sampleLoc.customLocations[1]))

        if sampleLoc.customLocations[1] == sampleLoc.customLocations[2]:
            raise rdtest.TestFailureException(
                "In rotated case, sample locations [1] and [2] DO match: {} vs {}"
                .format(sampleLoc.customLocations[1],
                        sampleLoc.customLocations[2]))

        if sampleLoc.customLocations[2] == sampleLoc.customLocations[3]:
            raise rdtest.TestFailureException(
                "In rotated case, sample locations [2] and [3] DO match: {} vs {}"
                .format(sampleLoc.customLocations[2],
                        sampleLoc.customLocations[3]))

        rdtest.log.success("Pipeline state is correct")

        # Grab the multisampled image's ID here
        save_data = rd.TextureSave()
        curpass: rd.VKCurrentPass = pipe.currentPass
        save_data.resourceId = curpass.framebuffer.attachments[
            curpass.renderpass.colorAttachments[0]].imageResourceId
        save_data.destType = rd.FileType.PNG
        save_data.sample.mapToArray = False

        dim = (0, 0)
        texs = self.controller.GetTextures()
        for tex in texs:
            if tex.resourceId == save_data.resourceId:
                dim = (tex.width, tex.height)

        if dim == (0, 0):
            raise rdtest.TestFailureException(
                "Couldn't get dimensions of texture")

        last_draw: rd.DrawcallDescription = self.get_last_draw()

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

        # Due to the variability of rasterization between implementations or even drivers,
        # we don't want to check against a 'known good'.
        # So instead we verify that at the first degenerate draw each pair of two sample's images are identical and that
        # in the rotated grid case each sample's image is distinct.
        # In future we could also check that the degenerate case 'stretches' the triangle up, as with the way the
        # geometry is defined the second sample image should be a superset (i.e. strictly more samples covered).
        rotated_paths = []
        degenerate_paths = []

        for sample in range(0, 4):
            tmp_path = rdtest.get_tmp_path('sample{}.png'.format(sample))
            degenerate_path = rdtest.get_tmp_path(
                'degenerate{}.png'.format(sample))
            rotated_path = rdtest.get_tmp_path('rotated{}.png'.format(sample))

            rotated_paths.append(rotated_path)
            degenerate_paths.append(degenerate_path)

            save_data.sample.sampleIndex = sample
            self.controller.SaveTexture(save_data, tmp_path)

            try:
                img = Image.open(tmp_path)
            except Exception as ex:
                raise FileNotFoundError("Can't open {}".format(
                    rdtest.sanitise_filename(tmp_path)))

            img.crop((0, 0, dim[0] / 2, dim[1])).save(degenerate_path)
            img.crop((dim[0] / 2, 0, dim[0], dim[1])).save(rotated_path)

        # first two degenerate images should be identical, as should the last two, and they should be different.
        if not rdtest.image_compare(degenerate_paths[0], degenerate_paths[1],
                                    0):
            raise rdtest.TestFailureException(
                "Degenerate grid sample 0 and 1 are different",
                degenerate_paths[0], degenerate_paths[1])

        if not rdtest.image_compare(degenerate_paths[2], degenerate_paths[3],
                                    0):
            raise rdtest.TestFailureException(
                "Degenerate grid sample 2 and 3 are different",
                degenerate_paths[2], degenerate_paths[3])

        if rdtest.image_compare(degenerate_paths[1], degenerate_paths[2], 0):
            raise rdtest.TestFailureException(
                "Degenerate grid sample 1 and 2 are identical",
                degenerate_paths[1], degenerate_paths[2])

        rdtest.log.success("Degenerate grid sample images are as expected")

        # all rotated images should be different
        for A in range(0, 4):
            for B in range(A + 1, 4):
                if rdtest.image_compare(rotated_paths[A], rotated_paths[B], 0):
                    raise rdtest.TestFailureException(
                        "Rotated grid sample {} and {} are identical".format(
                            A, B), rotated_paths[A], rotated_paths[B])

        rdtest.log.success("Rotated grid sample images are as expected")
Ejemplo n.º 14
0
    def check_test(self, fmt_name: str, name: str, test_mode: int):
        pipe: rd.PipeState = self.controller.GetPipelineState()

        image_view = (test_mode != Texture_Zoo.TEST_CAPTURE)

        if image_view:
            bound_res: rd.BoundResource = pipe.GetOutputTargets()[0]
        else:
            bound_res: rd.BoundResource = pipe.GetReadOnlyResources(
                rd.ShaderStage.Pixel)[0].resources[0]

        texs = self.controller.GetTextures()
        for t in texs:
            self.textures[t.resourceId] = t

        tex_id: rd.ResourceId = bound_res.resourceId
        tex: rd.TextureDescription = self.textures[tex_id]

        comp_type: rd.CompType = tex.format.compType
        if bound_res.typeCast != rd.CompType.Typeless:
            comp_type = bound_res.typeCast

        # When not running proxied, save non-typecasted textures to disk
        if not image_view and not self.proxied and (
                tex.format.compType == comp_type
                or tex.format.type == rd.ResourceFormatType.D24S8
                or tex.format.type == rd.ResourceFormatType.D32S8):
            save_data = rd.TextureSave()
            save_data.resourceId = tex_id
            save_data.destType = rd.FileType.DDS
            save_data.sample.mapToArray = True

            self.textures[self.filename] = tex

            path = rdtest.get_tmp_path(self.filename + '.dds')

            success: bool = self.controller.SaveTexture(save_data, path)

            if not success:
                if self.d3d_mode:
                    raise rdtest.TestFailureException(
                        "Couldn't save DDS to {} on D3D.".format(
                            self.filename))

                try:
                    os.remove(path)
                except Exception:
                    pass

            save_data.destType = rd.FileType.PNG
            save_data.slice.sliceIndex = 0
            save_data.sample.sampleIndex = 0
            path = path.replace('.dds', '.png')

            if comp_type == rd.CompType.UInt:
                save_data.comp.blackPoint = 0.0
                save_data.comp.whitePoint = 255.0
            elif comp_type == rd.CompType.SInt:
                save_data.comp.blackPoint = -255.0
                save_data.comp.whitePoint = 0.0
            elif comp_type == rd.CompType.SNorm:
                save_data.comp.blackPoint = -1.0
                save_data.comp.whitePoint = 0.0

            success: bool = self.controller.SaveTexture(save_data, path)

            if not success:
                try:
                    os.remove(path)
                except Exception:
                    pass

        value0 = []

        comp_count = tex.format.compCount

        # When viewing PNGs only compare the components that the original texture had
        if test_mode == Texture_Zoo.TEST_PNG:
            comp_count = self.textures[self.filename]
            tex.msSamp = 0
            tex.arraysize = 1
            tex.depth = 1
            self.fake_msaa = 'MSAA' in name
        elif test_mode == Texture_Zoo.TEST_DDS:
            tex.arraysize = self.textures[self.filename].arraysize
            tex.msSamp = self.textures[self.filename].msSamp
            self.fake_msaa = 'MSAA' in name

        # HACK: We don't properly support BGRX, so just drop the alpha channel. We can't set this to compCount = 3
        # internally because that's a 24-bit format with no padding...
        if 'B8G8R8X8' in fmt_name:
            comp_count = 3

        # Completely ignore the alpha for BC1, our encoder doesn't pay attention to it
        if tex.format.type == rd.ResourceFormatType.BC1:
            comp_count = 3

        # Calculate format-appropriate epsilon
        eps_significand = 1.0
        # Account for the sRGB curve by more generous epsilon

        if comp_type == rd.CompType.UNormSRGB:
            eps_significand = 2.5
        # Similarly SNorm essentially loses a bit of accuracy due to us only using negative values
        elif comp_type == rd.CompType.SNorm:
            eps_significand = 2.0

        if tex.format.type == rd.ResourceFormatType.R4G4B4A4 or tex.format.type == rd.ResourceFormatType.R4G4:
            eps = (eps_significand / 15.0)
        elif rd.ResourceFormatType.BC1 <= tex.format.type <= rd.ResourceFormatType.BC3:
            eps = (eps_significand / 15.0)  # 4-bit precision in some channels
        elif tex.format.type == rd.ResourceFormatType.R5G5B5A1 or tex.format.type == rd.ResourceFormatType.R5G6B5:
            eps = (eps_significand / 31.0)
        elif tex.format.type == rd.ResourceFormatType.R11G11B10:
            eps = (eps_significand / 31.0)  # 5-bit mantissa in blue
        elif tex.format.type == rd.ResourceFormatType.R9G9B9E5:
            eps = (
                eps_significand / 63.0
            )  # we have 9 bits of data, but might lose 2-3 due to shared exponent
        elif tex.format.type == rd.ResourceFormatType.BC6 and tex.format.compType == rd.CompType.SNorm:
            eps = (eps_significand / 63.0
                   )  # Lose a bit worth of precision for the signed version
        elif rd.ResourceFormatType.BC4 <= tex.format.type <= rd.ResourceFormatType.BC7:
            eps = (eps_significand / 127.0)
        elif tex.format.compByteWidth == 1:
            eps = (eps_significand / 255.0)
        elif comp_type == rd.CompType.Depth and tex.format.compCount == 2:
            eps = (eps_significand / 255.0)  # stencil is only 8-bit
        elif tex.format.type == rd.ResourceFormatType.A8:
            eps = (eps_significand / 255.0)
        elif tex.format.type == rd.ResourceFormatType.R10G10B10A2:
            eps = (eps_significand / 1023.0)
        else:
            # half-floats have 11-bit mantissa. This epsilon is tight enough that we can be sure
            # any remaining errors are implementation inaccuracy and not our bug
            eps = (eps_significand / 2047.0)

        for mp in range(tex.mips):
            for sl in range(max(tex.arraysize, max(1, tex.depth >> mp))):
                z = 0
                if tex.depth > 1:
                    z = sl

                for sm in range(tex.msSamp):
                    cur_sub = self.sub(mp, sl, sm)

                    tex_display = rd.TextureDisplay()
                    tex_display.resourceId = tex_id
                    tex_display.subresource = cur_sub
                    tex_display.flipY = self.opengl_mode
                    tex_display.typeCast = comp_type
                    tex_display.alpha = False
                    tex_display.scale = 1.0 / float(1 << mp)
                    tex_display.backgroundColor = rd.FloatVector(
                        0.0, 0.0, 0.0, 1.0)

                    if comp_type == rd.CompType.UInt:
                        tex_display.rangeMin = 0.0
                        tex_display.rangeMax = 255.0
                    elif comp_type == rd.CompType.SInt:
                        tex_display.rangeMin = -255.0
                        tex_display.rangeMax = 0.0
                    elif comp_type == rd.CompType.SNorm:
                        tex_display.rangeMin = -1.0
                        tex_display.rangeMax = 0.0

                    self.out.SetTextureDisplay(tex_display)

                    self.out.Display()

                    pixels: bytes = self.out.ReadbackOutputTexture()
                    dim = self.out.GetDimensions()

                    stencilpixels = None

                    # Grab stencil separately
                    if tex.format.type == rd.ResourceFormatType.D16S8 or tex.format.type == rd.ResourceFormatType.D24S8 or tex.format.type == rd.ResourceFormatType.D32S8:
                        tex_display.red = False
                        tex_display.green = True
                        tex_display.blue = False
                        tex_display.alpha = False

                        self.out.SetTextureDisplay(tex_display)
                        self.out.Display()

                        stencilpixels: bytes = self.out.ReadbackOutputTexture()

                    # Grab alpha if needed (since the readback output is RGB only)
                    if comp_count == 4 or tex.format.type == rd.ResourceFormatType.A8:
                        tex_display.red = False
                        tex_display.green = False
                        tex_display.blue = False
                        tex_display.alpha = True

                        self.out.SetTextureDisplay(tex_display)
                        self.out.Display()

                        alphapixels: bytes = self.out.ReadbackOutputTexture()

                    all_good = True

                    for x in range(max(1, tex.width >> mp)):
                        if not all_good:
                            break
                        for y in range(max(1, tex.height >> mp)):
                            expected = self.get_expected_value(
                                comp_count, comp_type, cur_sub, test_mode, tex,
                                x, y, z)

                            # for this test to work the expected values have to be within the range we selected for
                            # display above
                            for i in expected:
                                if i < tex_display.rangeMin or tex_display.rangeMax < i:
                                    raise rdtest.TestFailureException(
                                        "expected value {} is outside of texture display range! {} - {}"
                                        .format(i, tex_display.rangeMin,
                                                tex_display.rangeMax))

                            # convert the expected values to range-adapted values
                            for i in range(len(expected)):
                                expected[i] = (expected[i] -
                                               tex_display.rangeMin) / (
                                                   tex_display.rangeMax -
                                                   tex_display.rangeMin)

                            # get the bytes from the displayed pixel
                            offs = y * dim[0] * 3 + x * 3
                            displayed = [
                                int(a) for a in pixels[offs:offs + comp_count]
                            ]
                            if comp_count == 4:
                                del displayed[3]
                                displayed.append(int(alphapixels[offs]))
                            if stencilpixels is not None:
                                del displayed[1:]
                                displayed.append(int(stencilpixels[offs]))
                            if tex.format.type == rd.ResourceFormatType.A8:
                                displayed = [int(alphapixels[offs])]

                            # normalise the displayed values
                            for i in range(len(displayed)):
                                displayed[i] = float(displayed[i]) / 255.0

                            # For SRGB textures match linear picked values. We do this for alpha too since it's rendered
                            # via RGB
                            if comp_type == rd.CompType.UNormSRGB:
                                displayed[0:4] = [
                                    srgb2linear(x) for x in displayed[0:4]
                                ]

                            # alpha channel in 10:10:10:2 has extremely low precision, and the ULP requirements mean
                            # we basically can't trust anything between 0 and 1 on float formats. Just round in that
                            # case as it still lets us differentiate between alpha 0.0-0.5 and 0.5-1.0
                            if tex.format.type == rd.ResourceFormatType.R10G10B10A2 and comp_type != rd.CompType.UInt:
                                displayed[3] = round(displayed[3]) * 1.0

                            # Handle 1-bit alpha
                            if tex.format.type == rd.ResourceFormatType.R5G5B5A1:
                                displayed[
                                    3] = 1.0 if displayed[3] >= 0.5 else 0.0

                            # Need an additional 1/255 epsilon to account for us going via a 8-bit backbuffer for display
                            if not rdtest.value_compare(
                                    displayed, expected, 1.0 / 255.0 + eps):
                                #rdtest.log.print(
                                #    "Quick-checking ({},{}) of slice {}, mip {}, sample {} of {} {} got {}. Expected {}.".format(
                                #        x, y, sl, mp, sm, name, fmt_name, displayed, expected) +
                                #    "Falling back to pixel picking tests.")
                                # Currently this seems to fail in some proxy scenarios with sRGB, but since it's not a
                                # real error we just silently swallow it
                                all_good = False
                                break

                    if all_good:
                        continue

                    for x in range(max(1, tex.width >> mp)):
                        for y in range(max(1, tex.height >> mp)):
                            expected = self.get_expected_value(
                                comp_count, comp_type, cur_sub, test_mode, tex,
                                x, y, z)
                            picked = self.get_picked_pixel_value(
                                comp_count, comp_type, cur_sub, tex, tex_id, x,
                                y)

                            if mp == 0 and sl == 0 and sm == 0 and x == 0 and y == 0:
                                value0 = picked

                            if not rdtest.value_compare(picked, expected, eps):
                                raise rdtest.TestFailureException(
                                    "At ({},{}) of slice {}, mip {}, sample {} of {} {} got {}. Expected {}"
                                    .format(x, y, sl, mp, sm, name, fmt_name,
                                            picked, expected))

        if not image_view:
            output_tex = pipe.GetOutputTargets()[0].resourceId

            # in the test captures pick the output texture, it should be identical to the
            # (0,0) pixel in slice 0, mip 0, sample 0
            view: rd.Viewport = pipe.GetViewport(0)

            val: rd.PixelValue = self.pick(
                pipe.GetOutputTargets()[0].resourceId,
                int(view.x + view.width / 2), int(view.y + view.height / 2),
                rd.Subresource(), rd.CompType.Typeless)

            picked = list(val.floatValue)

            # A8 picked values come out in alpha, but we want to compare against the single channel
            if tex.format.type == rd.ResourceFormatType.A8:
                picked[0] = picked[3]

            # Clamp to number of components in the texture
            picked = picked[0:comp_count]

            # If we didn't get a value0 (because we did all texture render compares) then fetch it here
            if len(value0) == 0:
                value0 = self.get_picked_pixel_value(comp_count, comp_type,
                                                     rd.Subresource(), tex,
                                                     tex_id, 0, 0)

            # Up-convert any non-float expected values to floats
            value0 = [float(x) for x in value0]

            # For depth/stencil images, one of either depth or stencil should match
            if comp_type == rd.CompType.Depth and len(value0) == 2:
                if picked[0] == 0.0:
                    value0[0] = 0.0
                    # normalise stencil value if it isn't already
                    if picked[1] > 1.0:
                        picked[1] /= 255.0
                elif picked[0] > 1.0:
                    # un-normalised stencil being rendered in red, match against our stencil expectation
                    picked[0] /= 255.0
                    value0[0] = value0[1]
                    value0[1] = 0.0
                else:
                    if picked[1] == 0.0:
                        value0[1] = 0.0
                    if picked[1] > 1.0:
                        picked[1] /= 255.0

            if not rdtest.value_compare(picked, value0, eps):
                raise rdtest.TestFailureException(
                    "In {} {} Top-left pixel as rendered is {}. Expected {}".
                    format(name, fmt_name, picked, value0))
Ejemplo n.º 15
0
    def check_test(self, fmt_name: str, name: str, test_mode: int):
        pipe: rd.PipeState = self.controller.GetPipelineState()

        image_view = (test_mode != Texture_Zoo.TEST_CAPTURE)

        if image_view:
            bound_res: rd.BoundResource = pipe.GetOutputTargets()[0]
        else:
            bound_res: rd.BoundResource = pipe.GetReadOnlyResources(
                rd.ShaderStage.Pixel)[0].resources[0]

        texs = self.controller.GetTextures()
        for t in texs:
            self.textures[t.resourceId] = t

        tex_id: rd.ResourceId = bound_res.resourceId
        tex: rd.TextureDescription = self.textures[tex_id]

        comp_type: rd.CompType = tex.format.compType
        if bound_res.typeCast != rd.CompType.Typeless:
            comp_type = bound_res.typeCast

        # When not running proxied, save non-typecasted textures to disk
        if not image_view and not self.proxied and (
                tex.format.compType == comp_type
                or tex.format.type == rd.ResourceFormatType.D24S8
                or tex.format.type == rd.ResourceFormatType.D32S8):
            save_data = rd.TextureSave()
            save_data.resourceId = tex_id
            save_data.destType = rd.FileType.DDS
            save_data.sample.mapToArray = True

            self.textures[self.filename] = tex

            path = rdtest.get_tmp_path(self.filename + '.dds')

            success: bool = self.controller.SaveTexture(save_data, path)

            if not success:
                try:
                    os.remove(path)
                except Exception:
                    pass

            save_data.destType = rd.FileType.PNG
            save_data.slice.sliceIndex = 0
            save_data.sample.sampleIndex = 0
            path = path.replace('.dds', '.png')

            if comp_type == rd.CompType.UInt:
                save_data.comp.blackPoint = 0.0
                save_data.comp.whitePoint = 255.0
            elif comp_type == rd.CompType.SInt:
                save_data.comp.blackPoint = -255.0
                save_data.comp.whitePoint = 0.0
            elif comp_type == rd.CompType.SNorm:
                save_data.comp.blackPoint = -1.0
                save_data.comp.whitePoint = 0.0

            success: bool = self.controller.SaveTexture(save_data, path)

            if not success:
                try:
                    os.remove(path)
                except Exception:
                    pass

        value0 = []

        comp_count = tex.format.compCount

        # When viewing PNGs only compare the components that the original texture had
        if test_mode == Texture_Zoo.TEST_PNG:
            comp_count = self.textures[self.filename]
            tex.msSamp = 0
            tex.arraysize = 1
            tex.depth = 1
            self.fake_msaa = 'MSAA' in name
        elif test_mode == Texture_Zoo.TEST_DDS:
            tex.arraysize = self.textures[self.filename].arraysize
            tex.msSamp = self.textures[self.filename].msSamp
            self.fake_msaa = 'MSAA' in name

        # HACK: We don't properly support BGRX, so just drop the alpha channel. We can't set this to compCount = 3
        # internally because that's a 24-bit format with no padding...
        if 'B8G8R8X8' in fmt_name:
            comp_count = 3

        # Completely ignore the alpha for BC1, our encoder doesn't pay attention to it
        if tex.format.type == rd.ResourceFormatType.BC1:
            comp_count = 3

        # Calculate format-appropriate epsilon
        eps_significand = 1.0
        # Account for the sRGB curve by more generous epsilon

        if comp_type == rd.CompType.UNormSRGB:
            eps_significand = 2.5
        # Similarly SNorm essentially loses a bit of accuracy due to us only using negative values
        elif comp_type == rd.CompType.SNorm:
            eps_significand = 2.0

        if tex.format.type == rd.ResourceFormatType.R4G4B4A4 or tex.format.type == rd.ResourceFormatType.R4G4:
            eps = (eps_significand / 15.0)
        elif rd.ResourceFormatType.BC1 <= tex.format.type <= rd.ResourceFormatType.BC3:
            eps = (eps_significand / 15.0)  # 4-bit precision in some channels
        elif tex.format.type == rd.ResourceFormatType.R5G5B5A1 or tex.format.type == rd.ResourceFormatType.R5G6B5:
            eps = (eps_significand / 31.0)
        elif tex.format.type == rd.ResourceFormatType.R11G11B10:
            eps = (eps_significand / 31.0)  # 5-bit mantissa in blue
        elif tex.format.type == rd.ResourceFormatType.R9G9B9E5:
            eps = (
                eps_significand / 63.0
            )  # we have 9 bits of data, but might lose 2-3 due to shared exponent
        elif tex.format.type == rd.ResourceFormatType.BC6 and tex.format.compType == rd.CompType.SNorm:
            eps = (eps_significand / 63.0
                   )  # Lose a bit worth of precision for the signed version
        elif rd.ResourceFormatType.BC4 <= tex.format.type <= rd.ResourceFormatType.BC7:
            eps = (eps_significand / 127.0)
        elif tex.format.compByteWidth == 1:
            eps = (eps_significand / 255.0)
        elif comp_type == rd.CompType.Depth and tex.format.compCount == 2:
            eps = (eps_significand / 255.0)  # stencil is only 8-bit
        elif tex.format.type == rd.ResourceFormatType.A8:
            eps = (eps_significand / 255.0)
        elif tex.format.type == rd.ResourceFormatType.R10G10B10A2:
            eps = (eps_significand / 1023.0)
        else:
            # half-floats have 11-bit mantissa. This epsilon is tight enough that we can be sure
            # any remaining errors are implementation inaccuracy and not our bug
            eps = (eps_significand / 2047.0)

        for mp in range(tex.mips):
            for sl in range(max(tex.arraysize, max(1, tex.depth >> mp))):
                z = 0
                if tex.depth > 1:
                    z = sl

                for sm in range(tex.msSamp):
                    for x in range(max(1, tex.width >> mp)):
                        for y in range(max(1, tex.height >> mp)):
                            picked: rd.PixelValue = self.pick(
                                tex_id, x, y, self.sub(mp, sl, sm), comp_type)

                            # each 3D slice cycles the x. This only affects the primary diagonal
                            offs_x = (x + z) % max(1, tex.width >> mp)

                            # The diagonal inverts the colors
                            inverted = (offs_x != y)

                            # second slice adds a coarse checkerboard pattern of inversion
                            if tex.arraysize > 1 and sl == 1 and (
                                (int(x / 2) % 2) != (int(y / 2) % 2)):
                                inverted = not inverted

                            if comp_type == rd.CompType.UInt or comp_type == rd.CompType.SInt:
                                expected = [10, 40, 70, 100]

                                if inverted:
                                    expected = list(reversed(expected))

                                expected = [
                                    c + 10 * (sm + mp) for c in expected
                                ]

                                if comp_type == rd.CompType.SInt:
                                    picked = picked.intValue
                                else:
                                    picked = picked.uintValue
                            elif (tex.format.type
                                  == rd.ResourceFormatType.D16S8
                                  or tex.format.type
                                  == rd.ResourceFormatType.D24S8
                                  or tex.format.type
                                  == rd.ResourceFormatType.D32S8):
                                # depth/stencil is a bit special
                                expected = [0.1, 10, 100, 0.85]

                                if inverted:
                                    expected = list(reversed(expected))

                                expected[0] += 0.075 * (sm + mp)
                                expected[1] += 10 * (sm + mp)

                                # Normalise stencil value
                                expected[1] = expected[1] / 255.0

                                picked = picked.floatValue
                            else:
                                expected = [0.1, 0.35, 0.6, 0.85]

                                if inverted:
                                    expected = list(reversed(expected))

                                expected = [
                                    c + 0.075 * (sm + mp) for c in expected
                                ]

                                picked = picked.floatValue

                            # SNorm/SInt is negative
                            if comp_type == rd.CompType.SNorm or comp_type == rd.CompType.SInt:
                                expected = [-c for c in expected]

                            # BGRA textures have a swizzle applied
                            if tex.format.BGRAOrder():
                                expected[0:3] = reversed(expected[0:3])

                            # alpha channel in 10:10:10:2 has extremely low precision, and the ULP requirements mean
                            # we basically can't trust anything between 0 and 1 on float formats. Just round in that
                            # case as it still lets us differentiate between alpha 0.0-0.5 and 0.5-1.0
                            if tex.format.type == rd.ResourceFormatType.R10G10B10A2:
                                if comp_type == rd.CompType.UInt:
                                    expected[3] = min(3, expected[3])
                                else:
                                    expected[3] = round(expected[3]) * 1.0
                                    picked[3] = round(picked[3]) * 1.0

                            # Handle 1-bit alpha
                            if tex.format.type == rd.ResourceFormatType.R5G5B5A1:
                                expected[
                                    3] = 1.0 if expected[3] >= 0.5 else 0.0
                                picked[3] = 1.0 if picked[3] >= 0.5 else 0.0

                            # A8 picked values come out in alpha, but we want to compare against the single channel
                            if tex.format.type == rd.ResourceFormatType.A8:
                                picked[0] = picked[3]

                            # Clamp to number of components in the texture
                            expected = expected[0:comp_count]
                            picked = picked[0:comp_count]

                            if mp == 0 and sl == 0 and sm == 0 and x == 0 and y == 0:
                                value0 = picked

                            # For SRGB textures picked values will come out as linear
                            def srgb2linear(f):
                                if f <= 0.04045:
                                    return f / 12.92
                                else:
                                    return ((f + 0.055) / 1.055)**2.4

                            if comp_type == rd.CompType.UNormSRGB:
                                expected[0:3] = [
                                    srgb2linear(x) for x in expected[0:3]
                                ]

                            if test_mode == Texture_Zoo.TEST_PNG:
                                orig_comp = self.textures[
                                    self.filename].format.compType
                                if orig_comp == rd.CompType.SNorm or orig_comp == rd.CompType.SInt:
                                    expected = [1.0 - x for x in expected]

                            if not rdtest.value_compare(picked, expected, eps):
                                raise rdtest.TestFailureException(
                                    "At ({},{}) of slice {}, mip {}, sample {} of {} {} got {}. Expected {}"
                                    .format(x, y, sl, mp, sm, name, fmt_name,
                                            picked, expected))

        if not image_view:
            output_tex = pipe.GetOutputTargets()[0].resourceId

            # in the test captures pick the output texture, it should be identical to the
            # (0,0) pixel in slice 0, mip 0, sample 0
            view: rd.Viewport = pipe.GetViewport(0)

            val: rd.PixelValue = self.pick(
                pipe.GetOutputTargets()[0].resourceId,
                int(view.x + view.width / 2), int(view.y + view.height / 2),
                rd.Subresource(), rd.CompType.Typeless)

            picked = val.floatValue

            # A8 picked values come out in alpha, but we want to compare against the single channel
            if tex.format.type == rd.ResourceFormatType.A8:
                picked[0] = picked[3]

            # Clamp to number of components in the texture
            picked = picked[0:comp_count]

            # Up-convert any non-float expected values to floats
            value0 = [float(x) for x in value0]

            # For depth/stencil images, one of either depth or stencil should match
            if comp_type == rd.CompType.Depth and len(value0) == 2:
                if picked[0] == 0.0:
                    value0[0] = 0.0
                    # normalise stencil value if it isn't already
                    if picked[1] > 1.0:
                        picked[1] /= 255.0
                elif picked[0] > 1.0:
                    # un-normalised stencil being rendered in red, match against our stencil expectation
                    picked[0] /= 255.0
                    value0[0] = value0[1]
                    value0[1] = 0.0
                else:
                    value0[1] = 0.0

            if not rdtest.value_compare(picked, value0, eps):
                raise rdtest.TestFailureException(
                    "In {} {} Top-left pixel as rendered is {}. Expected {}".
                    format(name, fmt_name, picked, value0))
Ejemplo n.º 16
0
    def run(self):
        controller = self.controller
        drawcalls = controller.GetDrawcalls()
        relevant_drawcalls, capture_type = self.extractRelevantCalls(drawcalls)
        print(f"Scrapping capture from {capture_type}...")

        if MAX_BLOCKS <= 0:
            max_drawcall = len(relevant_drawcalls)
        else:
            max_drawcall = min(MAX_BLOCKS, len(relevant_drawcalls))

        for drawcallId, draw in enumerate(relevant_drawcalls[:max_drawcall]):
            print("Draw call: " + draw.name)
            
            controller.SetFrameEvent(draw.eventId, True)
            state = controller.GetPipelineState()

            ib = state.GetIBuffer()
            vbs = state.GetVBuffers()
            attrs = state.GetVertexInputs()
            meshes = [makeMeshData(attr, ib, vbs, draw) for attr in attrs]

            try:
                # Position
                m = meshes[0]
                m.fetchTriangle(controller)
                indices = m.fetchIndices(controller)
                with open("{}{:05d}-indices.bin".format(FILEPREFIX, drawcallId), 'wb') as file:
                    pickle.dump(indices, file)
                unpacked = m.fetchData(controller)
                with open("{}{:05d}-positions.bin".format(FILEPREFIX, drawcallId), 'wb') as file:
                    pickle.dump(unpacked, file)

                # UV
                m = meshes[2 if capture_type == "Google Earth" else 1]
                m.fetchTriangle(controller)
                unpacked = m.fetchData(controller)
                with open("{}{:05d}-uv.bin".format(FILEPREFIX, drawcallId), 'wb') as file:
                    pickle.dump(unpacked, file)
            except RuntimeError as err:
                print("(Skipping: {})".format(err))
                continue

            # Vertex Shader Constants
            shader = state.GetShader(rd.ShaderStage.Vertex)
            ep = state.GetShaderEntryPoint(rd.ShaderStage.Vertex)
            ref = state.GetShaderReflection(rd.ShaderStage.Vertex)
            constants = self.getVertexShaderConstants(draw, state=state)
            constants["DrawCall"] = {
                "topology": 'TRIANGLE_STRIP' if draw.topology == rd.Topology.TriangleStrip else 'TRIANGLES',
                "type": capture_type
            }
            with open("{}{:05d}-constants.bin".format(FILEPREFIX, drawcallId), 'wb') as file:
                pickle.dump(constants, file)

            # Texture
            # dirty
            bindpoints = state.GetBindpointMapping(rd.ShaderStage.Fragment)
            texture_bind = bindpoints.samplers[-1].bind
            resources = state.GetReadOnlyResources(rd.ShaderStage.Fragment)
            rid = resources[texture_bind].resources[0].resourceId
            
            texsave = rd.TextureSave()
            texsave.resourceId = rid
            texsave.mip = 0
            texsave.slice.sliceIndex = 0
            texsave.alpha = rd.AlphaMapping.Preserve
            texsave.destType = rd.FileType.PNG
            controller.SaveTexture(texsave, "{}{:05d}-texture.png".format(FILEPREFIX, drawcallId))
Ejemplo n.º 17
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")
Ejemplo n.º 18
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()
Ejemplo n.º 19
0
    def check_capture(self):
        action: rd.ActionDescription = self.find_action("Degenerate")
        self.controller.SetFrameEvent(action.next.eventId, True)
        pipe: rd.VKState = self.controller.GetVulkanPipelineState()

        if pipe.multisample.rasterSamples != 4:
            raise rdtest.TestFailureException("MSAA sample count is {}, not 1".format(pipe.multisample.rasterSamples))

        sampleLoc: rd.VKSampleLocations = pipe.multisample.sampleLocations

        if sampleLoc.gridWidth != 1:
            raise rdtest.TestFailureException("Sample locations grid width is {}, not 1".format(sampleLoc.gridWidth))
        if sampleLoc.gridHeight != 1:
            raise rdtest.TestFailureException("Sample locations grid height is {}, not 1".format(sampleLoc.gridHeight))

        # [0] and [1] should be identical, as should [2] and [3], but they should be different from each other
        if not sampleLoc.customLocations[0] == sampleLoc.customLocations[1]:
            raise rdtest.TestFailureException("In degenerate case, sample locations [0] and [1] don't match: {} vs {}"
                                              .format(sampleLoc.customLocations[0], sampleLoc.customLocations[1]))

        if not sampleLoc.customLocations[2] == sampleLoc.customLocations[3]:
            raise rdtest.TestFailureException("In degenerate case, sample locations [2] and [3] don't match: {} vs {}"
                                              .format(sampleLoc.customLocations[2], sampleLoc.customLocations[3]))

        if sampleLoc.customLocations[1] == sampleLoc.customLocations[2]:
            raise rdtest.TestFailureException("In degenerate case, sample locations [1] and [2] DO match: {} vs {}"
                                              .format(sampleLoc.customLocations[1], sampleLoc.customLocations[2]))

        action: rd.ActionDescription = self.find_action("Rotated")
        self.controller.SetFrameEvent(action.next.eventId, True)
        pipe: rd.VKState = self.controller.GetVulkanPipelineState()

        if pipe.multisample.rasterSamples != 4:
            raise rdtest.TestFailureException("MSAA sample count is {}, not 1".format(pipe.multisample.rasterSamples))

        sampleLoc: rd.VKSampleLocations = pipe.multisample.sampleLocations

        if sampleLoc.gridWidth != 1:
            raise rdtest.TestFailureException("Sample locations grid width is {}, not 1".format(sampleLoc.gridWidth))
        if sampleLoc.gridHeight != 1:
            raise rdtest.TestFailureException("Sample locations grid height is {}, not 1".format(sampleLoc.gridHeight))

        # All sample locations should be unique
        if sampleLoc.customLocations[0] == sampleLoc.customLocations[1]:
            raise rdtest.TestFailureException("In rotated case, sample locations [0] and [1] DO match: {} vs {}"
                                              .format(sampleLoc.customLocations[0], sampleLoc.customLocations[1]))

        if sampleLoc.customLocations[1] == sampleLoc.customLocations[2]:
            raise rdtest.TestFailureException("In rotated case, sample locations [1] and [2] DO match: {} vs {}"
                                              .format(sampleLoc.customLocations[1], sampleLoc.customLocations[2]))

        if sampleLoc.customLocations[2] == sampleLoc.customLocations[3]:
            raise rdtest.TestFailureException("In rotated case, sample locations [2] and [3] DO match: {} vs {}"
                                              .format(sampleLoc.customLocations[2], sampleLoc.customLocations[3]))

        rdtest.log.success("Pipeline state is correct")

        # Grab the multisampled image's ID here
        save_data = rd.TextureSave()
        curpass: rd.VKCurrentPass = pipe.currentPass
        save_data.resourceId = curpass.framebuffer.attachments[curpass.renderpass.colorAttachments[0]].imageResourceId
        save_data.destType = rd.FileType.PNG
        save_data.sample.mapToArray = False

        dim = (0, 0)
        fmt: rd.ResourceFormat = None
        texs = self.controller.GetTextures()
        for tex in texs:
            tex: rd.TextureDescription
            if tex.resourceId == save_data.resourceId:
                dim = (tex.width, tex.height)
                fmt = tex.format

        if dim == (0,0):
            raise rdtest.TestFailureException("Couldn't get dimensions of texture")

        halfdim = (dim[0] >> 1, dim[1])

        if (fmt.type != rd.ResourceFormatType.Regular or fmt.compByteWidth != 1 or fmt.compCount != 4):
            raise rdtest.TestFailureException("Texture is not RGBA8 as expected: {}".format(fmt.Name()))

        stride = fmt.compByteWidth * fmt.compCount * dim[0]

        last_action: rd.ActionDescription = self.get_last_action()

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

        # Due to the variability of rasterization between implementations or even drivers,
        # we don't want to check against a 'known good'.
        # So instead we verify that at the first degenerate action each pair of two sample's images are identical and that
        # in the rotated grid case each sample's image is distinct.
        # In future we could also check that the degenerate case 'stretches' the triangle up, as with the way the
        # geometry is defined the second sample image should be a superset (i.e. strictly more samples covered).
        rotated_paths = []
        degenerate_paths = []

        for sample in range(0, 4):
            tmp_path = rdtest.get_tmp_path('sample{}.png'.format(sample))
            degenerate_path = rdtest.get_tmp_path('degenerate{}.png'.format(sample))
            rotated_path = rdtest.get_tmp_path('rotated{}.png'.format(sample))

            rotated_paths.append(rotated_path)
            degenerate_paths.append(degenerate_path)

            save_data.sample.sampleIndex = sample
            self.controller.SaveTexture(save_data, tmp_path)

            combined_data = rdtest.png_load_data(tmp_path)

            # crop left for degenerate, and crop right for rotated
            degenerate = []
            rotated = []
            for row in range(0, dim[1]):
                srcstart = row * stride

                len = halfdim[0] * fmt.compCount

                degenerate.append(combined_data[row][0:len])
                rotated.append(combined_data[row][len:])

            rdtest.png_save(degenerate_path, degenerate, halfdim, True)
            rdtest.png_save(rotated_path, rotated, halfdim, True)

        # first two degenerate images should be identical, as should the last two, and they should be different.
        if not rdtest.png_compare(degenerate_paths[0], degenerate_paths[1], 0):
            raise rdtest.TestFailureException("Degenerate grid sample 0 and 1 are different",
                                              degenerate_paths[0], degenerate_paths[1])

        if not rdtest.png_compare(degenerate_paths[2], degenerate_paths[3], 0):
            raise rdtest.TestFailureException("Degenerate grid sample 2 and 3 are different",
                                              degenerate_paths[2], degenerate_paths[3])

        if rdtest.png_compare(degenerate_paths[1], degenerate_paths[2], 0):
            raise rdtest.TestFailureException("Degenerate grid sample 1 and 2 are identical",
                                              degenerate_paths[1], degenerate_paths[2])

        rdtest.log.success("Degenerate grid sample images are as expected")

        # all rotated images should be different
        for A in range(0, 4):
            for B in range(A+1, 4):
                if rdtest.png_compare(rotated_paths[A], rotated_paths[B], 0):
                    raise rdtest.TestFailureException("Rotated grid sample {} and {} are identical".format(A, B),
                                                      rotated_paths[A], rotated_paths[B])

        rdtest.log.success("Rotated grid sample images are as expected")
Ejemplo n.º 20
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()
Ejemplo n.º 21
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")
Ejemplo n.º 22
0
def main(controller):
    drawcalls = controller.GetDrawcalls()
    relevant_drawcalls = list_relevant_calls(drawcalls)

    for drawcallId, draw in enumerate(relevant_drawcalls):
        print("Draw call: " + draw.name)
        if not draw.name.startswith("glDrawElements"):
            print("(Skipping)")
            continue

        controller.SetFrameEvent(draw.eventId, True)
        state = controller.GetPipelineState()

        ib = state.GetIBuffer()
        vbs = state.GetVBuffers()
        attrs = state.GetVertexInputs()
        meshes = [makeMeshData(attr, ib, vbs, draw) for attr in attrs]

        try:
            # Position
            m = meshes[0]
            m.fetchTriangle(controller)
            indices = m.fetchIndices(controller)
            with open("{}{:05d}-indices.bin".format(FILEPREFIX, drawcallId),
                      'wb') as file:
                pickle.dump(indices, file)
            unpacked = m.fetchData(controller)
            with open("{}{:05d}-positions.bin".format(FILEPREFIX, drawcallId),
                      'wb') as file:
                pickle.dump(unpacked, file)

            # UV
            m = meshes[1]
            m.fetchTriangle(controller)
            unpacked = m.fetchData(controller)
            with open("{}{:05d}-uv.bin".format(FILEPREFIX, drawcallId),
                      'wb') as file:
                pickle.dump(unpacked, file)
        except RuntimeError as err:
            print("(Skipping: {})".format(err))
            continue

        # Vertex Shader Constants
        shader = state.GetShader(rd.ShaderStage.Vertex)
        ep = state.GetShaderEntryPoint(rd.ShaderStage.Vertex)
        ref = state.GetShaderReflection(rd.ShaderStage.Vertex)
        constants = {}
        for cb in ref.constantBlocks:
            block = {}
            variables = controller.GetCBufferVariableContents(
                shader, ep, cb.bindPoint, rd.ResourceId.Null(), 0)
            for var in variables:
                val = 0
                if var.members:
                    val = []
                    for member in var.members:
                        memval = 0
                        if member.type == rd.VarType.Float:
                            memval = member.value.fv[:member.rows *
                                                     member.columns]
                        elif member.type == rd.VarType.Int:
                            memval = member.value.iv[:member.rows *
                                                     member.columns]
                        # ...
                        val.append(memval)
                else:
                    if var.type == rd.VarType.Float:
                        val = var.value.fv[:var.rows * var.columns]
                    elif var.type == rd.VarType.Int:
                        val = var.value.iv[:var.rows * var.columns]
                    # ...
                block[var.name] = val
            constants[cb.name] = block
        with open("{}{:05d}-constants.bin".format(FILEPREFIX, drawcallId),
                  'wb') as file:
            pickle.dump(constants, file)

        # Texture
        # dirty
        resources = state.GetReadOnlyResources(rd.ShaderStage.Fragment)
        rid = resources[0].resources[0].resourceId

        texsave = rd.TextureSave()
        texsave.resourceId = rid
        texsave.mip = 0
        texsave.slice.sliceIndex = 0
        texsave.alpha = rd.AlphaMapping.Preserve
        texsave.destType = rd.FileType.PNG
        controller.SaveTexture(
            texsave, "{}{:05d}-texture.png".format(FILEPREFIX, drawcallId))
Ejemplo n.º 23
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(rd.MeshDataStage.VSOut, 0,
                                      draw.numIndices)

        postvs_ref = {
            0: {
                'vtx': 0,
                'idx': 0,
                'gl_Position': [-0.5, -0.5, 0.0, 1.0],
                'v2fcol': [1.0, 0.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, 0.0, 1.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")
Ejemplo n.º 24
0
    def check_capture(self):
        out: rd.ReplayOutput = self.controller.CreateOutput(rd.CreateHeadlessWindowingData(100, 100), rd.ReplayOutputType.Texture)

        self.check(out is not None)

        api: rd.GraphicsAPI = self.controller.GetAPIProperties().pipelineType

        # Check the actual output is as expected first.

        for is_msaa in [False, True]:
            if is_msaa:
                test_marker: rd.DrawcallDescription = self.find_draw("MSAA Test")
            else:
                test_marker: rd.DrawcallDescription = self.find_draw("Normal 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
            tex.subresource.sample = 0

            # 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("MSAA" if is_msaa else "normal", 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
                    # Also to be safe we don't run this test on MSAA
                    if not is_msaa:
                        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
                        picked: rd.PixelValue = self.controller.PickPixel(overlay_id, x, 150, rd.Subresource(), rd.CompType.Typeless)

                        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.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], eps=eps)
                    self.check_pixel_value(overlay_id, 350, 50, [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], eps=eps)
                    self.check_pixel_value(overlay_id, 50, 250, [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], eps=eps)
                    self.check_pixel_value(overlay_id, 350, 250, [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], eps=eps)

                    # Passing triangle inside the viewport
                    self.check_pixel_value(overlay_id, 200, 150,
                                           [0.2 * 0.4, 1.0 * 0.6 + 0.2 * 0.4, 0.9 * 0.4, 1.0 * 0.6 + 0.4 * 0.4], 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:
                    # This would require extreme command buffer patching to de-MSAA the framebuffer and renderpass
                    if api == rd.GraphicsAPI.Vulkan and is_msaa:
                        rdtest.log.print("Quad overdraw not currently supported on MSAA on Vulkan")
                        continue

                    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:
                    # This would require extreme command buffer patching to de-MSAA the framebuffer and renderpass
                    if api == rd.GraphicsAPI.Vulkan and is_msaa:
                        rdtest.log.print("Quad overdraw not currently supported on MSAA on Vulkan")
                        continue

                    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)))

            if is_msaa:
                rdtest.log.success("All MSAA overlays are as expected")
            else:
                rdtest.log.success("All normal overlays are as expected")

        # Check the viewport overlay especially
        view_marker: rd.DrawcallDescription = self.find_draw("Viewport Test")

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

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

        col_tex: rd.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

            # 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 viewport draw".format(str(overlay)))

            tex.resourceId = col_tex
            tex.overlay = overlay
            out.SetTextureDisplay(tex)

            out.Display()

            eps = 1.0 / 256.0

            overlay_id: rd.ResourceId = out.GetDebugOverlayTexID()

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

            self.controller.SaveTexture(save_data, rdtest.get_tmp_path('overlay.png'))

            if overlay == rd.DebugOverlay.Drawcall:
                # The drawcall overlay will show up outside the scissor region
                self.check_pixel_value(overlay_id, 50, 85, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 50, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 10, [0.8, 0.1, 0.8, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 85, 85, [0.8, 0.1, 0.8, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 50, 5, [0.0, 0.0, 0.0, 0.5], eps=eps)
                self.check_pixel_value(overlay_id, 95, 85, [0.0, 0.0, 0.0, 0.5], eps=eps)
                self.check_pixel_value(overlay_id, 80, 30, [0.0, 0.0, 0.0, 0.5], 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

                found = False

                for delta in range(0, 5):
                    try:
                        self.check_pixel_value(overlay_id, 30 + delta, 32, [200.0 / 255.0, 1.0, 0.0, 1.0], eps=eps)
                        found = True
                        break
                    except rdtest.TestFailureException:
                        pass

                if not found:
                    raise rdtest.TestFailureException("Couldn't find wireframe within scissor")

                found = False

                for delta in range(0, 5):
                    try:
                        self.check_pixel_value(overlay_id, 34 + delta, 22, [200.0 / 255.0, 1.0, 0.0, 1.0], eps=eps)
                        found = True
                        break
                    except rdtest.TestFailureException:
                        pass

                if found:
                    raise rdtest.TestFailureException("Found wireframe outside of scissor")
            elif overlay == rd.DebugOverlay.Depth or overlay == rd.DebugOverlay.Stencil or overlay == rd.DebugOverlay.BackfaceCull:
                self.check_pixel_value(overlay_id, 50, 25, [0.0, 1.0, 0.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 75, [0.0, 1.0, 0.0, 1.0], eps=eps)

                self.check_pixel_value(overlay_id, 50, 20, [0.0, 1.0, 0.0, 0.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 80, [0.0, 1.0, 0.0, 0.0], eps=eps)
            elif overlay == rd.DebugOverlay.ViewportScissor:
                # Inside viewport and scissor, passing triangle
                self.check_pixel_value(overlay_id, 50, 50,
                                       [0.2 * 0.4, 1.0 * 0.6 + 0.2 * 0.4, 0.9 * 0.4, 1.0 * 0.6 + 0.4 * 0.4], eps=eps)

                # Inside viewport and outside scissor
                self.check_pixel_value(overlay_id, 50, 80,
                                       [1.0 * 0.6 + 0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 1.0 * 0.6 + 0.4 * 0.4], eps=eps)
            elif overlay == rd.DebugOverlay.QuadOverdrawDraw:
                self.check_pixel_value(overlay_id, 50, 50, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 15, [0.0, 0.0, 0.0, 0.0], eps=eps)
            elif overlay == rd.DebugOverlay.QuadOverdrawPass:
                self.check_pixel_value(overlay_id, 50, 50, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 15, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 270, [1.0, 1.0, 1.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 280, [0.0, 0.0, 0.0, 0.0], eps=eps)
            elif overlay == rd.DebugOverlay.TriangleSizeDraw:
                eps = 1.0

                self.check_pixel_value(overlay_id, 50, 50, [5408.0, 5408.0, 5408.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 15, [0.0, 0.0, 0.0, 0.0], eps=eps)
            elif overlay == rd.DebugOverlay.TriangleSizePass:
                eps = 1.0

                self.check_pixel_value(overlay_id, 50, 50, [5408.0, 5408.0, 5408.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 50, 15, [0.0, 0.0, 0.0, 0.0], eps=eps)

                self.check_pixel_value(overlay_id, 200, 270, [43072.0, 43072.0, 43072.0, 1.0], eps=eps)
                self.check_pixel_value(overlay_id, 200, 280, [0.0, 0.0, 0.0, 0.0], eps=eps)

            rdtest.log.success("Picked pixels are as expected for {}".format(str(overlay)))

        rdtest.log.success("Overlays are as expected around viewport/scissor behaviour")

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

        # Now check clear-before-X by hand, for colour and for depth
        self.controller.SetFrameEvent(test_marker.next.eventId, True)

        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)

        rdtest.log.success("Colour and depth at end are correct")

        # 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, pipe.GetOutputTargets()[0].firstSlice, 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.print("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 >> shift, 150 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))
                self.check_pixel_value(overlay_id, 197 >> shift, 147 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))
                self.check_pixel_value(overlay_id, 203 >> shift, 153 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(0, 0, 0))

                # Also for array slice 0 on this mip
                self.check_pixel_value(overlay_id, 200 >> shift, 150 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(mip, 0, 0))
                self.check_pixel_value(overlay_id, 197 >> shift, 147 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(mip, 0, 0))
                self.check_pixel_value(overlay_id, 203 >> shift, 153 >> shift, [0.0, 0.0, 0.0, 0.0], sub=rd.Subresource(mip, 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, 20 >> shift, 15 >> shift,
                                           [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 80 >> shift, 15 >> shift,
                                           [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 20 >> shift, 60 >> shift,
                                           [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], sub=sub, eps=eps)
                    self.check_pixel_value(overlay_id, 80 >> shift, 60 >> shift,
                                           [0.2 * 0.4, 0.2 * 0.4, 0.9 * 0.4, 0.4 * 0.4], sub=sub, eps=eps)

                    self.check_pixel_value(overlay_id, 50 >> shift, 36 >> shift,
                                           [0.2 * 0.4, 1.0 * 0.6 + 0.2 * 0.4, 0.9 * 0.4, 1.0 * 0.6 + 0.4 * 0.4],
                                           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()