MVKCmdBlitImage: Support blits with non-2D images.

This is needed to get us past the 3D blit tests that were recently added
to the CTS. It *almost* passes all these new tests; the 3D format tests
fail for some reason.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index e4e641d..42ae92b 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -428,6 +428,18 @@
             MVKRPSKeyBlitImg blitKey;
             blitKey.srcMTLPixelFormat = _srcImage->getMTLPixelFormat(srcPlaneIndex);
             blitKey.srcMTLTextureType = _srcImage->getMTLTextureType();
+            if (blitKey.srcMTLTextureType == MTLTextureTypeCube || blitKey.srcMTLTextureType == MTLTextureTypeCubeArray) {
+                // In this case, I'll use a temp 2D array view. That way, I don't have to
+                // deal with mapping the blit coordinates to a cube direction vector.
+                blitKey.srcMTLTextureType = MTLTextureType2DArray;
+                srcMTLTex = [srcMTLTex newTextureViewWithPixelFormat: (MTLPixelFormat)blitKey.srcMTLPixelFormat
+                                                         textureType: MTLTextureType2DArray
+                                                              levels: NSMakeRange(0, srcMTLTex.mipmapLevelCount)
+                                                              slices: NSMakeRange(0, srcMTLTex.arrayLength)];
+                [cmdEncoder->_mtlCmdBuffer addCompletedHandler: ^(id<MTLCommandBuffer>) {
+                    [srcMTLTex release];
+                }];
+            }
             blitKey.dstMTLPixelFormat = _dstImage->getMTLPixelFormat(dstPlaneIndex);
             blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(_filter);
             blitKey.dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount());
@@ -438,12 +450,31 @@
             mtlColorAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
 
             uint32_t layCnt = mvkIBR.region.srcSubresource.layerCount;
+            if (_dstImage->getMTLTextureType() == MTLTextureType3D) {
+                layCnt = mvkAbsDiff(mvkIBR.region.dstOffsets[1].z, mvkIBR.region.dstOffsets[0].z);
+            }
             for (uint32_t layIdx = 0; layIdx < layCnt; layIdx++) {
                 // Update the render pass descriptor for the texture level and slice, and create a render encoder.
-                mtlColorAttDesc.slice = mvkIBR.region.dstSubresource.baseArrayLayer + layIdx;
+                if (_dstImage->getMTLTextureType() == MTLTextureType3D) {
+                    mtlColorAttDesc.depthPlane = mvkIBR.region.dstOffsets[0].z + (mvkIBR.region.dstOffsets[1].z > mvkIBR.region.dstOffsets[0].z ? layIdx : -(layIdx + 1));
+                } else {
+                    mtlColorAttDesc.slice = mvkIBR.region.dstSubresource.baseArrayLayer + layIdx;
+                }
                 id<MTLRenderCommandEncoder> mtlRendEnc = [cmdEncoder->_mtlCmdBuffer renderCommandEncoderWithDescriptor: mtlRPD];
                 setLabelIfNotNil(mtlRendEnc, mvkMTLRenderCommandEncoderLabel(commandUse));
 
+                if (blitKey.srcMTLTextureType == MTLTextureType3D) {
+                    // In this case, I need to interpolate along the third dimension manually.
+                    VkExtent3D srcExtent = _srcImage->getExtent3D(srcPlaneIndex, mvkIBR.region.dstSubresource.mipLevel);
+                    VkOffset3D so0 = mvkIBR.region.srcOffsets[0], so1 = mvkIBR.region.srcOffsets[1];
+                    VkOffset3D do0 = mvkIBR.region.dstOffsets[0], do1 = mvkIBR.region.dstOffsets[1];
+                    CGFloat startZ = (CGFloat)so0.z / (CGFloat)srcExtent.depth;
+                    CGFloat endZ = (CGFloat)so1.z / (CGFloat)srcExtent.depth;
+                    CGFloat zIncr = (endZ - startZ) / mvkAbsDiff(do1.z, do0.z);
+                    for (uint32_t i = 0; i < kMVKBlitVertexCount; ++i) {
+                        mvkIBR.vertices[i].texCoord.z = startZ + layIdx * zIncr;
+                    }
+                }
                 [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"];
                 [mtlRendEnc setRenderPipelineState: mtlRPS];
                 cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices), vtxBuffIdx);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h b/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h
index 68e60d7..d562183 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPipelineStateFactoryShaderSource.h
@@ -29,12 +29,12 @@
                                                                                                                 \n\
 typedef struct {                                                                                                \n\
     float2 a_position [[attribute(0)]];                                                                         \n\
-    float2 a_texCoord [[attribute(1)]];                                                                         \n\
+    float3 a_texCoord [[attribute(1)]];                                                                         \n\
 } AttributesPosTex;                                                                                             \n\
                                                                                                                 \n\
 typedef struct {                                                                                                \n\
     float4 v_position [[position]];                                                                             \n\
-    float2 v_texCoord;                                                                                          \n\
+    float3 v_texCoord;                                                                                          \n\
 } VaryingsPosTex;                                                                                               \n\
                                                                                                                 \n\
 typedef size_t VkDeviceSize;                                                                                    \n\
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
index 7006fe4..1e5d460 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.h
@@ -58,6 +58,8 @@
 
 	inline MTLSamplerMinMagFilter getSrcMTLSamplerMinMagFilter() { return (MTLSamplerMinMagFilter)srcFilter; }
 
