MVKCmdResolveImage: Fix resolving sub-regions.

When doing multisample resolution in Metal, the dimensions of the MSAA
RT and the resolve destination must be the same. Therefore, if the
resolve region does not cover the entire destination, we must use a
temporary transfer image. This fixes a validation error in the differing
image size tests from the CTS
(`dEQP-VK.api.copy_and_blit.*.resolve_image.diff_image_size.*`).

Because the temporary transfer image has the same dimensions as the
destination and is intended to be resolved to it, copies from the source
should use the destination's parameters for the temp image. That way,
the regions show up in the correct place in the destination. This fixes
the remaining resolve tests.

Don't do expansion blits if the resolve region covers the entire
destination. This should reduce the amount of needless work we do in
that case.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
index 4bc8b11..849d51c 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
@@ -125,9 +125,8 @@
 
 /** Describes Metal texture resolve parameters. */
 typedef struct {
-    VkImageCopy* copyRegion;
-    uint32_t level;
-    uint32_t slice;
+    VkImageSubresource srcSubresource;
+    VkImageSubresource dstSubresource;
 } MVKMetalResolveSlice;
 
 /**
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index f494748..24e840b 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -490,68 +490,75 @@
         uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.srcSubresource.aspectMask);
         uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(vkIR.dstSubresource.aspectMask);
 
-		uint32_t mipLvl = vkIR.dstSubresource.mipLevel;
-		VkExtent3D srcImgExt = _srcImage->getExtent3D(srcPlaneIndex, mipLvl);
-		VkExtent3D dstImgExt = _dstImage->getExtent3D(dstPlaneIndex, mipLvl);
+		VkExtent3D srcImgExt = _srcImage->getExtent3D(srcPlaneIndex, vkIR.srcSubresource.mipLevel);
+		VkExtent3D dstImgExt = _dstImage->getExtent3D(dstPlaneIndex, vkIR.dstSubresource.mipLevel);
 
-		// If the region does not cover the entire content of the source level, expand the
-		// destination content in the region to the temporary image. The purpose of this
+		// If the region does not cover the entire content of the destination level, expand
+		// the destination content in the region to the temporary image. The purpose of this
 		// expansion is to render the existing content of the destination image to the
 		// temporary transfer multisample image, so that regions of that temporary transfer
 		// image can then be overwritten with content from the source image, prior to
-		// resolving it back to the destination image. The source of this temporary content
-		// move is the full extent of the DESTINATION image of the resolve command, and the
-		// destination of this temporary content move is the full extent of the SOURCE image.
-		if ( !mvkVkExtent3DsAreEqual(srcImgExt, vkIR.extent) ) {
+		// resolving it back to the destination image.
+		if ( !mvkVkExtent3DsAreEqual(dstImgExt, vkIR.extent) ) {
 			VkImageBlit& expRgn = expansionRegions[expCnt++];
 			expRgn.srcSubresource = vkIR.dstSubresource;
 			expRgn.srcOffsets[0] = { 0, 0, 0 };
 			expRgn.srcOffsets[1] = { int32_t(dstImgExt.width), int32_t(dstImgExt.height), int32_t(dstImgExt.depth) };
 			expRgn.dstSubresource = vkIR.dstSubresource;
 			expRgn.dstOffsets[0] = { 0, 0, 0 };
-			expRgn.dstOffsets[1] = { int32_t(srcImgExt.width), int32_t(srcImgExt.height), int32_t(srcImgExt.depth) };
+			expRgn.dstOffsets[1] = { int32_t(dstImgExt.width), int32_t(dstImgExt.height), int32_t(dstImgExt.depth) };
 		}
 
 		// Copy the region from the source image to the temporary multisample image,
 		// prior to the temporary image being resolved back to the destination image.
 		// The source of this copy stage is the source image, and the destination of
 		// this copy stage is the temporary transfer image.
-		VkImageCopy& cpyRgn = copyRegions[copyCnt++];
-		cpyRgn.srcSubresource = vkIR.srcSubresource;
-		cpyRgn.srcOffset = vkIR.srcOffset;
-		cpyRgn.dstSubresource = vkIR.srcSubresource;
-		cpyRgn.dstOffset = vkIR.srcOffset;
-		cpyRgn.extent = vkIR.extent;
+		bool needXfrImage = !mvkVkExtent3DsAreEqual(srcImgExt, vkIR.extent) || !mvkVkExtent3DsAreEqual(dstImgExt, vkIR.extent);
+		if ( needXfrImage ) {
+			VkImageCopy& cpyRgn = copyRegions[copyCnt++];
+			cpyRgn.srcSubresource = vkIR.srcSubresource;
+			cpyRgn.srcOffset = vkIR.srcOffset;
+			cpyRgn.dstSubresource = vkIR.dstSubresource;
+			cpyRgn.dstOffset = vkIR.dstOffset;
+			cpyRgn.extent = vkIR.extent;
+		}
 
 		// Adds a resolve slice struct for each destination layer in the resolve region.
-		uint32_t baseLayer = vkIR.dstSubresource.baseArrayLayer;
+		// Note that the source subresource for this is that of the SOURCE image if we're doing a
+		// direct resolve, but that of the DESTINATION if we need a temporary transfer image.
 		uint32_t layCnt = vkIR.dstSubresource.layerCount;
 		for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) {
 			MVKMetalResolveSlice& rslvSlice = mtlResolveSlices[sliceCnt++];
-            rslvSlice.copyRegion = &cpyRgn;
-			rslvSlice.level = vkIR.dstSubresource.mipLevel;
-			rslvSlice.slice = baseLayer + layIdx;
+			rslvSlice.dstSubresource.aspectMask = vkIR.dstSubresource.aspectMask;
+			rslvSlice.dstSubresource.mipLevel = vkIR.dstSubresource.mipLevel;
+			rslvSlice.dstSubresource.arrayLayer = vkIR.dstSubresource.baseArrayLayer + layIdx;
+			rslvSlice.srcSubresource.aspectMask = needXfrImage ? vkIR.dstSubresource.aspectMask : vkIR.srcSubresource.aspectMask;
+			rslvSlice.srcSubresource.mipLevel = needXfrImage ? vkIR.dstSubresource.mipLevel : vkIR.srcSubresource.mipLevel;
+			rslvSlice.srcSubresource.arrayLayer = needXfrImage ? vkIR.dstSubresource.baseArrayLayer : vkIR.srcSubresource.baseArrayLayer;
+			rslvSlice.srcSubresource.arrayLayer += layIdx;
 		}
 	}
 
     // Expansion and copying is not required. Each mip level of the source image
     // is being resolved entirely. Resolve directly from the source image.
     MVKImage* xfrImage = _srcImage;
-	if (expCnt) {
-		// Expansion and copying is required. Acquire a temporary transfer image, expand
-		// the destination image into it, copy from the source image to the temporary image,
-		// and then resolve from the temporary image to the destination image.
+	if (copyCnt) {
+		// Expansion and/or copying is required. Acquire a temporary transfer image, expand
+		// the destination image into it if necessary, copy from the source image to the
+		// temporary image, and then resolve from the temporary image to the destination image.
 		MVKImageDescriptorData xferImageData;
 		_dstImage->getTransferDescriptorData(xferImageData);
 		xferImageData.samples = _srcImage->getSampleCount();
 		xfrImage = cmdEncoder->getCommandEncodingPool()->getTransferMVKImage(xferImageData);
 
-		// Expand the current content of the destination image to the temporary transfer image.
-		MVKCmdBlitImage<N> expCmd;
-		expCmd.setContent(cmdEncoder->_cmdBuffer,
-						  (VkImage)_dstImage, _dstLayout, (VkImage)xfrImage, _dstLayout,
-						  expCnt, expansionRegions, VK_FILTER_LINEAR);
-		expCmd.encode(cmdEncoder, kMVKCommandUseResolveExpandImage);
+		if (expCnt) {
+			// Expand the current content of the destination image to the temporary transfer image.
+			MVKCmdBlitImage<N> expCmd;
+			expCmd.setContent(cmdEncoder->_cmdBuffer,
+							  (VkImage)_dstImage, _dstLayout, (VkImage)xfrImage, _dstLayout,
+							  expCnt, expansionRegions, VK_FILTER_LINEAR);
+			expCmd.encode(cmdEncoder, kMVKCommandUseResolveExpandImage);
+		}
 
 		// Copy the resolve regions of the source image to the temporary transfer image.
 		MVKCmdCopyImage<N> copyCmd;
@@ -573,15 +580,15 @@
 	// the texture level and slice and create a render encoder.
 	for (uint32_t sIdx = 0; sIdx < sliceCnt; sIdx++) {
 		MVKMetalResolveSlice& rslvSlice = mtlResolveSlices[sIdx];
-        uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.copyRegion->srcSubresource.aspectMask);
-        uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.copyRegion->dstSubresource.aspectMask);
+        uint8_t srcPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.srcSubresource.aspectMask);
+        uint8_t dstPlaneIndex = MVKImage::getPlaneFromVkImageAspectFlags(rslvSlice.dstSubresource.aspectMask);
 
         mtlColorAttDesc.texture = xfrImage->getMTLTexture(srcPlaneIndex);
         mtlColorAttDesc.resolveTexture = _dstImage->getMTLTexture(dstPlaneIndex);
-		mtlColorAttDesc.level = rslvSlice.level;
-		mtlColorAttDesc.slice = rslvSlice.slice;
-		mtlColorAttDesc.resolveLevel = rslvSlice.level;
-		mtlColorAttDesc.resolveSlice = rslvSlice.slice;
+		mtlColorAttDesc.level = rslvSlice.srcSubresource.mipLevel;
+		mtlColorAttDesc.slice = rslvSlice.srcSubresource.arrayLayer;
+		mtlColorAttDesc.resolveLevel = rslvSlice.dstSubresource.mipLevel;
+		mtlColorAttDesc.resolveSlice = rslvSlice.dstSubresource.arrayLayer;
 		id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPD];
 		setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(kMVKCommandUseResolveImage));