Implement VK_EXT_host_image_copy

Tests: *
Bug: angleproject:8341
Change-Id: I201ace1d3d6016beba53199acf0814920d347193
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/72408
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Commit-Queue: Shahbaz Youssefi <syoussefi@google.com>
Tested-by: Shahbaz Youssefi <syoussefi@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
diff --git a/src/Vulkan/VkGetProcAddress.cpp b/src/Vulkan/VkGetProcAddress.cpp
index 6f6f276..d61f600 100644
--- a/src/Vulkan/VkGetProcAddress.cpp
+++ b/src/Vulkan/VkGetProcAddress.cpp
@@ -606,6 +606,16 @@
 	        MAKE_VULKAN_DEVICE_ENTRY(vkGetPrivateDataEXT),
 	        MAKE_VULKAN_DEVICE_ENTRY(vkSetPrivateDataEXT),
 	    } },
+	// VK_EXT_host_image_copy
+	{
+	    VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME,
+	    {
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCopyImageToImageEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCopyImageToMemoryEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkCopyMemoryToImageEXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkGetImageSubresourceLayout2EXT),
+	        MAKE_VULKAN_DEVICE_ENTRY(vkTransitionImageLayoutEXT),
+	    } },
 #if SWIFTSHADER_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER
 	// VK_ANDROID_external_memory_android_hardware_buffer
 	{
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index b4422fc..cc17cf7 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -162,6 +162,11 @@
 		case VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO:
 		case VK_STRUCTURE_TYPE_IMAGE_STENCIL_USAGE_CREATE_INFO:
 			break;
+		case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT:
+			{
+				// Explicitly ignored, since VK_EXT_image_drm_format_modifier is not supported
+			}
+			break;
 		case VK_STRUCTURE_TYPE_MAX_ENUM:
 			// dEQP tests that this value is ignored.
 			break;
@@ -581,9 +586,19 @@
 	dstImage->contentsChanged(ImageSubresourceRange(region.dstSubresource));
 }
 
