Add ASTC LDR error colour quality warning

Some hardware can output the HDR error colour (black) instead of the
LDR error colour (magenta). Valid compression blocks are rendered
correctly.

This change adds a second comparison mechanism if the initial *fast*
deMemCmp fails; the texture values are compared allowing for any value
matching an error colour to compare against the other error colour.

If such a match is detected the test will output a QualityWarning
instead of a Pass.

If the two compared values don't match, but are not both error colours
the test result is still a Fail.

Affects:

dEQP-VK.image.texel_view_compatible*astc*

Components: Vulkan

VK-GL-CTS issue: 1231

Change-Id: Ie14913de0f2f3cf7fed90c275e48c67181c1e7ff
(cherry picked from commit b038d43aa868be1e6d4471c6977984151ada3af5)
diff --git a/external/vulkancts/modules/vulkan/image/vktImageCompressionTranscodingSupport.cpp b/external/vulkancts/modules/vulkan/image/vktImageCompressionTranscodingSupport.cpp
index bb3a9e3..a6ae4df 100644
--- a/external/vulkancts/modules/vulkan/image/vktImageCompressionTranscodingSupport.cpp
+++ b/external/vulkancts/modules/vulkan/image/vktImageCompressionTranscodingSupport.cpp
@@ -51,6 +51,7 @@
 #include "tcuSurface.hpp"
 
 #include <vector>
+
 using namespace vk;
 namespace vkt
 {
@@ -111,6 +112,7 @@
 	VkImageUsageFlags	uncompressedImageUsage;
 	bool				useMipmaps;
 	VkFormat			formatForVerify;
+	bool				formatIsASTC;
 };
 
 template<typename T>
@@ -128,6 +130,93 @@
 const deUint32 SINGLE_LEVEL = 1u;
 const deUint32 SINGLE_LAYER = 1u;
 
+enum BinaryCompareMode
+{
+	COMPARE_MODE_NORMAL,
+	COMPARE_MODE_ALLOW_ASTC_ERROR_COLOUR_WARNING,
+};
+
+enum BinaryCompareResult
+{
+	COMPARE_RESULT_OK,
+	COMPARE_RESULT_ASTC_QUALITY_WARNING,
+	COMPARE_RESULT_FAILED,
+};
+
+const deUint32 ASTC_LDR_ERROR_COLOUR = 0xFFFF00FF;
+const deUint32 ASTC_HDR_ERROR_COLOUR = 0x00000000;
+
+static BinaryCompareResult BinaryCompare(const void				*reference,
+										 const void				*result,
+										 VkDeviceSize			sizeInBytes,
+										 VkFormat				formatForVerify,
+										 BinaryCompareMode		mode)
+{
+	DE_UNREF(formatForVerify);
+
+	// Compare quickly using deMemCmp
+	if (deMemCmp(reference, result, (size_t)sizeInBytes) == 0)
+	{
+		return COMPARE_RESULT_OK;
+	}
+	// If deMemCmp indicated a mismatch, we can re-check with a manual comparison of
+	// the ref and res images that allows for ASTC error colour mismatches if the ASTC
+	// comparison mode was selected. This slows down the affected ASTC tests if you
+	// didn't pass in the first comparison, but means in the general case the
+	// comparion is still fast.
+	else if (mode == COMPARE_MODE_ALLOW_ASTC_ERROR_COLOUR_WARNING)
+	{
+		bool bWarn = false;
+		bool bFail = false;
+		const deUint32 *pui32RefVal = (deUint32*)reference;
+		const deUint32 *pui32ResVal = (deUint32*)result;
+
+		DE_ASSERT(formatForVerify == VK_FORMAT_R8G8B8A8_UNORM);
+		size_t numPixels = (size_t)(sizeInBytes / 4) /* bytes */;
+		for (size_t i = 0; i < numPixels; i++)
+		{
+			const deUint32 ref = *pui32RefVal++;
+			const deUint32 res = *pui32ResVal++;
+
+			if (ref != res)
+			{
+				// QualityWarning !1231: If the astc pixel was the ASTC LDR error colour
+				// and the result image has the HDR error colour (or vice versa as the test
+				// cases below sometimes reverse the operands) then issue a quality warning
+				// instead of a failure.
+				if ((ref == ASTC_LDR_ERROR_COLOUR && res == ASTC_HDR_ERROR_COLOUR) ||
+					(ref == ASTC_HDR_ERROR_COLOUR && res == ASTC_LDR_ERROR_COLOUR))
+				{
+					bWarn = true;
+				}
+				else
+				{
+					bFail = true;
+				}
+			}
+		}
+
+		if (!bFail)
+		{
+			return (bWarn)
+				? (COMPARE_RESULT_ASTC_QUALITY_WARNING)
+				: (COMPARE_RESULT_OK);
+		}
+	}
+
+	return COMPARE_RESULT_FAILED;
+}
+
+static bool FormatIsASTC(VkFormat format)
+{
+	return deInRange32(format, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK);
+}
+
+static TestStatus TestStatusASTCQualityWarning()
+{
+	return TestStatus(QP_TEST_RESULT_QUALITY_WARNING, "ASTC HDR error colour output instead of LDR error colour");
+}
+
 class BasicTranscodingTestInstance : public TestInstance
 {
 public:
@@ -153,6 +242,10 @@
 	const deUint32			m_levelCount;
 	const UVec3				m_layerSize;
 
+	// Detected error colour mismatch while verifying image. Output
+	// the ASTC quality warning instead of a pass
+	bool					m_bASTCErrorColourMismatch;
+
 private:
 	deUint32				findMipMapLevelCount			();
 };
