Implement image blitting

First simple implementation of image blitting.
Note that layers are not yet taken into account.

Passes almost all tests in (There are only 2 failures,
which both have an off by 1 error on a single pixel):
dEQP-VK.api.copy_and_blit.core.blit_image.simple_tests.*

Bug b/118619338 b/119620767

Change-Id: I1e0ac88089d6159924569099ea6345804a219d2c
Reviewed-on: https://swiftshader-review.googlesource.com/c/23268
Tested-by: Alexis Hétu <sugoi@google.com>
Reviewed-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Vulkan/VkCommandBuffer.cpp b/src/Vulkan/VkCommandBuffer.cpp
index 7525749..86e2436 100644
--- a/src/Vulkan/VkCommandBuffer.cpp
+++ b/src/Vulkan/VkCommandBuffer.cpp
@@ -220,6 +220,25 @@
 	const VkBufferImageCopy region;
 };
 
+struct BlitImage : public CommandBuffer::Command
+{
+	BlitImage(VkImage srcImage, VkImage dstImage, const VkImageBlit& region, VkFilter filter) :
+		srcImage(srcImage), dstImage(dstImage), region(region), filter(filter)
+	{
+	}
+
+	void play(CommandBuffer::ExecutionState& executionState)
+	{
+		Cast(srcImage)->blit(dstImage, region, filter);
+	}
+
+private:
+	VkImage srcImage;
+	VkImage dstImage;
+	const VkImageBlit& region;
+	VkFilter filter;
+};
+
 struct PipelineBarrier : public CommandBuffer::Command
 {
 	PipelineBarrier()
@@ -530,7 +549,16 @@
 void CommandBuffer::blitImage(VkImage srcImage, VkImageLayout srcImageLayout, VkImage dstImage, VkImageLayout dstImageLayout,
 	uint32_t regionCount, const VkImageBlit* pRegions, VkFilter filter)
 {
-	UNIMPLEMENTED();
+	ASSERT(state == RECORDING);
+	ASSERT(srcImageLayout == VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL ||
+	       srcImageLayout == VK_IMAGE_LAYOUT_GENERAL);
+	ASSERT(dstImageLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL ||
+	       dstImageLayout == VK_IMAGE_LAYOUT_GENERAL);
+
+	for(uint32_t i = 0; i < regionCount; i++)
+	{
+		commands->push_back(std::make_unique<BlitImage>(srcImage, dstImage, pRegions[i], filter));
+	}
 }
 
 void CommandBuffer::copyBufferToImage(VkBuffer srcBuffer, VkImage dstImage, VkImageLayout dstImageLayout,
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 16e3fd9..0310615 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -368,6 +368,54 @@
 	return arrayLayers * extent.depth * slicePitchB;
 }
 
+sw::Surface* Image::asSurface(const VkImageAspectFlags& flags) const
+{
+	return sw::Surface::create(extent.width, extent.height, extent.depth, getFormat(flags),
+	                           deviceMemory->getOffsetPointer(memoryOffset),
+	                           rowPitchBytes(flags), slicePitchBytes(flags));
+}
+
+void Image::blit(VkImage dstImage, const VkImageBlit& region, VkFilter filter)
+{
+	VkImageAspectFlags srcFlags = region.srcSubresource.aspectMask;
+	VkImageAspectFlags dstFlags = region.dstSubresource.aspectMask;
+	if((region.srcSubresource.baseArrayLayer != 0) ||
+	   (region.dstSubresource.baseArrayLayer != 0) ||
+	   (region.srcSubresource.layerCount != 1) ||
+	   (region.dstSubresource.layerCount != 1) ||
+	   (region.srcSubresource.mipLevel != 0) ||
+	   (region.dstSubresource.mipLevel != 0) ||
+	   (srcFlags != dstFlags))
+	{
+		UNIMPLEMENTED();
+	}
+
+	int32_t numSlices = (region.srcOffsets[1].z - region.srcOffsets[0].z);
+	ASSERT(numSlices == (region.dstOffsets[1].z - region.dstOffsets[0].z));
+
+	sw::Surface* srcSurface = asSurface(srcFlags);
+	sw::Surface* dstSurface = Cast(dstImage)->asSurface(dstFlags);
+
+	sw::SliceRectF sRect(static_cast<float>(region.srcOffsets[0].x), static_cast<float>(region.srcOffsets[0].y),
+	                     static_cast<float>(region.srcOffsets[1].x), static_cast<float>(region.srcOffsets[1].y),
+	                     region.srcOffsets[0].z);
+
+	sw::SliceRect dRect(region.dstOffsets[0].x, region.dstOffsets[0].y,
+	                    region.dstOffsets[1].x, region.dstOffsets[1].y, region.dstOffsets[0].z);
+
+	sw::Blitter blitter;
+	for(int i = 0; i < numSlices; i++)
+	{
+		blitter.blit(srcSurface, sRect, dstSurface, dRect,
+		             {filter != VK_FILTER_NEAREST, srcFlags == VK_IMAGE_ASPECT_STENCIL_BIT, false});
+		sRect.slice++;
+		dRect.slice++;
+	}
+
+	delete srcSurface;
+	delete dstSurface;
+}
+
 void Image::clear(const VkClearValue& clearValue, const VkRect2D& renderArea, const VkImageSubresourceRange& subresourceRange)
 {
 	if(!((subresourceRange.aspectMask == VK_IMAGE_ASPECT_COLOR_BIT) ||
@@ -398,9 +446,7 @@
 	                    renderArea.offset.y + renderArea.extent.height);
 	const sw::SliceRect dRect(rect);
 
-	sw::Surface* surface = sw::Surface::create(extent.width, extent.height, extent.depth,
-		getFormat(subresourceRange.aspectMask), deviceMemory->getOffsetPointer(memoryOffset),
-		rowPitchBytes(subresourceRange.aspectMask), slicePitchBytes(subresourceRange.aspectMask));
+	sw::Surface* surface = asSurface(subresourceRange.aspectMask);
 	sw::Blitter blitter;
 	blitter.clear((void*)clearValue.color.float32, clearFormat, surface, dRect, 0xF);
 	delete surface;
diff --git a/src/Vulkan/VkImage.hpp b/src/Vulkan/VkImage.hpp
index 006b3c6..cfcd379 100644
--- a/src/Vulkan/VkImage.hpp
+++ b/src/Vulkan/VkImage.hpp
@@ -42,6 +42,7 @@
 	void copyTo(VkBuffer dstBuffer, const VkBufferImageCopy& region);
 	void copyFrom(VkBuffer srcBuffer, const VkBufferImageCopy& region);
 
+	void blit(VkImage dstImage, const VkImageBlit& region, VkFilter filter);
 	void clear(const VkClearValue& clearValue, const VkRect2D& renderArea, const VkImageSubresourceRange& subresourceRange);
 
 	VkImageType              getImageType() const { return imageType; }
@@ -58,6 +59,7 @@
 	int bytesPerTexel(const VkImageAspectFlags& flags) const;
 	VkFormat getFormat(const VkImageAspectFlags& flags) const;
 	int getBorder() const;
+	sw::Surface* asSurface(const VkImageAspectFlags& flags) const;
 
 	DeviceMemory*            deviceMemory = nullptr;
 	VkDeviceSize             memoryOffset = 0;