-void Image::copy(Buffer *buffer, const VkBufferImageCopy2KHR &region, bool bufferIsSource)
+void Image::copy(const void *srcCopyMemory,
+                 void *dstCopyMemory,
+                 uint32_t rowLength,
+                 uint32_t imageHeight,
+                 const VkImageSubresourceLayers &imageSubresource,
+                 const VkOffset3D &imageCopyOffset,
+                 const VkExtent3D &imageCopyExtent)
 {
-	switch(region.imageSubresource.aspectMask)
+	// Decide on whether copying from buffer/memory or to buffer/memory
+	ASSERT((srcCopyMemory == nullptr) != (dstCopyMemory == nullptr));
+	const bool memoryIsSource = srcCopyMemory != nullptr;
+
+	switch(imageSubresource.aspectMask)
 	{
 	case VK_IMAGE_ASPECT_COLOR_BIT:
 	case VK_IMAGE_ASPECT_DEPTH_BIT:
@@ -593,56 +608,54 @@
 	case VK_IMAGE_ASPECT_PLANE_2_BIT:
 		break;
 	default:
-		UNSUPPORTED("aspectMask %x", int(region.imageSubresource.aspectMask));
+		UNSUPPORTED("aspectMask %x", int(imageSubresource.aspectMask));
 		break;
 	}
 
-	auto aspect = static_cast<VkImageAspectFlagBits>(region.imageSubresource.aspectMask);
+	auto aspect = static_cast<VkImageAspectFlagBits>(imageSubresource.aspectMask);
 	Format copyFormat = getFormat(aspect);
 
-	VkExtent3D imageExtent = imageExtentInBlocks(region.imageExtent, aspect);
+	VkExtent3D imageExtent = imageExtentInBlocks(imageCopyExtent, aspect);
 
 	if(imageExtent.width == 0 || imageExtent.height == 0 || imageExtent.depth == 0)
 	{
 		return;
 	}
 
-	VkExtent2D bufferExtent = bufferExtentInBlocks(Extent2D(imageExtent), region);
+	VkExtent2D extent = bufferExtentInBlocks(Extent2D(imageExtent), rowLength, imageHeight, imageSubresource, imageCopyOffset);
 	int bytesPerBlock = copyFormat.bytesPerBlock();
-	int bufferRowPitchBytes = bufferExtent.width * bytesPerBlock;
-	int bufferSlicePitchBytes = bufferExtent.height * bufferRowPitchBytes;
+	int memoryRowPitchBytes = extent.width * bytesPerBlock;
+	int memorySlicePitchBytes = extent.height * memoryRowPitchBytes;
 	ASSERT(samples == 1);
 
-	uint8_t *bufferMemory = static_cast<uint8_t *>(buffer->getOffsetPointer(region.bufferOffset));
-	uint8_t *imageMemory = static_cast<uint8_t *>(getTexelPointer(region.imageOffset, ImageSubresource(region.imageSubresource)));
-	uint8_t *srcMemory = bufferIsSource ? bufferMemory : imageMemory;
-	uint8_t *dstMemory = bufferIsSource ? imageMemory : bufferMemory;
-	int imageRowPitchBytes = rowPitchBytes(aspect, region.imageSubresource.mipLevel);
-	int imageSlicePitchBytes = slicePitchBytes(aspect, region.imageSubresource.mipLevel);
+	uint8_t *imageMemory = static_cast<uint8_t *>(getTexelPointer(imageCopyOffset, ImageSubresource(imageSubresource)));
+	const uint8_t *srcMemory = memoryIsSource ? static_cast<const uint8_t *>(srcCopyMemory) : imageMemory;
+	uint8_t *dstMemory = memoryIsSource ? imageMemory : static_cast<uint8_t *>(dstCopyMemory);
+	int imageRowPitchBytes = rowPitchBytes(aspect, imageSubresource.mipLevel);
+	int imageSlicePitchBytes = slicePitchBytes(aspect, imageSubresource.mipLevel);
 
-	int srcSlicePitchBytes = bufferIsSource ? bufferSlicePitchBytes : imageSlicePitchBytes;
-	int dstSlicePitchBytes = bufferIsSource ? imageSlicePitchBytes : bufferSlicePitchBytes;
-	int srcRowPitchBytes = bufferIsSource ? bufferRowPitchBytes : imageRowPitchBytes;
-	int dstRowPitchBytes = bufferIsSource ? imageRowPitchBytes : bufferRowPitchBytes;
+	int srcSlicePitchBytes = memoryIsSource ? memorySlicePitchBytes : imageSlicePitchBytes;
+	int dstSlicePitchBytes = memoryIsSource ? imageSlicePitchBytes : memorySlicePitchBytes;
+	int srcRowPitchBytes = memoryIsSource ? memoryRowPitchBytes : imageRowPitchBytes;
+	int dstRowPitchBytes = memoryIsSource ? imageRowPitchBytes : memoryRowPitchBytes;
 
 	VkDeviceSize copySize = imageExtent.width * bytesPerBlock;
 
 	VkDeviceSize imageLayerSize = getLayerSize(aspect);
-	VkDeviceSize srcLayerSize = bufferIsSource ? bufferSlicePitchBytes : imageLayerSize;
-	VkDeviceSize dstLayerSize = bufferIsSource ? imageLayerSize : bufferSlicePitchBytes;
+	VkDeviceSize srcLayerSize = memoryIsSource ? memorySlicePitchBytes : imageLayerSize;
+	VkDeviceSize dstLayerSize = memoryIsSource ? imageLayerSize : memorySlicePitchBytes;
 
-	for(uint32_t i = 0; i < region.imageSubresource.layerCount; i++)
+	for(uint32_t i = 0; i < imageSubresource.layerCount; i++)
 	{
-		uint8_t *srcLayerMemory = srcMemory;
+		const uint8_t *srcLayerMemory = srcMemory;
 		uint8_t *dstLayerMemory = dstMemory;
 		for(uint32_t z = 0; z < imageExtent.depth; z++)
 		{
-			uint8_t *srcSliceMemory = srcLayerMemory;
+			const uint8_t *srcSliceMemory = srcLayerMemory;
 			uint8_t *dstSliceMemory = dstLayerMemory;
 			for(uint32_t y = 0; y < imageExtent.height; y++)
 			{
-				ASSERT(((bufferIsSource ? dstSliceMemory : srcSliceMemory) + copySize) < end());
-				ASSERT(((bufferIsSource ? srcSliceMemory : dstSliceMemory) + copySize) < buffer->end());
+				ASSERT(((memoryIsSource ? dstSliceMemory : srcSliceMemory) + copySize) < end());
 				memcpy(dstSliceMemory, srcSliceMemory, copySize);
 				srcSliceMemory += srcRowPitchBytes;
 				dstSliceMemory += dstRowPitchBytes;
@@ -655,20 +668,30 @@
 		dstMemory += dstLayerSize;
 	}
 
-	if(bufferIsSource)
+	if(memoryIsSource)
 	{
-		contentsChanged(ImageSubresourceRange(region.imageSubresource));
+		contentsChanged(ImageSubresourceRange(imageSubresource));
 	}
 }
 
 void Image::copyTo(Buffer *dstBuffer, const VkBufferImageCopy2KHR &region)
 {
-	copy(dstBuffer, region, false);
+	copy(nullptr, dstBuffer->getOffsetPointer(region.bufferOffset), region.bufferRowLength, region.bufferImageHeight, region.imageSubresource, region.imageOffset, region.imageExtent);
 }
 
 void Image::copyFrom(Buffer *srcBuffer, const VkBufferImageCopy2KHR &region)
 {
-	copy(srcBuffer, region, true);
+	copy(srcBuffer->getOffsetPointer(region.bufferOffset), nullptr, region.bufferRowLength, region.bufferImageHeight, region.imageSubresource, region.imageOffset, region.imageExtent);
+}
+
+void Image::copyToMemory(const VkImageToMemoryCopyEXT &region)
+{
+	copy(nullptr, region.pHostPointer, region.memoryRowLength, region.memoryImageHeight, region.imageSubresource, region.imageOffset, region.imageExtent);
+}
+
+void Image::copyFromMemory(const VkMemoryToImageCopyEXT &region)
+{
+	copy(region.pHostPointer, nullptr, region.memoryRowLength, region.memoryImageHeight, region.imageSubresource, region.imageOffset, region.imageExtent);
 }
 
 void *Image::getTexelPointer(const VkOffset3D &offset, const VkImageSubresource &subresource) const
@@ -714,33 +737,33 @@
 	return adjustedOffset;
 }
 
-VkExtent2D Image::bufferExtentInBlocks(const VkExtent2D &extent, const VkBufferImageCopy2KHR &region) const
+VkExtent2D Image::bufferExtentInBlocks(const VkExtent2D &extent, uint32_t rowLength, uint32_t imageHeight, const VkImageSubresourceLayers &imageSubresource, const VkOffset3D &imageOffset) const
 {
 	VkExtent2D adjustedExtent = extent;
-	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(region.imageSubresource.aspectMask);
+	VkImageAspectFlagBits aspect = static_cast<VkImageAspectFlagBits>(imageSubresource.aspectMask);
 	Format usedFormat = getFormat(aspect);
 
-	if(region.bufferRowLength != 0)
+	if(rowLength != 0)
 	{
-		adjustedExtent.width = region.bufferRowLength;
+		adjustedExtent.width = rowLength;
 
 		if(usedFormat.isCompressed())
 		{
 			int blockWidth = usedFormat.blockWidth();
-			ASSERT((adjustedExtent.width % blockWidth == 0) || (adjustedExtent.width + region.imageOffset.x == extent.width));
-			adjustedExtent.width = (region.bufferRowLength + blockWidth - 1) / blockWidth;
+			ASSERT((adjustedExtent.width % blockWidth == 0) || (adjustedExtent.width + imageOffset.x == extent.width));
+			adjustedExtent.width = (rowLength + blockWidth - 1) / blockWidth;
 		}
 	}
 
-	if(region.bufferImageHeight != 0)
+	if(imageHeight != 0)
 	{
-		adjustedExtent.height = region.bufferImageHeight;
+		adjustedExtent.height = imageHeight;
 
 		if(usedFormat.isCompressed())
 		{
 			int blockHeight = usedFormat.blockHeight();
-			ASSERT((adjustedExtent.height % blockHeight == 0) || (adjustedExtent.height + region.imageOffset.y == extent.height));
-			adjustedExtent.height = (region.bufferImageHeight + blockHeight - 1) / blockHeight;
+			ASSERT((adjustedExtent.height % blockHeight == 0) || (adjustedExtent.height + imageOffset.y == extent.height));
+			adjustedExtent.height = (imageHeight + blockHeight - 1) / blockHeight;
 		}
 	}
 
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 839c1c4..d8a247e 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -63,6 +63,10 @@
 	void copyTo(Buffer *dstBuffer, const VkBufferImageCopy2KHR &region);
 	void copyFrom(Buffer *srcBuffer, const VkBufferImageCopy2KHR &region);
 
+	// VK_EXT_host_image_copy variants of copy
+	void copyToMemory(const VkImageToMemoryCopyEXT &region);
+	void copyFromMemory(const VkMemoryToImageCopyEXT &region);
+
 	void blitTo(Image *dstImage, const VkImageBlit2KHR &region, VkFilter filter) const;
 	void copyTo(uint8_t *dst, unsigned int dstPitch) const;
 	void resolveTo(Image *dstImage, const VkImageResolve2KHR &region) const;
@@ -119,7 +123,13 @@
 	DeviceMemory *deviceMemory = nullptr;
 
 private:
-	void copy(Buffer *buffer, const VkBufferImageCopy2KHR &region, bool bufferIsSource);
+	void copy(const void *srcCopyMemory,
+		void *dstCopyMemory,
+		uint32_t rowLength,
+		uint32_t imageHeight,
+		const VkImageSubresourceLayers    &imageSubresource,
+		const VkOffset3D                  &imageCopyOffset,
+		const VkExtent3D                  &imageCopyExtent);
 	void copySingleAspectTo(Image *dstImage, const VkImageCopy2KHR &region) const;
 	VkDeviceSize getStorageSize(VkImageAspectFlags flags) const;
 	VkDeviceSize getMultiSampledLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
@@ -130,7 +140,7 @@
 	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D &offset, const VkImageSubresource &subresource) const;
 	VkExtent3D imageExtentInBlocks(const VkExtent3D &extent, VkImageAspectFlagBits aspect) const;
 	VkOffset3D imageOffsetInBlocks(const VkOffset3D &offset, VkImageAspectFlagBits aspect) const;
-	VkExtent2D bufferExtentInBlocks(const VkExtent2D &extent, const VkBufferImageCopy2KHR &region) const;
+	VkExtent2D bufferExtentInBlocks(const VkExtent2D &extent, uint32_t rowLength, uint32_t imageHeight, const VkImageSubresourceLayers &imageSubresource, const VkOffset3D &imageOffset) const;
 	void clear(const void *pixelData, VkFormat pixelFormat, const vk::Format &viewFormat, const VkImageSubresourceRange &subresourceRange, const VkRect2D *renderArea);
 	int borderSize() const;
 
diff --git a/src/Vulkan/VkPhysicalDevice.cpp b/src/Vulkan/VkPhysicalDevice.cpp
index 049ec97..3866d91 100644
--- a/src/Vulkan/VkPhysicalDevice.cpp
+++ b/src/Vulkan/VkPhysicalDevice.cpp
@@ -401,6 +401,12 @@
 }
 
 template<typename T>