@@ -193,6 +286,7 @@
 	, m_blockHeight	(getBlockHeight(m_parameters.formatCompressed))
 	, m_levelCount	(findMipMapLevelCount())
 	, m_layerSize	(getLayerSize(m_parameters.imageType, m_parameters.size))
+	, m_bASTCErrorColourMismatch(false)
 {
 	DE_ASSERT(deLog2Floor32(m_parameters.size.x()) == deLog2Floor32(m_parameters.size.y()));
 }
@@ -542,6 +636,13 @@
 	};
 	if (!decompressImage(*cmdBuffer, imageData, mipMapSizes))
 			return TestStatus::fail("Fail");
+
+	if (m_bASTCErrorColourMismatch)
+	{
+		DE_ASSERT(m_parameters.formatIsASTC);
+		return TestStatusASTCQualityWarning();
+	}
+
 	return TestStatus::pass("Pass");
 }
 
@@ -907,7 +1008,7 @@
 			DE_NULL,															// const void*				pNext;
 			0u,																	// VkImageCreateFlags		flags;
 			VK_IMAGE_TYPE_2D,													// VkImageType				imageType;
-			VK_FORMAT_R8G8B8A8_UNORM,											// VkFormat					format;
+			m_parameters.formatForVerify,										// VkFormat					format;
 			extentCompressed,													// VkExtent3D				extent;
 			1u,																	// deUint32					mipLevels;
 			1u,																	// deUint32					arrayLayers;
@@ -976,7 +1077,7 @@
 		Move<VkDescriptorSet>			descriptorSet			= makeDescriptorSet(vk, device, *descriptorPool, *descriptorSetLayout);
 		const Unique<VkPipelineLayout>	pipelineLayout			(makePipelineLayout(vk, device, *descriptorSetLayout));
 		const Unique<VkPipeline>		pipeline				(makeComputePipeline(vk, device, *pipelineLayout, *shaderModule));
