Support VK_IMAGE_CREATE_EXTENDED_USAGE_BIT

The Vulkan spec states that "For images created with
VK_IMAGE_CREATE_EXTENDED_USAGE_BIT a usage bit is valid if it is
supported for at least one of the formats a VkImageView created from the
image can have".

Previously this flag was ignored and we failed to create some images for
which the usage flag was meant for an image view with a different
format.

The Format::compatibleFormat() method was renamed to
getCompatibilityClassRepresentative(). This method is used in the
implementation of the new Format::getCompatibleFormats() method which
returns an std::vector with all the formats in the same compatibility
class.

Bug: b/214957976
Change-Id: I1db61f51744914405825c0471fee014a6b6845af
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/64568
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Nicolas Capens <nicolascapens@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
diff --git a/src/Vulkan/VkFormat.cpp b/src/Vulkan/VkFormat.cpp
index 9cb2061..cdf40ac 100644
--- a/src/Vulkan/VkFormat.cpp
+++ b/src/Vulkan/VkFormat.cpp
@@ -682,7 +682,368 @@
 	}
 }
 
-VkFormat Format::compatibleFormat() const
+std::vector<Format> Format::getCompatibleFormats() const
+{
+	switch(getCompatibilityClassRepresentative())
+	{
+	// 8 - bit, Block size 1 byte, 1 texel / block
+	case VK_FORMAT_R8_UNORM:
+		return {
+			VK_FORMAT_R4G4_UNORM_PACK8,
+			VK_FORMAT_R8_UNORM,
+			VK_FORMAT_R8_SNORM,
+			VK_FORMAT_R8_USCALED,
+			VK_FORMAT_R8_SSCALED,
+			VK_FORMAT_R8_UINT,
+			VK_FORMAT_R8_SINT,
+			VK_FORMAT_R8_SRGB,
+		};
+
+	// 16 - bit, Block size 2 bytes, 1 texel / block
+	case VK_FORMAT_R16_UNORM:
+		return {
+			VK_FORMAT_R4G4B4A4_UNORM_PACK16,
+			VK_FORMAT_B4G4R4A4_UNORM_PACK16,
+			VK_FORMAT_A4R4G4B4_UNORM_PACK16,
+			VK_FORMAT_A4B4G4R4_UNORM_PACK16,
+			VK_FORMAT_R5G6B5_UNORM_PACK16,
+			VK_FORMAT_B5G6R5_UNORM_PACK16,
+			VK_FORMAT_R5G5B5A1_UNORM_PACK16,
+			VK_FORMAT_B5G5R5A1_UNORM_PACK16,
+			VK_FORMAT_A1R5G5B5_UNORM_PACK16,
+			VK_FORMAT_R8G8_UNORM,
+			VK_FORMAT_R8G8_SNORM,
+			VK_FORMAT_R8G8_USCALED,
+			VK_FORMAT_R8G8_SSCALED,
+			VK_FORMAT_R8G8_UINT,
+			VK_FORMAT_R8G8_SINT,
+			VK_FORMAT_R8G8_SRGB,
+			VK_FORMAT_R16_UNORM,
+			VK_FORMAT_R16_SNORM,
+			VK_FORMAT_R16_USCALED,
+			VK_FORMAT_R16_SSCALED,
+			VK_FORMAT_R16_UINT,
+			VK_FORMAT_R16_SINT,
+			VK_FORMAT_R16_SFLOAT,
+			VK_FORMAT_R10X6_UNORM_PACK16,
+			VK_FORMAT_R12X4_UNORM_PACK16,
+		};
+
+	// 32 - bit, Block size 4 bytes, 1 texel / block
+	case VK_FORMAT_R8G8B8A8_UNORM:
+		return {
+			VK_FORMAT_R8G8B8A8_UNORM,
+			VK_FORMAT_R8G8B8A8_SNORM,
+			VK_FORMAT_R8G8B8A8_USCALED,
+			VK_FORMAT_R8G8B8A8_SSCALED,
+			VK_FORMAT_R8G8B8A8_UINT,
+			VK_FORMAT_R8G8B8A8_SINT,
+			VK_FORMAT_R8G8B8A8_SRGB,
+			VK_FORMAT_B8G8R8A8_UNORM,
+			VK_FORMAT_B8G8R8A8_SNORM,
+			VK_FORMAT_B8G8R8A8_USCALED,
+			VK_FORMAT_B8G8R8A8_SSCALED,
+			VK_FORMAT_B8G8R8A8_UINT,
+			VK_FORMAT_B8G8R8A8_SINT,
+			VK_FORMAT_B8G8R8A8_SRGB,
+			VK_FORMAT_A8B8G8R8_UNORM_PACK32,
+			VK_FORMAT_A8B8G8R8_SNORM_PACK32,
+			VK_FORMAT_A8B8G8R8_USCALED_PACK32,
+			VK_FORMAT_A8B8G8R8_SSCALED_PACK32,
+			VK_FORMAT_A8B8G8R8_UINT_PACK32,
+			VK_FORMAT_A8B8G8R8_SINT_PACK32,
+			VK_FORMAT_A8B8G8R8_SRGB_PACK32,
+			VK_FORMAT_A2R10G10B10_UNORM_PACK32,
+			VK_FORMAT_A2R10G10B10_SNORM_PACK32,
+			VK_FORMAT_A2R10G10B10_USCALED_PACK32,
+			VK_FORMAT_A2R10G10B10_SSCALED_PACK32,
+			VK_FORMAT_A2R10G10B10_UINT_PACK32,
+			VK_FORMAT_A2R10G10B10_SINT_PACK32,
+			VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+			VK_FORMAT_A2B10G10R10_SNORM_PACK32,
+			VK_FORMAT_A2B10G10R10_USCALED_PACK32,
+			VK_FORMAT_A2B10G10R10_SSCALED_PACK32,
+			VK_FORMAT_A2B10G10R10_UINT_PACK32,
+			VK_FORMAT_A2B10G10R10_SINT_PACK32,
+			VK_FORMAT_R16G16_UNORM,
+			VK_FORMAT_R16G16_SNORM,
+			VK_FORMAT_R16G16_USCALED,
+			VK_FORMAT_R16G16_SSCALED,
+			VK_FORMAT_R16G16_UINT,
+			VK_FORMAT_R16G16_SINT,
+			VK_FORMAT_R16G16_SFLOAT,
+			VK_FORMAT_R32_UINT,
+			VK_FORMAT_R32_SINT,
+			VK_FORMAT_R32_SFLOAT,
+			VK_FORMAT_B10G11R11_UFLOAT_PACK32,
+			VK_FORMAT_E5B9G9R9_UFLOAT_PACK32,
+			VK_FORMAT_R10X6G10X6_UNORM_2PACK16,
+			VK_FORMAT_R12X4G12X4_UNORM_2PACK16,
+		};
+
+	// 48 - bit, Block size 6 bytes, 1 texel / block
+	case VK_FORMAT_R16G16B16_UNORM:
+		return {
+			VK_FORMAT_R16G16B16_UNORM,
+			VK_FORMAT_R16G16B16_SNORM,
+			VK_FORMAT_R16G16B16_USCALED,
+			VK_FORMAT_R16G16B16_SSCALED,
+			VK_FORMAT_R16G16B16_UINT,
+			VK_FORMAT_R16G16B16_SINT,
+			VK_FORMAT_R16G16B16_SFLOAT,
+		};
+
+	// 64 - bit, Block size 8 bytes, 1 texel / block
+	case VK_FORMAT_R16G16B16A16_UNORM:
+		return {
+			VK_FORMAT_R16G16B16A16_UNORM,
+			VK_FORMAT_R16G16B16A16_SNORM,
+			VK_FORMAT_R16G16B16A16_USCALED,
+			VK_FORMAT_R16G16B16A16_SSCALED,
+			VK_FORMAT_R16G16B16A16_UINT,
+			VK_FORMAT_R16G16B16A16_SINT,
+			VK_FORMAT_R16G16B16A16_SFLOAT,
+			VK_FORMAT_R32G32_UINT,
+			VK_FORMAT_R32G32_SINT,
+			VK_FORMAT_R32G32_SFLOAT,
+			VK_FORMAT_R64_UINT,
+			VK_FORMAT_R64_SINT,
+			VK_FORMAT_R64_SFLOAT,
+		};
+
+	// 96 - bit, Block size 12 bytes, 1 texel / block
+	case VK_FORMAT_R32G32B32_UINT:
+		return {
+			VK_FORMAT_R32G32B32_UINT,
+			VK_FORMAT_R32G32B32_SINT,
+			VK_FORMAT_R32G32B32_SFLOAT,
+		};
+
+	// 128 - bit, Block size 16 bytes, 1 texel / block
+	case VK_FORMAT_R32G32B32A32_UINT:
+		return {
+			VK_FORMAT_R32G32B32A32_UINT,
+			VK_FORMAT_R32G32B32A32_SINT,
+			VK_FORMAT_R32G32B32A32_SFLOAT,
+			VK_FORMAT_R64G64_UINT,
+			VK_FORMAT_R64G64_SINT,
+			VK_FORMAT_R64G64_SFLOAT,
+		};
+
+	// 192 - bit, Block size 24 bytes, 1 texel / block
+	case VK_FORMAT_R64G64B64_UINT:
+		return {
+			VK_FORMAT_R64G64B64_UINT,
+			VK_FORMAT_R64G64B64_SINT,
+			VK_FORMAT_R64G64B64_SFLOAT,
+		};
+
+	// 256 - bit, Block size 32 bytes, 1 texel / block
+	case VK_FORMAT_R64G64B64A64_UINT:
+		return {
+			VK_FORMAT_R64G64B64A64_UINT,
+			VK_FORMAT_R64G64B64A64_SINT,
+			VK_FORMAT_R64G64B64A64_SFLOAT,
+		};
+
+	// BC1_RGB(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC1_RGB_UNORM_BLOCK,
+			VK_FORMAT_BC1_RGB_SRGB_BLOCK,
+		};
+
+	// BC1_RGBA(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC1_RGBA_UNORM_BLOCK,
+			VK_FORMAT_BC1_RGBA_SRGB_BLOCK,
+		};
+
+	// BC2(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_BC2_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC2_UNORM_BLOCK,
+			VK_FORMAT_BC2_SRGB_BLOCK,
+		};
+
+	// BC3(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_BC3_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC3_UNORM_BLOCK,
+			VK_FORMAT_BC3_SRGB_BLOCK,
+		};
+
+	// BC4(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_BC4_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC4_UNORM_BLOCK,
+			VK_FORMAT_BC4_SNORM_BLOCK,
+		};
+
+	// BC5(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_BC5_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC5_UNORM_BLOCK,
+			VK_FORMAT_BC5_SNORM_BLOCK,
+		};
+
+	// BC6H(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		return {
+			VK_FORMAT_BC6H_UFLOAT_BLOCK,
+			VK_FORMAT_BC6H_SFLOAT_BLOCK,
+		};
+
+	// BC7(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_BC7_UNORM_BLOCK:
+		return {
+			VK_FORMAT_BC7_UNORM_BLOCK,
+			VK_FORMAT_BC7_SRGB_BLOCK,
+		};
+
+	// ETC2_RGB(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK,
+			VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK,
+		};
+
+	// ETC2_RGBA(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK,
+			VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK,
+		};
+
+	// ETC2_EAC_RGBA(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK,
+			VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK,
+		};
+
+	// EAC_R(64 bit), Block size 8 bytes, 16 texels / block
+	case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+		return {
+			VK_FORMAT_EAC_R11_UNORM_BLOCK,
+			VK_FORMAT_EAC_R11_SNORM_BLOCK,
+		};
+
+	// EAC_RG(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+		return {
+			VK_FORMAT_EAC_R11G11_UNORM_BLOCK,
+			VK_FORMAT_EAC_R11G11_SNORM_BLOCK,
+		};
+
+	// ASTC_4x4(128 bit), Block size 16 bytes, 16 texels / block
+	case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_4x4_UNORM_BLOCK,
+			VK_FORMAT_ASTC_4x4_SRGB_BLOCK,
+		};
+
+	// ASTC_5x4(128 bit), Block size 16 bytes, 20 texels / block
+	case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_5x4_UNORM_BLOCK,
+			VK_FORMAT_ASTC_5x4_SRGB_BLOCK,
+		};
+
+	// ASTC_5x5(128 bit), Block size 16 bytes, 25 texels / block
+	case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_5x5_UNORM_BLOCK,
+			VK_FORMAT_ASTC_5x5_SRGB_BLOCK,
+		};
+
+	// ASTC_6x5(128 bit), Block size 16 bytes, 30 texels / block
+	case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_6x5_UNORM_BLOCK,
+			VK_FORMAT_ASTC_6x5_SRGB_BLOCK,
+		};
+
+	// ASTC_6x6(128 bit), Block size 16 bytes, 36 texels / block
+	case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_6x6_UNORM_BLOCK,
+			VK_FORMAT_ASTC_6x6_SRGB_BLOCK,
+		};
+
+	// ASTC_8x5(128 bit), Block size 16 bytes, 40 texels / block
+	case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_8x5_UNORM_BLOCK,
+			VK_FORMAT_ASTC_8x5_SRGB_BLOCK,
+		};
+
+	// ASTC_8x6(128 bit), Block size 16 bytes, 48 texels / block
+	case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_8x6_UNORM_BLOCK,
+			VK_FORMAT_ASTC_8x6_SRGB_BLOCK,
+		};
+
+	// ASTC_8x8(128 bit), Block size 16 bytes, 64 texels / block
+	case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_8x8_UNORM_BLOCK,
+			VK_FORMAT_ASTC_8x8_SRGB_BLOCK,
+		};
+
+	// ASTC_10x5(128 bit), Block size 16 bytes, 50 texels / block
+	case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_10x5_UNORM_BLOCK,
+			VK_FORMAT_ASTC_10x5_SRGB_BLOCK,
+		};
+
+	// ASTC_10x6(128 bit), Block size 16 bytes, 60 texels / block
+	case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_10x6_UNORM_BLOCK,
+			VK_FORMAT_ASTC_10x6_SRGB_BLOCK,
+		};
+
+	// ASTC_10x8(128 bit), Block size 16 bytes, 80 texels / block
+	case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_10x8_UNORM_BLOCK,
+			VK_FORMAT_ASTC_10x8_SRGB_BLOCK,
+		};
+
+	// ASTC_10x10(128 bit), Block size 16 bytes, 100 texels / block
+	case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_10x10_UNORM_BLOCK,
+			VK_FORMAT_ASTC_10x10_SRGB_BLOCK,
+		};
+
+	// ASTC_12x10(128 bit), Block size 16 bytes, 120 texels / block
+	case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_12x10_UNORM_BLOCK,
+			VK_FORMAT_ASTC_12x10_SRGB_BLOCK,
+		};
+
+	// ASTC_12x12(128 bit), Block size 16 bytes, 144 texels / block
+	case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+		return {
+			VK_FORMAT_ASTC_12x12_UNORM_BLOCK,
+			VK_FORMAT_ASTC_12x12_SRGB_BLOCK,
+		};
+
+	// All other formats are only compatible with themselves
+	default:
+		ASSERT(getCompatibilityClassRepresentative() == format);
+		return { format };
+	}
+}
+
+// Returns a single format per class of compatible formats.
+VkFormat Format::getCompatibilityClassRepresentative() const
 {
 	// According to the Vulkan 1.1 Spec, 37.1.6. Format Compatibility Classes:
 	// "Uncompressed color formats are compatible with each other if they occupy
@@ -693,7 +1054,6 @@
 	//  with itself. In the following table, all the formats in the same row are
 	//  compatible."
 
-	// Return a single format per group of compatible formats, for quick comparison
 	switch(format)
 	{
 	// 8 - bit, Block size 1 byte, 1 texel / block
@@ -979,9 +1339,9 @@
 	}
 }
 
