MVKCmdBlitImage support multisample textures.

MVKCmdCopyImage track src & dest sample counts.
MVKRPSKeyBlitImg track dest sample count.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
index 8c959fb..bd7110a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
@@ -62,6 +62,8 @@
 	VkImageLayout _dstLayout;
 	MTLPixelFormat _srcMTLPixFmt;
 	MTLPixelFormat _dstMTLPixFmt;
+	uint32_t _srcSampleCount;
+	uint32_t _dstSampleCount;
 	bool _isSrcCompressed;
 	bool _isDstCompressed;
 	bool _canCopyFormats;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index fbe04aa..0a514c9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -65,14 +65,18 @@
 	_srcImage = (MVKImage*)srcImage;
 	_srcLayout = srcImageLayout;
 	_srcMTLPixFmt = _srcImage->getMTLPixelFormat();
+	_srcSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_srcImage->getSampleCount());
 	_isSrcCompressed = _srcImage->getIsCompressed();
+	uint32_t srcBytesPerBlock = mvkMTLPixelFormatBytesPerBlock(_srcMTLPixFmt);
 
 	_dstImage = (MVKImage*)dstImage;
 	_dstLayout = dstImageLayout;
 	_dstMTLPixFmt = _dstImage->getMTLPixelFormat();
+	_dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount());
 	_isDstCompressed = _dstImage->getIsCompressed();
+	uint32_t dstBytesPerBlock = mvkMTLPixelFormatBytesPerBlock(_dstMTLPixFmt);
 
-	_canCopyFormats = mvkMTLPixelFormatBytesPerBlock(_srcMTLPixFmt) == mvkMTLPixelFormatBytesPerBlock(_dstMTLPixFmt);
+	_canCopyFormats = (srcBytesPerBlock == dstBytesPerBlock) && (_srcSampleCount == _dstSampleCount);
 	_shouldUseTextureView = (_srcMTLPixFmt != _dstMTLPixFmt) && !(_isSrcCompressed || _isDstCompressed);	// Different formats and neither is compressed
 	_shouldUseTempBuffer = (_srcMTLPixFmt != _dstMTLPixFmt) && (_isSrcCompressed || _isDstCompressed);		// Different formats and at least one is compressed
 
@@ -219,6 +223,7 @@
 	_blitKey.srcMTLPixelFormat = (uint32_t)_srcMTLPixFmt;
 	_blitKey.srcMTLTextureType = (uint32_t)_srcImage->getMTLTextureType();
 	_blitKey.dstMTLPixelFormat = (uint32_t)_dstMTLPixFmt;
+	_blitKey.dstSampleCount = _dstSampleCount;
 
 	for (uint32_t i = 0; i < regionCount; i++) {
 		addImageBlitRegion(pRegions[i]);
@@ -529,7 +534,7 @@
     if (expRgnCnt > 0) {
         MVKCmdBlitImage expandCmd(&getCommandPool()->_cmdBlitImagePool);
         expandCmd.setContent((VkImage)_dstImage, _dstLayout, (VkImage)xfrImage, _dstLayout,
-                             uint32_t(_expansionRegions.size()), _expansionRegions.data(),
+                             expRgnCnt, _expansionRegions.data(),
                              VK_FILTER_LINEAR, kMVKCommandUseResolveExpandImage);
         expandCmd.encode(cmdEncoder);
     }
@@ -541,7 +546,7 @@
     if (cpyRgnCnt > 0) {
         MVKCmdCopyImage copyCmd(&getCommandPool()->_cmdCopyImagePool);
         copyCmd.setContent((VkImage)_srcImage, _srcLayout, (VkImage)xfrImage, _dstLayout,
-                           uint32_t(_copyRegions.size()), _copyRegions.data(), kMVKCommandUseResolveCopyImage);
+                           cpyRgnCnt, _copyRegions.data(), kMVKCommandUseResolveCopyImage);
         copyCmd.encode(cmdEncoder);
     }
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
index 6a808be..0749be9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
@@ -38,11 +38,13 @@
 	uint16_t srcMTLPixelFormat = 0;			/**< as MTLPixelFormat */
 	uint16_t srcMTLTextureType = 0;			/**< as MTLTextureType */
 	uint16_t dstMTLPixelFormat = 0;			/**< as MTLPixelFormat */
+	uint16_t dstSampleCount = 0;
 
 	bool operator==(const MVKRPSKeyBlitImg_t& rhs) const {
 		if (srcMTLPixelFormat != rhs.srcMTLPixelFormat) { return false; }
 		if (srcMTLTextureType != rhs.srcMTLTextureType) { return false; }
 		if (dstMTLPixelFormat != rhs.dstMTLPixelFormat) { return false; }
+		if (dstSampleCount != rhs.dstSampleCount) { return false; }
 		return true;
 	}
 
@@ -63,6 +65,8 @@
 		hash |= srcMTLTextureType;
 		hash <<= 16;
 		hash |= dstMTLPixelFormat;
+		hash <<= 16;
+		hash |= dstSampleCount;
 		return hash;
 	}
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
index 845450a..bf86b8f 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
@@ -38,6 +38,7 @@
 
 	plDesc.vertexFunction = getFunctionNamed("vtxCmdBlitImage");
     plDesc.fragmentFunction = getBlitFragFunction(blitKey);
+	plDesc.sampleCount = blitKey.dstSampleCount;
 
 	plDesc.colorAttachments[0].pixelFormat = blitKey.getDstMTLPixelFormat();