Support regions in Buffer to/from Image copy

Added support for image regions and buffer rowLength/imageHeight
for Image to Buffer copies or Buffer to Image copies.

Passes all tests in:
dEQP-VK.api.copy_and_blit.core.image_to_buffer
dEQP-VK.api.copy_and_blit.core.buffer_to_image

Bug b/119620767

Change-Id: I2b2592c06d52609c3f9c51fa164a0335f58f361f
Reviewed-on: https://swiftshader-review.googlesource.com/c/23228
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 17fd908..16e3fd9 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -94,25 +94,31 @@
 
 	bool isSinglePlane = (pRegion.extent.depth == 1);
 	bool isSingleLine  = (pRegion.extent.height == 1) && isSinglePlane;
+	// In order to copy multiple lines using a single memcpy call, we
+	// have to make sure that we need to copy the entire line and that
+	// both source and destination lines have the same length in bytes
 	bool isEntireLine  = (pRegion.extent.width == extent.width) &&
 	                     (pRegion.extent.width == dst->extent.width) &&
 	                     (srcRowPitchBytes == dstRowPitchBytes);
+	// In order to copy multiple planes using a single memcpy call, we
+	// have to make sure that we need to copy the entire plane and that
+	// both source and destination planes have the same length in bytes
 	bool isEntirePlane = isEntireLine &&
 	                     (pRegion.extent.height == extent.height) &&
 	                     (pRegion.extent.height == dst->extent.height) &&
 	                     (srcSlicePitchBytes == dstSlicePitchBytes);
 
-	if(isSingleLine)
+	if(isSingleLine) // Copy one line
 	{
-		memcpy(dstMem, srcMem, pRegion.extent.width * srcBytesPerTexel); // Copy one line
+		memcpy(dstMem, srcMem, pRegion.extent.width * srcBytesPerTexel);
 	}
-	else if(isEntireLine && isSinglePlane)
+	else if(isEntireLine && isSinglePlane) // Copy one plane
 	{
-		memcpy(dstMem, srcMem, pRegion.extent.height * srcRowPitchBytes); // Copy one plane
+		memcpy(dstMem, srcMem, pRegion.extent.height * srcRowPitchBytes);
 	}