+	inline MTLTextureType getSrcMTLTextureType() { return (MTLTextureType)srcMTLTextureType; }
+
 	inline bool isSrcArrayType() {
 		return (srcMTLTextureType == MTLTextureType2DArray ||
 #if MVK_MACOS
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
index 751fb18..51640ae 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandResourceFactory.mm
@@ -57,14 +57,14 @@
     vaDesc.format = MTLVertexFormatFloat2;
     vaDesc.bufferIndex = vtxBuffIdx;
     vaDesc.offset = vtxStride;
-    vtxStride += sizeof(simd::float2);
+    vtxStride += sizeof(simd::float4);
 
     // Vertex texture coords
     vaDesc = vaDescArray[1];
-    vaDesc.format = MTLVertexFormatFloat2;
+    vaDesc.format = MTLVertexFormatFloat3;
     vaDesc.bufferIndex = vtxBuffIdx;
     vaDesc.offset = vtxStride;
-    vtxStride += sizeof(simd::float2);
+    vtxStride += sizeof(simd::float4);
 
     // Vertex attribute buffer.
     MTLVertexBufferLayoutDescriptorArray* vbDescArray = vtxDesc.layouts;
@@ -158,7 +158,34 @@
 
 		bool isArrayType = blitKey.isSrcArrayType();
 		bool isLinearFilter = (blitKey.getSrcMTLSamplerMinMagFilter() == MTLSamplerMinMagFilterLinear);
-		NSString* arraySuffix = isArrayType ? @"_array" : @"";
+		NSString* typeSuffix;
+		NSString* coordArg;
+		switch (blitKey.getSrcMTLTextureType()) {
+			case MTLTextureType1D:
+				typeSuffix = @"1d";
+				coordArg = @".x";
+				break;
+			case MTLTextureType1DArray:
+				typeSuffix = @"1d_array";
+				coordArg = @".x";
+				break;
+			case MTLTextureType2D:
+				typeSuffix = @"2d";
+				coordArg = @".xy";
+				break;
+			case MTLTextureType2DArray:
+				typeSuffix = @"2d_array";
+				coordArg = @".xy";
+				break;
+			case MTLTextureType3D:
+				typeSuffix = @"3d";
+				coordArg = @"";
+				break;
+			default:
+				typeSuffix = @"unsupported";
+				coordArg = @"";
+				break;
+		}
 		NSString* sliceArg = isArrayType ? @", subRez.slice" : @"";
 		NSString* srcFilter = isLinearFilter ? @"linear" : @"nearest";
 
@@ -168,7 +195,7 @@
 		[msl appendLineMVK];
 		[msl appendLineMVK: @"typedef struct {"];
 		[msl appendLineMVK: @"    float4 v_position [[position]];"];
-		[msl appendLineMVK: @"    float2 v_texCoord;"];
+		[msl appendLineMVK: @"    float3 v_texCoord;"];
 		[msl appendLineMVK: @"} VaryingsPosTex;"];
 		[msl appendLineMVK];
 		[msl appendLineMVK: @"typedef struct {"];
@@ -183,10 +210,10 @@
 		NSString* funcName = @"fragCmdBlitImage";
 		[msl appendFormat: @"fragment %@4 %@(VaryingsPosTex varyings [[stage_in]],", typeStr, funcName];
 		[msl appendLineMVK];
-		[msl appendFormat: @"                         texture2d%@<%@> tex [[texture(0)]],", arraySuffix, typeStr];
+		[msl appendFormat: @"                         texture%@<%@> tex [[texture(0)]],", typeSuffix, typeStr];
 		[msl appendLineMVK];
 		[msl appendLineMVK: @"                         constant TexSubrez& subRez [[buffer(0)]]) {"];
-		[msl appendFormat: @"    return tex.sample(ce_sampler, varyings.v_texCoord%@, level(subRez.lod));", sliceArg];
+		[msl appendFormat: @"    return tex.sample(ce_sampler, varyings.v_texCoord%@%@, level(subRez.lod));", coordArg, sliceArg];
 		[msl appendLineMVK];
 		[msl appendLineMVK: @"}"];
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index c4095dd..3ce5714 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -53,7 +53,7 @@
 /** 2D vertex position and texcoord content. */
 typedef struct {
 	simd::float2 position;
-	simd::float2 texCoord;
+	simd::float3 texCoord;
 } MVKVertexPosTex;
 
 
@@ -375,6 +375,12 @@
 	}
 };
 
+/** Returns the absolute value of the difference of two numbers. */
+template<typename T, typename U>
+constexpr typename std::common_type<T, U>::type mvkAbsDiff(T x, U y) {
+	return x >= y ? x - y : y - x;
+}
+
 /** Returns the greatest common divisor of two numbers. */
 template<typename T>
 constexpr T mvkGreatestCommonDivisorImpl(T a, T b) {