+static void getPhysicalDeviceHostImageCopyFeatures(T *features)
+{
+	features->hostImageCopy = VK_TRUE;
+}
+
+template<typename T>
 static void getPhysicalDeviceVulkan12Features(T *features)
 {
 	features->samplerMirrorClampToEdge = VK_TRUE;
@@ -643,6 +649,9 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT:
 			getPhysicalDeviceSwapchainMaintenance1FeaturesKHR(reinterpret_cast<struct VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT *>(curExtension));
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT:
+			getPhysicalDeviceHostImageCopyFeatures(reinterpret_cast<struct VkPhysicalDeviceHostImageCopyFeaturesEXT *>(curExtension));
+			break;
 		case VK_STRUCTURE_TYPE_MAX_ENUM:  // TODO(b/176893525): This may not be legal. dEQP tests that this value is ignored.
 			break;
 		default:
@@ -1403,6 +1412,60 @@
 	getPipelineRobustnessProperties(properties);
 }
 
+template<typename T>
+static void getHostImageCopyProperties(T *properties)
+{
+	// There are no image layouts in SwiftShader, so support all layouts for host image copy
+	constexpr VkImageLayout kAllLayouts[] = {
+		VK_IMAGE_LAYOUT_GENERAL,
+		VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+		VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_DEPTH_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_STENCIL_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_STENCIL_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_READ_ONLY_OPTIMAL,
+		VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL,
+		VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
+		VK_IMAGE_LAYOUT_SHARED_PRESENT_KHR,
+	};
+	constexpr uint32_t kAllLayoutsCount = std::size(kAllLayouts);
+
+	if(properties->pCopySrcLayouts == nullptr)
+	{
+		properties->copySrcLayoutCount = kAllLayoutsCount;
+	}
+	else
+	{
+		properties->copySrcLayoutCount = std::min(properties->copySrcLayoutCount, kAllLayoutsCount);
+		memcpy(properties->pCopySrcLayouts, kAllLayouts, properties->copySrcLayoutCount * sizeof(*properties->pCopySrcLayouts));
+	}
+
+	if(properties->pCopyDstLayouts == nullptr)
+	{
+		properties->copyDstLayoutCount = kAllLayoutsCount;
+	}
+	else
+	{
+		properties->copyDstLayoutCount = std::min(properties->copyDstLayoutCount, kAllLayoutsCount);
+		memcpy(properties->pCopyDstLayouts, kAllLayouts, properties->copyDstLayoutCount * sizeof(*properties->pCopyDstLayouts));
+	}
+
+	memcpy(properties->optimalTilingLayoutUUID, SWIFTSHADER_UUID, VK_UUID_SIZE);
+	properties->identicalMemoryTypeRequirements = VK_TRUE;
+}
+
+void PhysicalDevice::getProperties(VkPhysicalDeviceHostImageCopyPropertiesEXT *properties) const
+{
+	getHostImageCopyProperties(properties);
+}
+
 void PhysicalDevice::getProperties(VkPhysicalDeviceVulkan12Properties *properties) const
 {
 	getDriverProperties(properties);
@@ -1672,6 +1735,13 @@
 	return CheckFeature(requested, supported, swapchainMaintenance1);
 }
 
+bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceHostImageCopyFeaturesEXT *requested) const
+{
+	auto supported = getSupportedFeatures(requested);
+
+	return CheckFeature(requested, supported, hostImageCopy);
+}
+
 bool PhysicalDevice::hasExtendedFeatures(const VkPhysicalDeviceDescriptorIndexingFeatures *requested) const
 {
 	auto supported = getSupportedFeatures(requested);
@@ -1729,7 +1799,7 @@
 }
 #undef CheckFeature
 
-static bool checkFormatUsage(VkImageUsageFlags usage, VkFormatFeatureFlags features)
+static bool checkFormatUsage(VkImageUsageFlags usage, VkFormatFeatureFlags2KHR features)
 {
 	// Check for usage conflict with features
 	if((usage & VK_IMAGE_USAGE_SAMPLED_BIT) && !(features & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT))
@@ -1767,13 +1837,18 @@
 		return false;
 	}
 
+	if((usage & VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT) && !(features & VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT))
+	{
+		return false;
+	}
+
 	return true;
 }
 
 bool vk::PhysicalDevice::isFormatSupported(vk::Format format, VkImageType type, VkImageTiling tiling,
                                            VkImageUsageFlags usage, VkImageUsageFlags stencilUsage, VkImageCreateFlags flags)
 {
-	VkFormatProperties properties = {};
+	VkFormatProperties3 properties = {};
 	vk::PhysicalDevice::GetFormatProperties(format, &properties);
 
 	if(flags & VK_IMAGE_CREATE_EXTENDED_USAGE_BIT)
@@ -1788,7 +1863,7 @@
 		}
 	}
 