-	else if(isEntirePlane)
+	else if(isEntirePlane) // Copy multiple planes
 	{
-		memcpy(dstMem, srcMem, pRegion.extent.depth * srcSlicePitchBytes); // Copy multiple planes
+		memcpy(dstMem, srcMem, pRegion.extent.depth * srcSlicePitchBytes);
 	}
 	else if(isEntireLine) // Copy plane by plane
 	{
@@ -135,34 +141,94 @@
 
 void Image::copy(VkBuffer buffer, const VkBufferImageCopy& region, bool bufferIsSource)
 {
-	if((region.imageExtent.width != extent.width) ||
-	   (region.imageExtent.height != extent.height) ||
-	   (region.imageExtent.depth != extent.depth) ||
-	   !((region.imageSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
+	if(!((region.imageSubresource.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
 	     (region.imageSubresource.aspectMask == VK_IMAGE_ASPECT_DEPTH_BIT) ||
 	     (region.imageSubresource.aspectMask == VK_IMAGE_ASPECT_STENCIL_BIT)) ||
-	   (region.imageSubresource.mipLevel != 0) ||
-	   (region.imageOffset.x != 0) ||
-	   (region.imageOffset.y != 0) ||
-	   (region.imageOffset.z != 0) ||
-	   (region.bufferRowLength != extent.width) ||
-	   (region.bufferImageHeight != extent.height))
+	   (region.imageSubresource.mipLevel != 0))
 	{
 		UNIMPLEMENTED();
 	}
 
-	VkDeviceSize copySize = slicePitchBytes(region.imageSubresource.aspectMask) * region.imageExtent.depth;
+	int imageBytesPerTexel = bytesPerTexel(region.imageSubresource.aspectMask);
+	int imageRowPitchBytes = rowPitchBytes(region.imageSubresource.aspectMask);
+	int imageSlicePitchBytes = slicePitchBytes(region.imageSubresource.aspectMask);
+	int bufferRowPitchBytes = ((region.bufferRowLength == 0) ? region.imageExtent.width : region.bufferRowLength) *
+	                          imageBytesPerTexel;
+	int bufferSlicePitchBytes = (((region.bufferImageHeight == 0) || (region.bufferRowLength == 0)) ?
+                                region.imageExtent.height : (region.bufferImageHeight * region.bufferRowLength)) *
+	                            imageBytesPerTexel;
+
+	int srcSlicePitchBytes = bufferIsSource ? bufferSlicePitchBytes : imageSlicePitchBytes;
+	int dstSlicePitchBytes = bufferIsSource ? imageSlicePitchBytes : bufferSlicePitchBytes;
+	int srcRowPitchBytes = bufferIsSource ? bufferRowPitchBytes : imageRowPitchBytes;
+	int dstRowPitchBytes = bufferIsSource ? imageRowPitchBytes : bufferRowPitchBytes;
+
+	bool isSinglePlane = (region.imageExtent.depth == 1);
+	bool isSingleLine  = (region.imageExtent.height == 1) && isSinglePlane;
+	bool isEntireLine  = (region.imageExtent.width == extent.width) &&
+	                     (imageRowPitchBytes == bufferRowPitchBytes);
+	bool isEntirePlane = isEntireLine && (region.imageExtent.height == extent.height) &&
+	                     (imageSlicePitchBytes == bufferSlicePitchBytes);
+
 	VkDeviceSize layerSize = slicePitchBytes(region.imageSubresource.aspectMask) * extent.depth;
 	char* bufferMemory = static_cast<char*>(Cast(buffer)->getOffsetPointer(region.bufferOffset));
-	char* imageMemory = static_cast<char*>(deviceMemory->getOffsetPointer(getMemoryOffset(region.imageSubresource.aspectMask)));
+	char* imageMemory = static_cast<char*>(deviceMemory->getOffsetPointer(getMemoryOffset(region.imageSubresource.aspectMask) +
+	                    texelOffsetBytesInStorage(region.imageOffset, region.imageSubresource.baseArrayLayer, region.imageSubresource.aspectMask)));
 	char* srcMemory = bufferIsSource ? bufferMemory : imageMemory;
 	char* dstMemory = bufferIsSource ? imageMemory : bufferMemory;
 
+	VkDeviceSize copySize = 0;
+	if(isSingleLine)
+	{
+		copySize = region.imageExtent.width * imageBytesPerTexel;
+	}
+	else if(isEntireLine && isSinglePlane)
+	{
+		copySize = region.imageExtent.height * imageRowPitchBytes;
+	}
+	else if(isEntirePlane)
+	{
+		copySize = region.imageExtent.depth * imageSlicePitchBytes; // Copy multiple planes
+	}
+	else if(isEntireLine) // Copy plane by plane
+	{
+		copySize = region.imageExtent.height * imageRowPitchBytes;
+	}
+	else // Copy line by line
+	{
+		copySize = region.imageExtent.width * imageBytesPerTexel;
+	}
+
 	uint32_t firstLayer = region.imageSubresource.baseArrayLayer;
 	uint32_t lastLayer = firstLayer + region.imageSubresource.layerCount - 1;
 	for(uint32_t layer = firstLayer; layer <= lastLayer; layer++)
 	{
-		memcpy(dstMemory, srcMemory, copySize);
+		if(isSingleLine || (isEntireLine && isSinglePlane) || isEntirePlane)
+		{
+			memcpy(dstMemory, srcMemory, copySize);
+		}
+		else if(isEntireLine) // Copy plane by plane
+		{
+			for(uint32_t z = 0; z < region.imageExtent.depth; z++)
+			{
+				memcpy(dstMemory, srcMemory, copySize);
+				srcMemory += srcSlicePitchBytes;
+				dstMemory += dstSlicePitchBytes;
+			}
+		}
+		else // Copy line by line
+		{
+			for(uint32_t z = 0; z < region.imageExtent.depth; z++)
+			{
+				for(uint32_t y = 0; y < region.imageExtent.height; y++)
+				{
+					memcpy(dstMemory, srcMemory, copySize);
+					srcMemory += srcRowPitchBytes;
+					dstMemory += dstRowPitchBytes;
+				}
+			}
+		}
+
 		srcMemory += layerSize;
 		dstMemory += layerSize;
 	}