Merge pull request #1029 from cdavis5e/resolve-subregion-fix

MVKCmdResolveImage: Fix resolving sub-regions.
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));