-	VkFormatFeatureFlags features;
+	VkFormatFeatureFlags2KHR features;
 	switch(tiling)
 	{
 	case VK_IMAGE_TILING_LINEAR:
@@ -1828,7 +1903,8 @@
 	                              VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT |
 	                              VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
 	                              VK_IMAGE_USAGE_TRANSFER_DST_BIT |
-	                              VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
+	                              VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
+	                              VK_IMAGE_USAGE_HOST_TRANSFER_BIT_EXT;
 	ASSERT(!(usage & ~(allRecognizedUsageBits)));
 
 	if(usage & VK_IMAGE_USAGE_SAMPLED_BIT)
@@ -2029,7 +2105,8 @@
 		    VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT |
 		    VK_FORMAT_FEATURE_BLIT_SRC_BIT |
 		    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
-		    VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
+		    VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
+		    VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT;
 		break;
 
 	// YCbCr formats:
@@ -2041,7 +2118,8 @@
 		    VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT |
 		    VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
 		    VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
-		    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT;
+		    VK_FORMAT_FEATURE_COSITED_CHROMA_SAMPLES_BIT |
+		    VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT;
 		break;
 	default:
 		break;
@@ -2329,9 +2407,16 @@
 	{
 		// "Formats that are required to support VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT must also support
 		//  VK_FORMAT_FEATURE_TRANSFER_SRC_BIT and VK_FORMAT_FEATURE_TRANSFER_DST_BIT."
+		//
+		//  Additionally:
+		//  "If VK_EXT_host_image_copy is supported and VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT is supported
+		//  in optimalTilingFeatures or linearTilingFeatures for a color format,
+		//  VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT must also be supported in optimalTilingFeatures
+		//  or linearTilingFeatures respectively."
 
 		pFormatProperties->linearTilingFeatures |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT |
-		                                           VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
+		                                           VK_FORMAT_FEATURE_TRANSFER_DST_BIT |
+		                                           VK_FORMAT_FEATURE_2_HOST_IMAGE_TRANSFER_BIT_EXT;
 
 		if(!format.isCompressed())
 		{
diff --git a/src/Vulkan/VkPhysicalDevice.hpp b/src/Vulkan/VkPhysicalDevice.hpp
index 11e3d47..60454e3 100644
--- a/src/Vulkan/VkPhysicalDevice.hpp
+++ b/src/Vulkan/VkPhysicalDevice.hpp
@@ -62,6 +62,7 @@
 	bool hasExtendedFeatures(const VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT *requested) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceGlobalPriorityQueryFeaturesKHR *requested) const;
 	bool hasExtendedFeatures(const VkPhysicalDeviceSwapchainMaintenance1FeaturesEXT *requested) const;
+	bool hasExtendedFeatures(const VkPhysicalDeviceHostImageCopyFeaturesEXT *requested) const;
 
 	const VkPhysicalDeviceProperties &getProperties() const;
 	void getProperties(VkPhysicalDeviceIDProperties *properties) const;
@@ -98,6 +99,7 @@
 	void getProperties(VkPhysicalDeviceShaderIntegerDotProductProperties *properties) const;
 	void getProperties(VkPhysicalDevicePipelineRobustnessPropertiesEXT *properties) const;
 	void getProperties(VkPhysicalDeviceGraphicsPipelineLibraryPropertiesEXT *properties) const;
+	void getProperties(VkPhysicalDeviceHostImageCopyPropertiesEXT *properties) const;
 	void getProperties(VkPhysicalDeviceVulkan11Properties *properties) const;
 	void getProperties(VkPhysicalDeviceVulkan12Properties *properties) const;
 	void getProperties(VkPhysicalDeviceVulkan13Properties *properties) const;
diff --git a/src/Vulkan/libVulkan.cpp b/src/Vulkan/libVulkan.cpp
index d27b46b..5360f95 100644
--- a/src/Vulkan/libVulkan.cpp
+++ b/src/Vulkan/libVulkan.cpp
@@ -436,7 +436,8 @@
 	// Used by ANGLE to implement triangle/etc list restarts as possible in OpenGL
 	{ { VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_EXTENSION_NAME, VK_EXT_PRIMITIVE_TOPOLOGY_LIST_RESTART_SPEC_VERSION } },
 	{ { VK_EXT_PIPELINE_ROBUSTNESS_EXTENSION_NAME, VK_EXT_PIPELINE_ROBUSTNESS_SPEC_VERSION } },
-	{ { VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION } }
+	{ { VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_EXTENSION_NAME, VK_EXT_RASTERIZATION_ORDER_ATTACHMENT_ACCESS_SPEC_VERSION } },
+	{ { VK_EXT_HOST_IMAGE_COPY_EXTENSION_NAME, VK_EXT_HOST_IMAGE_COPY_SPEC_VERSION } },
 };
 
 static uint32_t numSupportedExtensions(const ExtensionProperties *extensionProperties, uint32_t extensionPropertiesCount)
@@ -1125,6 +1126,7 @@
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_CONTROL_FEATURES_EXT:
 		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PIPELINE_ROBUSTNESS_FEATURES_EXT:
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_FEATURES_EXT:
 			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]"
