Allow treating a 3D image as a layered 2D image

When an image is created as a 3D image, but contains the
VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT flag, it becomes
equivalent to a 2D array with "depth" layers and should
be treated as such when accessing a single slice of a 3D
image.

Bug b/119620767

Change-Id: Ibdc6985acdf5e5f30efcb7bf5adc2563bf64c4f7
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/29189
Tested-by: Alexis Hétu <sugoi@google.com>
Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Device/Blitter.cpp b/src/Device/Blitter.cpp
index ea0bc96..1b49c92 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -192,6 +192,10 @@
 				area.extent.width = extent.width;
 				area.extent.height = extent.height;
 			}
+			if(dest->is3DSlice())
+			{
+				extent.depth = 1; // The 3D image is instead interpreted as a 2D image with layers
+			}
 
 			for(subresLayers.baseArrayLayer = subresourceRange.baseArrayLayer; subresLayers.baseArrayLayer <= lastLayer; subresLayers.baseArrayLayer++)
 			{
@@ -209,6 +213,7 @@
 						case 2:
 							for(uint32_t i = 0; i < area.extent.height; i++)
 							{
+								ASSERT(d < dest->end());
 								sw::clear((uint16_t*)d, packed, area.extent.width);
 								d += rowPitchBytes;
 							}
@@ -216,6 +221,7 @@
 						case 4:
 							for(uint32_t i = 0; i < area.extent.height; i++)
 							{
+								ASSERT(d < dest->end());
 								sw::clear((uint32_t*)d, packed, area.extent.width);
 								d += rowPitchBytes;
 							}
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 5498bb7..a2784e6 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -580,7 +580,7 @@
 
 VkDeviceSize Image::getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const
 {
-	return layer * getLayerSize(aspect) + getMemoryOffset(aspect, mipLevel);
+	return layer * getLayerOffset(aspect, mipLevel) + getMemoryOffset(aspect, mipLevel);
 }
 
 VkDeviceSize Image::getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const
@@ -593,6 +593,28 @@
 	return getMipLevelSize(aspect, mipLevel) * samples;
 }
 
+bool Image::is3DSlice() const
+{
+	return ((imageType == VK_IMAGE_TYPE_3D) && (flags & VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT));
+}
+
+VkDeviceSize Image::getLayerOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const
+{
+	if(is3DSlice())
+	{
+		// When the VkImageSubresourceRange structure is used to select a subset of the slices of a 3D
+		// image’s mip level in order to create a 2D or 2D array image view of a 3D image created with
+		// VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT, baseArrayLayer and layerCount specify the first
+		// slice index and the number of slices to include in the created image view.
+		ASSERT(samples == VK_SAMPLE_COUNT_1_BIT);
+
+		// Offset to the proper slice of the 3D image's mip level
+		return slicePitchBytes(aspect, mipLevel);
+	}
+
+	return getLayerSize(aspect);
+}
+
 VkDeviceSize Image::getLayerSize(VkImageAspectFlagBits aspect) const
 {
 	VkDeviceSize layerSize = 0;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 0ac2c9a..eff44b0 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -66,6 +66,7 @@
 	int                      slicePitchBytes(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	void*                    getTexelPointer(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;
 	bool                     isCube() const;
+	bool                     is3DSlice() const;
 	uint8_t*                 end() const;
 	VkDeviceSize             getLayerSize(VkImageAspectFlagBits aspect) const;
 
@@ -76,6 +77,7 @@
 	VkDeviceSize getStorageSize(VkImageAspectFlags flags) const;
 	VkDeviceSize getMipLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMultiSampledLevelSize(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
+	VkDeviceSize getLayerOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel) const;
 	VkDeviceSize getMemoryOffset(VkImageAspectFlagBits aspect, uint32_t mipLevel, uint32_t layer) const;
 	VkDeviceSize texelOffsetBytesInStorage(const VkOffset3D& offset, const VkImageSubresourceLayers& subresource) const;