Implement VkSamplerYcbcrConversion object

Implement the vk::SamplerYcbcrConversion object and pass it to the
vk::Sampler and vk::ImageView constructors if present as an
VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO extension struct value.

Bug: b/132437008
Tests: dEQP-VK.*ycbcr*
Change-Id: I16b03dc37920dd1eee7de80a370f8f7386dc4cea
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31614
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkImageView.cpp b/src/Vulkan/VkImageView.cpp
index 512743c..1560d0b 100644
--- a/src/Vulkan/VkImageView.cpp
+++ b/src/Vulkan/VkImageView.cpp
@@ -19,10 +19,7 @@
 {
 	VkComponentMapping ResolveComponentMapping(VkComponentMapping m, vk::Format format)
 	{
-		if (m.r == VK_COMPONENT_SWIZZLE_IDENTITY) m.r = VK_COMPONENT_SWIZZLE_R;
-		if (m.g == VK_COMPONENT_SWIZZLE_IDENTITY) m.g = VK_COMPONENT_SWIZZLE_G;
-		if (m.b == VK_COMPONENT_SWIZZLE_IDENTITY) m.b = VK_COMPONENT_SWIZZLE_B;
-		if (m.a == VK_COMPONENT_SWIZZLE_IDENTITY) m.a = VK_COMPONENT_SWIZZLE_A;
+		m = vk::ResolveIdentityMapping(m);
 
 		// Replace non-present components with zero/one swizzles so that the sampler
 		// will give us correct interactions between channel replacement and texel replacement,
@@ -58,10 +55,11 @@
 
 std::atomic<uint32_t> ImageView::nextID(1);
 
-ImageView::ImageView(const VkImageViewCreateInfo* pCreateInfo, void* mem) :
+ImageView::ImageView(const VkImageViewCreateInfo* pCreateInfo, void* mem, const vk::SamplerYcbcrConversion *ycbcrConversion) :
 	image(Cast(pCreateInfo->image)), viewType(pCreateInfo->viewType), format(pCreateInfo->format),
 	components(ResolveComponentMapping(pCreateInfo->components, format)),
-	subresourceRange(ResolveRemainingLevelsLayers(pCreateInfo->subresourceRange, image))
+	subresourceRange(ResolveRemainingLevelsLayers(pCreateInfo->subresourceRange, image)),
+	ycbcrConversion(ycbcrConversion)
 {
 }
 
diff --git a/src/Vulkan/VkImageView.hpp b/src/Vulkan/VkImageView.hpp
index 718ebd2..f857f4d 100644
--- a/src/Vulkan/VkImageView.hpp
+++ b/src/Vulkan/VkImageView.hpp
@@ -24,6 +24,7 @@
 
 namespace vk
 {
+class SamplerYcbcrConversion;
 
 class ImageView : public Object<ImageView, VkImageView>
 {
@@ -33,7 +34,7 @@
 	// SAMPLING: Image used for texture sampling
 	enum Usage { RAW, SAMPLING };
 
-	ImageView(const VkImageViewCreateInfo* pCreateInfo, void* mem);
+	ImageView(const VkImageViewCreateInfo* pCreateInfo, void* mem, const vk::SamplerYcbcrConversion *ycbcrConversion);
 	void destroy(const VkAllocationCallbacks* pAllocator);
 
 	static size_t ComputeRequiredAllocationSize(const VkImageViewCreateInfo* pCreateInfo);
@@ -72,6 +73,7 @@
 	size_t getImageSizeInBytes() const { return image->getMemoryRequirements().size; }
 
 	const uint32_t id = nextID++;
+
 private:
 	static std::atomic<uint32_t> nextID;
 	friend class BufferView;	// ImageView/BufferView share the ID space above.
@@ -84,8 +86,21 @@
 	const Format                  format;
 	const VkComponentMapping      components = {};
 	const VkImageSubresourceRange subresourceRange = {};
+
+	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
 };
 
+// TODO(b/132437008): Also used by SamplerYcbcrConversion. Move somewhere centrally?
+inline VkComponentMapping ResolveIdentityMapping(VkComponentMapping m)
+{
+	return {
+			(m.r == VK_COMPONENT_SWIZZLE_IDENTITY) ? VK_COMPONENT_SWIZZLE_R : m.r,
+			(m.g == VK_COMPONENT_SWIZZLE_IDENTITY) ? VK_COMPONENT_SWIZZLE_G : m.g,
+			(m.b == VK_COMPONENT_SWIZZLE_IDENTITY) ? VK_COMPONENT_SWIZZLE_B : m.b,
+			(m.a == VK_COMPONENT_SWIZZLE_IDENTITY) ? VK_COMPONENT_SWIZZLE_A : m.a,
+		};
+}
+
 static inline ImageView* Cast(VkImageView object)
 {
 	return reinterpret_cast<ImageView*>(object.get());
diff --git a/src/Vulkan/VkSampler.hpp b/src/Vulkan/VkSampler.hpp
index 97a7190..4313e3c 100644
--- a/src/Vulkan/VkSampler.hpp
+++ b/src/Vulkan/VkSampler.hpp
@@ -16,6 +16,7 @@
 #define VK_SAMPLER_HPP_
 
 #include "VkDevice.hpp"
+#include "VkImageView.hpp"  // For ResolveIdentityMapping()
 #include "Device/Config.hpp"
 #include "System/Math.hpp"
 
@@ -27,7 +28,7 @@
 class Sampler : public Object<Sampler, VkSampler>
 {
 public:
-	Sampler(const VkSamplerCreateInfo* pCreateInfo, void* mem) :
+	Sampler(const VkSamplerCreateInfo* pCreateInfo, void* mem, const vk::SamplerYcbcrConversion *ycbcrConversion) :
 		magFilter(pCreateInfo->magFilter),
 		minFilter(pCreateInfo->minFilter),
 		mipmapMode(pCreateInfo->mipmapMode),
@@ -42,7 +43,8 @@
 		minLod(ClampLod(pCreateInfo->minLod)),
 		maxLod(ClampLod(pCreateInfo->maxLod)),
 		borderColor(pCreateInfo->borderColor),
-		unnormalizedCoordinates(pCreateInfo->unnormalizedCoordinates)
+		unnormalizedCoordinates(pCreateInfo->unnormalizedCoordinates),
+		ycbcrConversion(ycbcrConversion)
 	{
 	}
 
@@ -74,15 +76,54 @@
 	const VkBorderColor        borderColor = VK_BORDER_COLOR_FLOAT_TRANSPARENT_BLACK;
 	const VkBool32             unnormalizedCoordinates = VK_FALSE;
 
+	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
+
 private:
 	static std::atomic<uint32_t> nextID;
 };
 
+class SamplerYcbcrConversion : public Object<SamplerYcbcrConversion, VkSamplerYcbcrConversion>
+{
+public:
+	SamplerYcbcrConversion(const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, void* mem) :
+		format(pCreateInfo->format),
+		ycbcrModel(pCreateInfo->ycbcrModel),
+		ycbcrRange(pCreateInfo->ycbcrRange),
+		components(ResolveIdentityMapping(pCreateInfo->components)),
+		xChromaOffset(pCreateInfo->xChromaOffset),
+		yChromaOffset(pCreateInfo->yChromaOffset),
+		chromaFilter(pCreateInfo->chromaFilter),
+		forceExplicitReconstruction(pCreateInfo->forceExplicitReconstruction)
+	{
+	}
+
+	~SamplerYcbcrConversion() = default;
+
+	static size_t ComputeRequiredAllocationSize(const VkSamplerYcbcrConversionCreateInfo* pCreateInfo)
+	{
+		return 0;
+	}
+
+	const VkFormat                      format = VK_FORMAT_UNDEFINED;
+	const VkSamplerYcbcrModelConversion ycbcrModel = VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
+	const VkSamplerYcbcrRange           ycbcrRange = VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
+	const VkComponentMapping            components = {VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A};
+	const VkChromaLocation              xChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
+	const VkChromaLocation              yChromaOffset = VK_CHROMA_LOCATION_COSITED_EVEN;
+	const VkFilter                      chromaFilter = VK_FILTER_NEAREST;
+	const VkBool32                      forceExplicitReconstruction = VK_FALSE;
+};
+
 static inline Sampler* Cast(VkSampler object)
 {
 	return reinterpret_cast<Sampler*>(object.get());
 }
 
+static inline SamplerYcbcrConversion* Cast(VkSamplerYcbcrConversion object)
+{
+	return reinterpret_cast<SamplerYcbcrConversion*>(object.get());
+}
+
 } // namespace vk
 
 #endif // VK_SAMPLER_HPP_
\ No newline at end of file
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index c639a81..081414e 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -443,7 +443,7 @@
 			break;
 		default:
 			// "the [driver] must skip over, without processing (other than reading the sType and pNext members) any structures in the chain with sType values not defined by [supported extenions]"
-			UNIMPLEMENTED("extensionCreateInfo->sType");   // TODO(b/119321052): UNIMPLEMENTED() should be used only for features that must still be implemented. Use a more informational macro here.
+			UNIMPLEMENTED("extensionCreateInfo->sType %d", int(extensionCreateInfo->sType));   // TODO(b/119321052): UNIMPLEMENTED() should be used only for features that must still be implemented. Use a more informational macro here.
 			break;
 		}
 
@@ -1045,6 +1045,7 @@
 	}
 
 	const VkBaseInStructure* extensionCreateInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
 
 	while(extensionCreateInfo)
 	{
@@ -1058,8 +1059,10 @@
 		break;
 		case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO:
 		{
-			const VkSamplerYcbcrConversionInfo* ycbcrConversionInfo = reinterpret_cast<const VkSamplerYcbcrConversionInfo*>(extensionCreateInfo);
-			if(ycbcrConversionInfo->conversion != VK_NULL_HANDLE)
+			const VkSamplerYcbcrConversionInfo* samplerYcbcrConversionInfo = reinterpret_cast<const VkSamplerYcbcrConversionInfo*>(extensionCreateInfo);
+			ycbcrConversion = vk::Cast(samplerYcbcrConversionInfo->conversion);
+
+			if(ycbcrConversion != VK_NULL_HANDLE)
 			{
 				ASSERT((pCreateInfo->components.r == VK_COMPONENT_SWIZZLE_IDENTITY) &&
 				       (pCreateInfo->components.g == VK_COMPONENT_SWIZZLE_IDENTITY) &&
@@ -1069,14 +1072,14 @@
 		}
 		break;
 		default:
-			UNIMPLEMENTED("extensionCreateInfo->sType");
+			UNIMPLEMENTED("extensionCreateInfo->sType %d", int(extensionCreateInfo->sType));
 			break;
 		}
 
 		extensionCreateInfo = extensionCreateInfo->pNext;
 	}
 
-	return vk::ImageView::Create(pAllocator, pCreateInfo, pView);
+	return vk::ImageView::Create(pAllocator, pCreateInfo, pView, ycbcrConversion);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkDestroyImageView(VkDevice device, VkImageView imageView, const VkAllocationCallbacks* pAllocator)
@@ -1245,12 +1248,33 @@
 	TRACE("(VkDevice device = %p, const VkSamplerCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkSampler* pSampler = %p)",
 		    device, pCreateInfo, pAllocator, pSampler);
 
-	if(pCreateInfo->pNext || pCreateInfo->flags)
+	if(pCreateInfo->flags)
 	{
 		UNIMPLEMENTED("pCreateInfo->pNext || pCreateInfo->flags");
 	}
 
-	return vk::Sampler::Create(pAllocator, pCreateInfo, pSampler);
+	const VkBaseInStructure* extensionCreateInfo = reinterpret_cast<const VkBaseInStructure*>(pCreateInfo->pNext);
+	const vk::SamplerYcbcrConversion *ycbcrConversion = nullptr;
+
+	while(extensionCreateInfo)
+	{
+		switch(extensionCreateInfo->sType)
+		{
+		case VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO:
+			{
+				const VkSamplerYcbcrConversionInfo* samplerYcbcrConversionInfo = reinterpret_cast<const VkSamplerYcbcrConversionInfo*>(extensionCreateInfo);
+				ycbcrConversion = vk::Cast(samplerYcbcrConversionInfo->conversion);
+			}
+			break;
+		default:
+			UNIMPLEMENTED("extensionCreateInfo->sType %d", int(extensionCreateInfo->sType));
+			break;
+		}
+
+		extensionCreateInfo = extensionCreateInfo->pNext;
+	}
+
+	return vk::Sampler::Create(pAllocator, pCreateInfo, pSampler, ycbcrConversion);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkDestroySampler(VkDevice device, VkSampler sampler, const VkAllocationCallbacks* pAllocator)
@@ -1276,7 +1300,7 @@
 			ASSERT(!vk::Cast(device)->hasExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME));
 			break;
 		default:
-			UNIMPLEMENTED("extensionCreateInfo->sType");
+			UNIMPLEMENTED("extensionCreateInfo->sType %d", int(extensionCreateInfo->sType));
 			break;
 		}
 
