Make multisample resolve a Blitter method

The Vulkan spec explicitly states that "vkCmdBlitImage must not be used
for multisampled source or destination images. Use vkCmdResolveImage for
this purpose." And the only other way to obtain resolve multisample
results is by using resolve attachments as part of a subpass.

This split between blit operations and resolve operations should be
reflected by the Blitter interface so we have less confusion about
its blit() method being used to perform resolves. It will also
facilitate adding a fast path for common resolve operations.

Bug: b/147802090
Change-Id: I2549a5e7acd7ef9ec3f70f8ceb88ff5fc65a0d17
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/47988
Presubmit-Ready: Nicolas Capens <nicolascapens@google.com>
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/Device/Blitter.cpp b/src/Device/Blitter.cpp
index 72f07d3..6d4b1ee 100644
--- a/src/Device/Blitter.cpp
+++ b/src/Device/Blitter.cpp
@@ -1741,31 +1741,10 @@
 	return cornerUpdateRoutine;
 }
 
-void Blitter::copy(const vk::Image *src, uint8_t *dst, unsigned int dstPitch)
-{
-	const VkExtent3D &extent = src->getExtent();
-	size_t rowBytes = src->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes() * extent.width;
-	unsigned int srcPitch = src->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
-	ASSERT(dstPitch >= rowBytes && srcPitch >= rowBytes);
-
-	const uint8_t *s = (uint8_t *)src->getTexelPointer({ 0, 0, 0 }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 });
-	uint8_t *d = dst;
-
-	for(uint32_t y = 0; y < extent.height; y++)
-	{
-		memcpy(d, s, rowBytes);
-
-		s += srcPitch;
-		d += dstPitch;
-	}
-}
-
 void Blitter::blit(const vk::Image *src, vk::Image *dst, VkImageBlit region, VkFilter filter)
 {
-	if(dst->getFormat() == VK_FORMAT_UNDEFINED)
-	{
-		return;
-	}
+	ASSERT(src->getFormat() != VK_FORMAT_UNDEFINED);
+	ASSERT(dst->getFormat() != VK_FORMAT_UNDEFINED);
 
 	// Vulkan 1.2 section 18.5. Image Copies with Scaling:
 	// "The layerCount member of srcSubresource and dstSubresource must match"
@@ -1874,7 +1853,7 @@
 	VkImageSubresourceRange dstSubresRange = {
 		region.dstSubresource.aspectMask,
 		region.dstSubresource.mipLevel,
-		1,
+		1,  // levelCount
 		region.dstSubresource.baseArrayLayer,
 		region.dstSubresource.layerCount
 	};
@@ -1895,6 +1874,45 @@
 	dst->contentsChanged(dstSubresRange);
 }
 
+void Blitter::resolve(const vk::Image *src, vk::Image *dst, VkImageResolve region)
+{
+	VkImageBlit blitRegion;
+
+	blitRegion.srcOffsets[0] = blitRegion.srcOffsets[1] = region.srcOffset;
+	blitRegion.srcOffsets[1].x += region.extent.width;
+	blitRegion.srcOffsets[1].y += region.extent.height;
+	blitRegion.srcOffsets[1].z += region.extent.depth;
+
+	blitRegion.dstOffsets[0] = blitRegion.dstOffsets[1] = region.dstOffset;
+	blitRegion.dstOffsets[1].x += region.extent.width;
+	blitRegion.dstOffsets[1].y += region.extent.height;
+	blitRegion.dstOffsets[1].z += region.extent.depth;
+
+	blitRegion.srcSubresource = region.srcSubresource;
+	blitRegion.dstSubresource = region.dstSubresource;
+
+	blit(src, dst, blitRegion, VK_FILTER_NEAREST);
+}
+
+void Blitter::copy(const vk::Image *src, uint8_t *dst, unsigned int dstPitch)
+{
+	VkExtent3D extent = src->getExtent();
+	size_t rowBytes = src->getFormat(VK_IMAGE_ASPECT_COLOR_BIT).bytes() * extent.width;
+	unsigned int srcPitch = src->rowPitchBytes(VK_IMAGE_ASPECT_COLOR_BIT, 0);
+	ASSERT(dstPitch >= rowBytes && srcPitch >= rowBytes && src->getMipLevelExtent(VK_IMAGE_ASPECT_COLOR_BIT, 0).height >= extent.height);
+
+	const uint8_t *s = (uint8_t *)src->getTexelPointer({ 0, 0, 0 }, { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 });
+	uint8_t *d = dst;
+
+	for(uint32_t y = 0; y < extent.height; y++)
+	{
+		memcpy(d, s, rowBytes);
+
+		s += srcPitch;
+		d += dstPitch;
+	}
+}
+
 void Blitter::computeCubeCorner(Pointer<Byte> &layer, Int &x0, Int &x1, Int &y0, Int &y1, Int &pitchB, const State &state)
 {
 	int bytes = state.sourceFormat.bytes();
diff --git a/src/Device/Blitter.hpp b/src/Device/Blitter.hpp
index d9cb93a..b114d73 100644
--- a/src/Device/Blitter.hpp
+++ b/src/Device/Blitter.hpp
@@ -144,6 +144,7 @@
 	void clear(void *clearValue, vk::Format clearFormat, vk::Image *dest, const vk::Format &viewFormat, const VkImageSubresourceRange &subresourceRange, const VkRect2D *renderArea = nullptr);
 
 	void blit(const vk::Image *src, vk::Image *dst, VkImageBlit region, VkFilter filter);
+	void resolve(const vk::Image *src, vk::Image *dst, VkImageResolve region);
 	void copy(const vk::Image *src, uint8_t *dst, unsigned int dstPitch);
 
 	void updateBorders(vk::Image *image, const VkImageSubresource &subresource);
diff --git a/src/Vulkan/VkImage.cpp b/src/Vulkan/VkImage.cpp
index 6807404..966dac3 100644
--- a/src/Vulkan/VkImage.cpp
+++ b/src/Vulkan/VkImage.cpp
@@ -942,22 +942,7 @@
 
 void Image::resolveTo(Image *dstImage, const VkImageResolve &region) const
 {
-	VkImageBlit blitRegion;
-
-	blitRegion.srcOffsets[0] = blitRegion.srcOffsets[1] = region.srcOffset;
-	blitRegion.srcOffsets[1].x += region.extent.width;
-	blitRegion.srcOffsets[1].y += region.extent.height;
-	blitRegion.srcOffsets[1].z += region.extent.depth;
-
-	blitRegion.dstOffsets[0] = blitRegion.dstOffsets[1] = region.dstOffset;
-	blitRegion.dstOffsets[1].x += region.extent.width;
-	blitRegion.dstOffsets[1].y += region.extent.height;
-	blitRegion.dstOffsets[1].z += region.extent.depth;
-
-	blitRegion.srcSubresource = region.srcSubresource;
-	blitRegion.dstSubresource = region.dstSubresource;
-
-	device->getBlitter()->blit(this, dstImage, blitRegion, VK_FILTER_NEAREST);
+	device->getBlitter()->resolve(this, dstImage, region);
 }
 
 VkFormat Image::getClearFormat() const