-bool Format::isCompatible(const Format &other) const
+bool Format::isCompatible(Format other) const
 {
-	return compatibleFormat() == other.compatibleFormat();
+	return getCompatibilityClassRepresentative() == other.getCompatibilityClassRepresentative();
 }
 
 int Format::blockWidth() const
diff --git a/src/Vulkan/VkFormat.hpp b/src/Vulkan/VkFormat.hpp
index dcfe40e..3cff4a0 100644
--- a/src/Vulkan/VkFormat.hpp
+++ b/src/Vulkan/VkFormat.hpp
@@ -16,9 +16,10 @@
 #define VK_FORMAT_HPP_
 
 #include "System/Types.hpp"
-
 #include "Vulkan/VulkanPlatform.hpp"
 
+#include <vector>
+
 namespace vk {
 
 class Format
@@ -46,7 +47,9 @@
 	bool isFloatFormat() const;
 	bool isYcbcrFormat() const;
 
-	bool isCompatible(const Format &other) const;
+	bool isCompatible(Format other) const;
+	std::vector<Format> getCompatibleFormats() const;
+
 	bool isCompressed() const;
 	VkFormat getDecompressedFormat() const;
 	int blockWidth() const;
@@ -77,7 +80,7 @@
 	static VkFormat mapFrom8bit(uint8_t format);
 
 private:
-	VkFormat compatibleFormat() const;
+	VkFormat getCompatibilityClassRepresentative() const;
 	size_t sliceBUnpadded(int width, int height, int border) const;
 
 	VkFormat format = VK_FORMAT_UNDEFINED;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index 09ba482..198f47b 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -3831,15 +3831,27 @@
 		extensionProperties = extensionProperties->pNext;
 	}
 
-	VkFormat format = pImageFormatInfo->format;
+	vk::Format format = pImageFormatInfo->format;
 	VkImageType type = pImageFormatInfo->type;
 	VkImageTiling tiling = pImageFormatInfo->tiling;
 	VkImageUsageFlags usage = pImageFormatInfo->usage;
 	VkImageCreateFlags flags = pImageFormatInfo->flags;
 
-	VkFormatProperties properties;
+	VkFormatProperties properties = {};
 	vk::PhysicalDevice::GetFormatProperties(format, &properties);
 
+	if(flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT)
+	{
+		for(vk::Format f : format.getCompatibleFormats())
+		{
+			VkFormatProperties extendedProperties = {};
+			vk::PhysicalDevice::GetFormatProperties(f, &extendedProperties);
+			properties.linearTilingFeatures |= extendedProperties.linearTilingFeatures;
+			properties.optimalTilingFeatures |= extendedProperties.optimalTilingFeatures;
+			properties.bufferFeatures |= extendedProperties.bufferFeatures;
+		}
+	}
+
 	VkFormatFeatureFlags features;
 	switch(tiling)
 	{