@@ -1466,7 +1490,7 @@
 		}
 		break;
 		default:
-			UNIMPLEMENTED("extensionCreateInfo->sType");
+			UNIMPLEMENTED("extensionCreateInfo->sType %d", int(extensionCreateInfo->sType));
 			break;
 		}
 
@@ -2432,15 +2456,23 @@
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateSamplerYcbcrConversion(VkDevice device, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkSamplerYcbcrConversion* pYcbcrConversion)
 {
-	TRACE("()");
-	UNIMPLEMENTED("vkCreateSamplerYcbcrConversion");
-	return VK_SUCCESS;
+	TRACE("(VkDevice device = %p, const VkSamplerYcbcrConversionCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkSamplerYcbcrConversion* pYcbcrConversion = %p)",
+		    device, pCreateInfo, pAllocator, pYcbcrConversion);
+
+	if(pCreateInfo->pNext)
+	{
+		UNIMPLEMENTED("pCreateInfo->pNext");
+	}
+
+	return vk::SamplerYcbcrConversion::Create(pAllocator, pCreateInfo, pYcbcrConversion);
 }
 
 VKAPI_ATTR void VKAPI_CALL vkDestroySamplerYcbcrConversion(VkDevice device, VkSamplerYcbcrConversion ycbcrConversion, const VkAllocationCallbacks* pAllocator)
 {
-	TRACE("()");
-	UNIMPLEMENTED("vkDestroySamplerYcbcrConversion");
+	TRACE("(VkDevice device = %p, VkSamplerYcbcrConversion ycbcrConversion = %p, const VkAllocationCallbacks* pAllocator = %p)",
+	      device, ycbcrConversion.get(), pAllocator);
+
+	vk::destroy(ycbcrConversion, pAllocator);
 }
 
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateDescriptorUpdateTemplate(VkDevice device, const VkDescriptorUpdateTemplateCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDescriptorUpdateTemplate* pDescriptorUpdateTemplate)