@@ -2001,6 +2003,12 @@
 				(void)stencilUsageInfo->stencilUsage;
 			}
 			break;
+		case VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT:
+			{
+				// Explicitly ignored, since VK_EXT_image_drm_format_modifier is not supported
+				ASSERT(!hasDeviceExtension(VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME));
+			}
+			break;
 		case VK_STRUCTURE_TYPE_MAX_ENUM:
 			// dEQP tests that this value is ignored.
 			break;
@@ -2072,6 +2080,37 @@
 	vk::Cast(image)->getSubresourceLayout(pSubresource, pLayout);
 }
 
+VKAPI_ATTR void VKAPI_CALL vkGetImageSubresourceLayout2EXT(VkDevice device, VkImage image, const VkImageSubresource2KHR *pSubresource, VkSubresourceLayout2KHR *pLayout)
+{
+	TRACE("(VkDevice device = %p, VkImage image = %p, const VkImageSubresource2KHR* pSubresource = %p, VkSubresourceLayout2KHR* pLayout = %p)",
+	      device, static_cast<void *>(image), pSubresource, pLayout);
+
+	// If tiling is OPTIMAL, this doesn't need to be done, but it's harmless especially since
+	// LINEAR and OPTIMAL are the same.
+	vk::Cast(image)->getSubresourceLayout(&pSubresource->imageSubresource, &pLayout->subresourceLayout);
+
+	VkBaseOutStructure *extInfo = reinterpret_cast<VkBaseOutStructure *>(pLayout->pNext);
+	while(extInfo)
+	{
+		switch(extInfo->sType)
+		{
+		case VK_STRUCTURE_TYPE_SUBRESOURCE_HOST_MEMCPY_SIZE_EXT:
+			{
+				// Since the subresource layout is filled above already, get the size out of
+				// that.
+				VkSubresourceHostMemcpySizeEXT *hostMemcpySize = reinterpret_cast<VkSubresourceHostMemcpySizeEXT *>(extInfo);
+				hostMemcpySize->size = pLayout->subresourceLayout.size;
+				break;
+			}
+		default:
+			UNSUPPORTED("pLayout->pNext sType = %s", vk::Stringify(extInfo->sType).c_str());
+			break;
+		}
+
+		extInfo = extInfo->pNext;
+	}
+}
+
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateImageView(VkDevice device, const VkImageViewCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkImageView *pView)
 {
 	TRACE("(VkDevice device = %p, const VkImageViewCreateInfo* pCreateInfo = %p, const VkAllocationCallbacks* pAllocator = %p, VkImageView* pView = %p)",
@@ -3760,6 +3799,12 @@
 				vk::Cast(physicalDevice)->getProperties(properties);
 			}
 			break;
+		case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_IMAGE_COPY_PROPERTIES_EXT:
+			{
+				auto *properties = reinterpret_cast<VkPhysicalDeviceHostImageCopyPropertiesEXT *>(extensionProperties);
+				vk::Cast(physicalDevice)->getProperties(properties);
+			}
+			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]"
 			UNSUPPORTED("pProperties->pNext sType = %s", vk::Stringify(extensionProperties->sType).c_str());
