Support images with linear tiling for transfers

The Vulkan 1.1 spec on vkCreateImage states that "Creation of images
with tiling VK_IMAGE_TILING_LINEAR may not be supported unless other
parameters meet all of the constraints:
* imageType is VK_IMAGE_TYPE_2D
* format is not a depth/stencil format
* mipLevels is 1
* arrayLayers is 1
* samples is VK_SAMPLE_COUNT_1_BIT
* usage only includes VK_IMAGE_USAGE_TRANSFER_SRC_BIT and/or
  VK_IMAGE_USAGE_TRANSFER_DST_BIT"

Also restrict YCbCr image creation in accordance with:

"Creation of images with a format requiring Y’CBCR conversion may not be
supported unless other parameters meet all of the constraints:
* imageType is VK_IMAGE_TYPE_2D
* mipLevels is 1
* arrayLayers is 1
* samples is VK_SAMPLE_COUNT_1_BIT"

Bug: b/119620767
Bug: b/132437008
Tests: dEQP-VK.*ycbcr*
Change-Id: I909fda2a154e69fbd4afb862cf4cc14c244005bf
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/31788
Tested-by: Nicolas Capens <nicolascapens@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index fb9c43c..91043c9 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -111,11 +111,15 @@
 {
 	// By spec, aspectMask has a single bit set.
 	if (!((pSubresource->aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
-		  (pSubresource->aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
-		  (pSubresource->aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)))
+	      (pSubresource->aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+	      (pSubresource->aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) ||
+	      (pSubresource->aspectMask == VK_IMAGE_ASPECT_PLANE_0_BIT) ||
+	      (pSubresource->aspectMask == VK_IMAGE_ASPECT_PLANE_1_BIT) ||
+	      (pSubresource->aspectMask == VK_IMAGE_ASPECT_PLANE_2_BIT)))
 	{
-		UNIMPLEMENTED("aspectMask");
+		UNSUPPORTED("aspectMask %X", pSubresource->aspectMask);
 	}
+
 	auto aspect = static_cast<VkImageAspectFlagBits>(pSubresource->aspectMask);
 	pLayout->offset = getMemoryOffset(aspect, pSubresource->mipLevel, pSubresource->arrayLayer);
 	pLayout->size = getMultiSampledLevelSize(aspect, pSubresource->mipLevel);
@@ -130,18 +134,24 @@
 	// an image to another image that has the same number of bytes per pixel.
 	Image* dst = Cast(dstImage);
 
-	if(!((pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
-		 (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
-		 (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)))
+	if (!((pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+	      (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+	      (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) ||
+	      (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_0_BIT) ||
+	      (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_1_BIT) ||
+	      (pRegion.srcSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_2_BIT)))
 	{
-		UNIMPLEMENTED("srcSubresource");
+		UNSUPPORTED("srcSubresource.aspectMask %X", pRegion.srcSubresource.aspectMask);
 	}
 
-	if(!((pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
-		 (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
-		 (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)))
+	if (!((pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+	      (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
+	      (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT) ||
+	      (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_0_BIT) ||
+	      (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_1_BIT) ||
+	      (pRegion.dstSubresource.aspectMask == VK_IMAGE_ASPECT_PLANE_2_BIT)))
 	{
-		UNIMPLEMENTED("dstSubresource");
+		UNSUPPORTED("dstSubresource.aspectMask %X", pRegion.dstSubresource.aspectMask);
 	}
 
 	VkImageAspectFlagBits srcAspect = static_cast<VkImageAspectFlagBits>(pRegion.srcSubresource.aspectMask);
@@ -510,9 +520,9 @@
 	mipLevelExtent.height = extent.height >> mipLevel;
 	mipLevelExtent.depth = extent.depth >> mipLevel;
 
-	if(mipLevelExtent.width == 0)  mipLevelExtent.width = 1;
-	if(mipLevelExtent.height == 0) mipLevelExtent.height = 1;
-	if(mipLevelExtent.depth == 0)  mipLevelExtent.depth = 1;
+	if(mipLevelExtent.width  == 0) { mipLevelExtent.width  = 1; }
+	if(mipLevelExtent.height == 0) { mipLevelExtent.height = 1; }
+	if(mipLevelExtent.depth  == 0) { mipLevelExtent.depth  = 1; }
 
 	switch(aspect)
 	{
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index dda45f3..8a3b59a 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -375,7 +375,7 @@
 	return true;
 }
 
-void PhysicalDevice::getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties) const
+void PhysicalDevice::getFormatProperties(Format format, VkFormatProperties* pFormatProperties) const
 {
 	pFormatProperties->linearTilingFeatures = 0; // Unsupported format
 	pFormatProperties->optimalTilingFeatures = 0; // Unsupported format
@@ -660,11 +660,17 @@
 	default:
 		break;
 	}
+
+	if(pFormatProperties->optimalTilingFeatures)
+	{
+		pFormatProperties->linearTilingFeatures = VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
+		                                          VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
+	}
 }
 
-void PhysicalDevice::getImageFormatProperties(VkFormat format, VkImageType type, VkImageTiling tiling,
+void PhysicalDevice::getImageFormatProperties(Format format, VkImageType type, VkImageTiling tiling,
                                               VkImageUsageFlags usage, VkImageCreateFlags flags,
-	                                          VkImageFormatProperties* pImageFormatProperties) const
+                                              VkImageFormatProperties* pImageFormatProperties) const
 {
 	pImageFormatProperties->sampleCounts = VK_SAMPLE_COUNT_1_BIT;
 	pImageFormatProperties->maxArrayLayers = vk::MAX_IMAGE_ARRAY_LAYERS;
@@ -713,6 +719,24 @@
 	}
 
 	pImageFormatProperties->maxResourceSize = 1 << 31; // Minimum value for maxResourceSize
+
+	// "Images created with tiling equal to VK_IMAGE_TILING_LINEAR have further restrictions on their limits and capabilities
+	//  compared to images created with tiling equal to VK_IMAGE_TILING_OPTIMAL."
+	if(tiling == VK_IMAGE_TILING_LINEAR)
+	{
+		pImageFormatProperties->maxMipLevels = 1;
+		pImageFormatProperties->maxArrayLayers = 1;
+		pImageFormatProperties->sampleCounts = VK_SAMPLE_COUNT_1_BIT;
+	}
+
+	// "Images created with a format from one of those listed in Formats requiring sampler Y’CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views
+	//  have further restrictions on their limits and capabilities compared to images created with other formats."
+	if(format.isYcbcrFormat())
+	{
+		pImageFormatProperties->maxMipLevels = 1;
+		pImageFormatProperties->maxArrayLayers = 1;
+		pImageFormatProperties->sampleCounts = VK_SAMPLE_COUNT_1_BIT;
+	}
 }
 
 uint32_t PhysicalDevice::getQueueFamilyPropertyCount() const
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index 216b22a..c126675 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -16,6 +16,7 @@
 #define VK_PHYSICAL_DEVICE_HPP_
 
 #include "VkObject.hpp"
+#include "VkFormat.h"
 
 #ifdef VK_USE_PLATFORM_ANDROID_KHR
 #include <vulkan/vk_android_native_buffer.h>
@@ -60,8 +61,8 @@
 	void getProperties(const VkPhysicalDeviceExternalFenceInfo* pExternalFenceInfo, VkExternalFenceProperties* pExternalFenceProperties) const;
 	void getProperties(const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo, VkExternalSemaphoreProperties* pExternalSemaphoreProperties) const;
 
-	void getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties) const;
-	void getImageFormatProperties(VkFormat format, VkImageType type, VkImageTiling tiling,
+	void getFormatProperties(Format format, VkFormatProperties* pFormatProperties) const;
+	void getImageFormatProperties(Format format, VkImageType type, VkImageTiling tiling,
 	                              VkImageUsageFlags usage, VkImageCreateFlags flags,
 	                              VkImageFormatProperties* pImageFormatProperties) const;
 	uint32_t getQueueFamilyPropertyCount() const;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 081414e..891ba3c 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -232,6 +232,10 @@
 	TRACE("(VkPhysicalDevice physicalDevice = %p, VkFormat format = %d, VkImageType type = %d, VkImageTiling tiling = %d, VkImageUsageFlags usage = %d, VkImageCreateFlags flags = %d, VkImageFormatProperties* pImageFormatProperties = %p)",
 			physicalDevice, (int)format, (int)type, (int)tiling, usage, flags, pImageFormatProperties);
 
+	// "If the combination of parameters to vkGetPhysicalDeviceImageFormatProperties is not supported by the implementation
+	//  for use in vkCreateImage, then all members of VkImageFormatProperties will be filled with zero."
+	memset(pImageFormatProperties, 0, sizeof(VkImageFormatProperties));
+
 	VkFormatProperties properties;
 	vk::Cast(physicalDevice)->getFormatProperties(format, &properties);
 
@@ -302,6 +306,31 @@
 			VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
 	ASSERT(!(usage & ~(allRecognizedUsageBits)));
 
+	// "Images created with tiling equal to VK_IMAGE_TILING_LINEAR have further restrictions on their limits and capabilities
+	//  compared to images created with tiling equal to VK_IMAGE_TILING_OPTIMAL."
+	if(tiling == VK_IMAGE_TILING_LINEAR)
+	{
+		if(type != VK_IMAGE_TYPE_2D)
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+
+		if(vk::Format(format).isDepth() || vk::Format(format).isStencil())
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+	}
+
+	// "Images created with a format from one of those listed in Formats requiring sampler Y’CBCR conversion for VK_IMAGE_ASPECT_COLOR_BIT image views
+	//  have further restrictions on their limits and capabilities compared to images created with other formats."
+	if(vk::Format(format).isYcbcrFormat())
+	{
+		if(type != VK_IMAGE_TYPE_2D)
+		{
+			return VK_ERROR_FORMAT_NOT_SUPPORTED;
+		}
+	}
+
 	vk::Cast(physicalDevice)->getImageFormatProperties(format, type, tiling, usage, flags, pImageFormatProperties);
 
 	return VK_SUCCESS;