-		const VkDeviceSize				bufferSize				= getImageSizeBytes(IVec3((int)extentCompressed.width, (int)extentCompressed.height, (int)extentCompressed.depth), VK_FORMAT_R8G8B8A8_UNORM);
+		const VkDeviceSize				bufferSize				= getImageSizeBytes(IVec3((int)extentCompressed.width, (int)extentCompressed.height, (int)extentCompressed.depth), m_parameters.formatForVerify);
 		Buffer							resultBuffer			(vk, device, allocator,
 																	makeBufferCreateInfo(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT), MemoryRequirement::HostVisible);
 		Buffer							referenceBuffer			(vk, device, allocator,
@@ -1162,7 +1263,18 @@
 		invalidateMappedMemoryRange(vk, device, resultAlloc.getMemory(), resultAlloc.getOffset(), bufferSize);
 		invalidateMappedMemoryRange(vk, device, referenceAlloc.getMemory(), referenceAlloc.getOffset(), bufferSize);
 
-		if (deMemCmp(resultAlloc.getHostPtr(), referenceAlloc.getHostPtr(), (size_t)bufferSize) != 0)
+		BinaryCompareMode compareMode =
+			(m_parameters.formatIsASTC)
+				?(COMPARE_MODE_ALLOW_ASTC_ERROR_COLOUR_WARNING)
+				:(COMPARE_MODE_NORMAL);
+
+		BinaryCompareResult res = BinaryCompare(referenceAlloc.getHostPtr(),
+												resultAlloc.getHostPtr(),
+												(size_t)bufferSize,
+												m_parameters.formatForVerify,
+												compareMode);
+
+		if (res == COMPARE_RESULT_FAILED)
 		{
 			ConstPixelBufferAccess	resultPixels		(mapVkFormat(decompressedImageInfo.format), decompressedImageInfo.extent.width, decompressedImageInfo.extent.height, decompressedImageInfo.extent.depth, resultAlloc.getHostPtr());
 			ConstPixelBufferAccess	referencePixels		(mapVkFormat(decompressedImageInfo.format), decompressedImageInfo.extent.width, decompressedImageInfo.extent.height, decompressedImageInfo.extent.depth, referenceAlloc.getHostPtr());
@@ -1170,6 +1282,10 @@
 			if(!fuzzyCompare(m_context.getTestContext().getLog(), "Image Comparison", "Image Comparison", resultPixels, referencePixels, 0.001f, tcu::COMPARE_LOG_EVERYTHING))
 				return false;
 		}
+		else if (res == COMPARE_RESULT_ASTC_QUALITY_WARNING)
+		{
+			m_bASTCErrorColourMismatch = true;
+		}
 	}
 
 	return true;
@@ -1420,6 +1536,12 @@
 					return TestStatus::fail("Images difference detected");
 			}
 
+	if (m_bASTCErrorColourMismatch)
+	{
+		DE_ASSERT(m_parameters.formatIsASTC);
+		return TestStatusASTCQualityWarning();
+	}
+
 	return TestStatus::pass("Pass");
 }
 
@@ -2006,7 +2128,18 @@
 		const Allocation&	resDstBufferAlloc	= resDstBuffer->getAllocation();
 		invalidateMappedMemoryRange(vk, device, resDstBufferAlloc.getMemory(), resDstBufferAlloc.getOffset(), dstBufferSize);
 
-		if (deMemCmp(refDstBufferAlloc.getHostPtr(), resDstBufferAlloc.getHostPtr(), (size_t)dstBufferSize) != 0)
+		BinaryCompareMode compareMode =
+			(m_parameters.formatIsASTC)
+				?(COMPARE_MODE_ALLOW_ASTC_ERROR_COLOUR_WARNING)
+				:(COMPARE_MODE_NORMAL);
+
+		BinaryCompareResult res = BinaryCompare(refDstBufferAlloc.getHostPtr(),
+												resDstBufferAlloc.getHostPtr(),
+												dstBufferSize,
+												m_parameters.formatForVerify,
+												compareMode);
+
+		if (res == COMPARE_RESULT_FAILED)
 		{
 			// Do fuzzy to log error mask
 			invalidateMappedMemoryRange(vk, device, resDstBufferAlloc.getMemory(), resDstBufferAlloc.getOffset(), dstBufferSize);
@@ -2024,6 +2157,10 @@
 
 			return false;
 		}
+		else if (res == COMPARE_RESULT_ASTC_QUALITY_WARNING)
+		{
+			m_bASTCErrorColourMismatch = true;
+		}
 	}
 
 	return true;
@@ -2608,7 +2745,7 @@
 			!physicalDeviceFeatures.textureCompressionETC2)
 			TCU_THROW(NotSupportedError, "textureCompressionETC2 not supported");
 
-		if (deInRange32(m_parameters.formatCompressed, VK_FORMAT_ASTC_4x4_UNORM_BLOCK, VK_FORMAT_ASTC_12x12_SRGB_BLOCK) &&
+		if (m_parameters.formatIsASTC &&
 			!physicalDeviceFeatures.textureCompressionASTC_LDR)
 			TCU_THROW(NotSupportedError, "textureCompressionASTC_LDR not supported");
 
@@ -2919,7 +3056,8 @@
 								compressedImageViewUsageFlags[operationNdx],
 								uncompressedImageUsageFlags[operationNdx],
 								mipmapTest,
-								VK_FORMAT_R8G8B8A8_UNORM
+								VK_FORMAT_R8G8B8A8_UNORM,
+								FormatIsASTC(formatCompressed)
 							};
 
 							compressedFormatGroup->addChild(new TexelViewCompatibleCase(testCtx, uncompressedFormatGroupName, "", parameters));