@@ -3883,6 +3928,14 @@
 				ASSERT(!hasDeviceExtension(VK_AMD_TEXTURE_GATHER_BIAS_LOD_EXTENSION_NAME));
 			}
 			break;
+		case VK_STRUCTURE_TYPE_HOST_IMAGE_COPY_DEVICE_PERFORMANCE_QUERY_EXT:
+			{
+				auto *properties = reinterpret_cast<VkHostImageCopyDevicePerformanceQueryEXT *>(extensionProperties);
+				// Host image copy is equally performant on the host with SwiftShader; it's the same code running on the main thread.
+				properties->optimalDeviceAccess = VK_TRUE;
+				properties->identicalMemoryLayout = VK_TRUE;
+			}
+			break;
 #ifdef __ANDROID__
 		case VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_USAGE_ANDROID:
 			{
@@ -4329,6 +4382,67 @@
 	vk::Cast(instance)->submitDebugUtilsMessage(messageSeverity, messageTypes, pCallbackData);
 }
 
+VKAPI_ATTR VkResult VKAPI_CALL vkCopyMemoryToImageEXT(VkDevice device, const VkCopyMemoryToImageInfoEXT *pCopyMemoryToImageInfo)
+{
+	TRACE("(VkDevice device = %p, const VkCopyMemoryToImageInfoEXT* pCopyMemoryToImageInfo = %p)",
+	      device, pCopyMemoryToImageInfo);
+
+	constexpr auto allRecognizedFlagBits = VK_HOST_IMAGE_COPY_MEMCPY_EXT;
+	ASSERT(!(pCopyMemoryToImageInfo->flags & ~allRecognizedFlagBits));
+
+	vk::Image *dstImage = vk::Cast(pCopyMemoryToImageInfo->dstImage);
+	for(uint32_t i = 0; i < pCopyMemoryToImageInfo->regionCount; i++)
+	{
+		dstImage->copyFromMemory(pCopyMemoryToImageInfo->pRegions[i]);
+	}
+
+	return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCopyImageToMemoryEXT(VkDevice device, const VkCopyImageToMemoryInfoEXT *pCopyImageToMemoryInfo)
+{
+	TRACE("(VkDevice device = %p, const VkCopyImageToMemoryInfoEXT* pCopyImageToMemoryInfo = %p)",
+	      device, pCopyImageToMemoryInfo);
+
+	constexpr auto allRecognizedFlagBits = VK_HOST_IMAGE_COPY_MEMCPY_EXT;
+	ASSERT(!(pCopyImageToMemoryInfo->flags & ~allRecognizedFlagBits));
+
+	vk::Image *srcImage = vk::Cast(pCopyImageToMemoryInfo->srcImage);
+	for(uint32_t i = 0; i < pCopyImageToMemoryInfo->regionCount; i++)
+	{
+		srcImage->copyToMemory(pCopyImageToMemoryInfo->pRegions[i]);
+	}
+
+	return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkCopyImageToImageEXT(VkDevice device, const VkCopyImageToImageInfoEXT *pCopyImageToImageInfo)
+{
+	TRACE("(VkDevice device = %p, const VkCopyImageToImageInfoEXT* pCopyImageToImageInfo = %p)",
+	      device, pCopyImageToImageInfo);
+
+	constexpr auto allRecognizedFlagBits = VK_HOST_IMAGE_COPY_MEMCPY_EXT;
+	ASSERT(!(pCopyImageToImageInfo->flags & ~allRecognizedFlagBits));
+
+	vk::Image *srcImage = vk::Cast(pCopyImageToImageInfo->srcImage);
+	vk::Image *dstImage = vk::Cast(pCopyImageToImageInfo->dstImage);
+	for(uint32_t i = 0; i < pCopyImageToImageInfo->regionCount; i++)
+	{
+		srcImage->copyTo(dstImage, pCopyImageToImageInfo->pRegions[i]);
+	}
+
+	return VK_SUCCESS;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL vkTransitionImageLayoutEXT(VkDevice device, uint32_t transitionCount, const VkHostImageLayoutTransitionInfoEXT *pTransitions)
+{
+	TRACE("(VkDevice device = %p, uint32_t transitionCount = %u, const VkHostImageLayoutTransitionInfoEXT* pTransitions = %p)",
+	      device, transitionCount, pTransitions);
+
+	// This function is a no-op; there are no image layouts in SwiftShader.
+	return VK_SUCCESS;
+}
+
 #ifdef VK_USE_PLATFORM_XCB_KHR
 VKAPI_ATTR VkResult VKAPI_CALL vkCreateXcbSurfaceKHR(VkInstance instance, const VkXcbSurfaceCreateInfoKHR *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkSurfaceKHR *pSurface)
 {