Merge pull request #864 from billhollings/master

In MVKCommand subclasses, replace holding Metal content with holding smaller Vulkan equivalents.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
index 1dd9afc..336355d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.h
@@ -40,7 +40,12 @@
 protected:
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
-    MTLRegion  _mtlThreadgroupCount;
+	uint32_t _baseGroupX;
+	uint32_t _baseGroupY;
+	uint32_t _baseGroupZ;
+	uint32_t _groupCountX;
+	uint32_t _groupCountY;
+	uint32_t _groupCountZ;
 };
 
 
@@ -59,6 +64,6 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	id<MTLBuffer> _mtlIndirectBuffer;
-	NSUInteger _mtlIndirectBufferOffset;
+	VkDeviceSize _mtlIndirectBufferOffset;
 };
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
index fd4479b..4a4fc48 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
@@ -33,7 +33,13 @@
 VkResult MVKCmdDispatch::setContent(MVKCommandBuffer* cmdBuff,
 									uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ,
 									uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
-    _mtlThreadgroupCount = MTLRegionMake3D(baseGroupX, baseGroupY, baseGroupZ, groupCountX, groupCountY, groupCountZ);
+	_baseGroupX = baseGroupX;
+	_baseGroupY = baseGroupY;
+	_baseGroupZ = baseGroupZ;
+
+	_groupCountX = groupCountX;
+	_groupCountY = groupCountY;
+	_groupCountZ = groupCountZ;
 
 	return VK_SUCCESS;
 }
@@ -41,6 +47,7 @@
 void MVKCmdDispatch::encode(MVKCommandEncoder* cmdEncoder) {
 //    MVKLogDebug("vkCmdDispatch() dispatching (%d, %d, %d) threadgroups.", _x, _y, _z);
 
+	MTLRegion mtlThreadgroupCount = MTLRegionMake3D(_baseGroupX, _baseGroupY, _baseGroupZ, _groupCountX, _groupCountY, _groupCountZ);
 	cmdEncoder->finalizeDispatchState();	// Ensure all updated state has been submitted to Metal
 	id<MTLComputeCommandEncoder> mtlEncoder = cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch);
 	auto* pipeline = (MVKComputePipeline*)cmdEncoder->_computePipelineState.getPipeline();
@@ -48,14 +55,14 @@
 		if ([mtlEncoder respondsToSelector: @selector(setStageInRegion:)]) {
 			// We'll use the stage-input region to pass the base along to the shader.
 			// Hopefully Metal won't complain that we didn't set up a stage-input descriptor.
-			[mtlEncoder setStageInRegion: _mtlThreadgroupCount];
+			[mtlEncoder setStageInRegion: mtlThreadgroupCount];
 		} else {
 			// We have to pass the base group in a buffer.
-			unsigned int base[3] = {(uint32_t)_mtlThreadgroupCount.origin.x, (uint32_t)_mtlThreadgroupCount.origin.y, (uint32_t)_mtlThreadgroupCount.origin.z};
+			uint32_t base[3] = {(uint32_t)mtlThreadgroupCount.origin.x, (uint32_t)mtlThreadgroupCount.origin.y, (uint32_t)mtlThreadgroupCount.origin.z};
 			cmdEncoder->setComputeBytes(mtlEncoder, base, sizeof(base), pipeline->getIndirectParamsIndex().stages[kMVKShaderStageCompute]);
 		}
 	}
-	[mtlEncoder dispatchThreadgroups: _mtlThreadgroupCount.size
+	[mtlEncoder dispatchThreadgroups: mtlThreadgroupCount.size
 			   threadsPerThreadgroup: cmdEncoder->_mtlThreadgroupSize];
 }
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
index 4039a63..8d453fe 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.h
@@ -139,7 +139,7 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	id<MTLBuffer> _mtlIndirectBuffer;
-	NSUInteger _mtlIndirectBufferOffset;
+	VkDeviceSize _mtlIndirectBufferOffset;
 	uint32_t _mtlIndirectBufferStride;
 	uint32_t _drawCount;
 };
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
index 78bc7e0..330c21e 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
@@ -125,7 +125,7 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	uint32_t _firstViewport;
-	MVKVectorInline<MTLViewport, kMVKCachedViewportScissorCount> _mtlViewports;
+	MVKVectorInline<VkViewport, kMVKCachedViewportScissorCount> _viewports;
 };
 
 
@@ -147,7 +147,7 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	uint32_t _firstScissor;
-	MVKVectorInline<MTLScissorRect, kMVKCachedViewportScissorCount> _mtlScissors;
+	MVKVectorInline<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
index d8b4925..d4a58a1 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
@@ -124,17 +124,17 @@
 									   uint32_t viewportCount,
 									   const VkViewport* pViewports) {
 	_firstViewport = firstViewport;
-	_mtlViewports.clear();	// Clear for reuse
-	_mtlViewports.reserve(viewportCount);
-	for (uint32_t i = 0; i < viewportCount; i++) {
-		_mtlViewports.push_back(mvkMTLViewportFromVkViewport(pViewports[i]));
+	_viewports.clear();	// Clear for reuse
+	_viewports.reserve(viewportCount);
+	for (uint32_t vpIdx = 0; vpIdx < viewportCount; vpIdx++) {
+		_viewports.push_back(pViewports[vpIdx]);
 	}
 
 	return VK_SUCCESS;
 }
 
 void MVKCmdSetViewport::encode(MVKCommandEncoder* cmdEncoder) {
-    cmdEncoder->_viewportState.setViewports(_mtlViewports, _firstViewport, true);
+    cmdEncoder->_viewportState.setViewports(_viewports, _firstViewport, true);
 }
 
 
@@ -148,17 +148,17 @@
 									  uint32_t scissorCount,
 									  const VkRect2D* pScissors) {
 	_firstScissor = firstScissor;
-	_mtlScissors.clear();	// Clear for reuse
-	_mtlScissors.reserve(scissorCount);
-	for (uint32_t i = 0; i < scissorCount; i++) {
-		_mtlScissors.push_back(mvkMTLScissorRectFromVkRect2D(pScissors[i]));
+	_scissors.clear();	// Clear for reuse
+	_scissors.reserve(scissorCount);
+	for (uint32_t sIdx = 0; sIdx < scissorCount; sIdx++) {
+		_scissors.push_back(pScissors[sIdx]);
 	}
 
 	return VK_SUCCESS;
 }
 
 void MVKCmdSetScissor::encode(MVKCommandEncoder* cmdEncoder) {
-    cmdEncoder->_scissorState.setScissors(_mtlScissors, _firstScissor, true);
+    cmdEncoder->_scissorState.setScissors(_scissors, _firstScissor, true);
 }
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
index 1f73921..66e316a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.h
@@ -64,8 +64,6 @@
 	VkImageLayout _srcLayout;
 	MVKImage* _dstImage;
 	VkImageLayout _dstLayout;
-	MTLPixelFormat _srcMTLPixFmt;
-	MTLPixelFormat _dstMTLPixFmt;
 	uint32_t _srcSampleCount;
 	uint32_t _dstSampleCount;
 	bool _isSrcCompressed;
@@ -131,8 +129,8 @@
 
 /** Describes Metal texture resolve parameters. */
 typedef struct {
-    NSUInteger	level;
-    NSUInteger	slice;
+    uint32_t	level;
+    uint32_t	slice;
 } MVKMetalResolveSlice;
 
 /** Vulkan command to resolve image regions. */
@@ -282,9 +280,7 @@
     MVKImage* _image;
     VkImageLayout _imgLayout;
 	MVKVectorInline<VkImageSubresourceRange, 4> _subresourceRanges;
-	MTLClearColor _mtlColorClearValue;
-	double _mtlDepthClearValue;
-    uint32_t _mtlStencilClearValue;
+	VkClearValue _clearValue;
     bool _isDepthStencilClear;
 };
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index 5af4cae..604887d 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -88,23 +88,23 @@
 
 	_srcImage = (MVKImage*)srcImage;
 	_srcLayout = srcImageLayout;
-	_srcMTLPixFmt = _srcImage->getMTLPixelFormat();
 	_srcSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_srcImage->getSampleCount());
 	_isSrcCompressed = _srcImage->getIsCompressed();
-	uint32_t srcBytesPerBlock = pixFmts->getMTLPixelFormatBytesPerBlock(_srcMTLPixFmt);
+	MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat();
+	uint32_t srcBytesPerBlock = pixFmts->getMTLPixelFormatBytesPerBlock(srcMTLPixFmt);
 
 	_dstImage = (MVKImage*)dstImage;
 	_dstLayout = dstImageLayout;
-	_dstMTLPixFmt = _dstImage->getMTLPixelFormat();
 	_dstSampleCount = mvkSampleCountFromVkSampleCountFlagBits(_dstImage->getSampleCount());
 	_isDstCompressed = _dstImage->getIsCompressed();
-	uint32_t dstBytesPerBlock = pixFmts->getMTLPixelFormatBytesPerBlock(_dstMTLPixFmt);
+	MTLPixelFormat dstMTLPixFmt = _dstImage->getMTLPixelFormat();
+	uint32_t dstBytesPerBlock = pixFmts->getMTLPixelFormatBytesPerBlock(dstMTLPixFmt);
 
 	_canCopyFormats = (_dstSampleCount == _srcSampleCount) && (formatsMustMatch
-																? (_dstMTLPixFmt == _srcMTLPixFmt)
+																? (dstMTLPixFmt == srcMTLPixFmt)
 																: (dstBytesPerBlock == srcBytesPerBlock));
 
-	_useTempBuffer = (_srcMTLPixFmt != _dstMTLPixFmt) && (_isSrcCompressed || _isDstCompressed);	// Different formats and at least one is compressed
+	_useTempBuffer = (srcMTLPixFmt != dstMTLPixFmt) && (_isSrcCompressed || _isDstCompressed);	// Different formats and at least one is compressed
 
 	_commandUse = commandUse;
 	_tmpBuffSize = 0;
@@ -141,9 +141,10 @@
 	// Extent is provided in source texels. If the source is compressed but the
 	// destination is not, each destination pixel will consume an entire source block,
 	// so we must downscale the destination extent by the size of the source block.
+	MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat();
 	VkExtent3D dstExtent = region.extent;
 	if (_isSrcCompressed && !_isDstCompressed) {
-		VkExtent2D srcBlockExtent = pixFmts->getMTLPixelFormatBlockTexelSize(_srcMTLPixFmt);
+		VkExtent2D srcBlockExtent = pixFmts->getMTLPixelFormatBlockTexelSize(srcMTLPixFmt);
 		dstExtent.width /= srcBlockExtent.width;
 		dstExtent.height /= srcBlockExtent.height;
 	}
@@ -155,8 +156,8 @@
 	buffImgCpy.imageExtent = dstExtent;
 	_dstTmpBuffImgCopies.push_back(buffImgCpy);
 
-	NSUInteger bytesPerRow = pixFmts->getMTLPixelFormatBytesPerRow(_srcMTLPixFmt, region.extent.width);
-	NSUInteger bytesPerRegion = pixFmts->getMTLPixelFormatBytesPerLayer(_srcMTLPixFmt, bytesPerRow, region.extent.height);
+	NSUInteger bytesPerRow = pixFmts->getMTLPixelFormatBytesPerRow(srcMTLPixFmt, region.extent.width);
+	NSUInteger bytesPerRegion = pixFmts->getMTLPixelFormatBytesPerLayer(srcMTLPixFmt, bytesPerRow, region.extent.height);
 	_tmpBuffSize += bytesPerRegion;
 }
 
@@ -164,7 +165,7 @@
 	// Unless we need to use an intermediary buffer copy, map the source pixel format to the
 	// dest pixel format through a texture view on the source texture. If the source and dest
 	// pixel formats are the same, this will simply degenerate to the source texture itself.
-	MTLPixelFormat mapSrcMTLPixFmt = _useTempBuffer ? _srcMTLPixFmt : _dstMTLPixFmt;
+	MTLPixelFormat mapSrcMTLPixFmt = (_useTempBuffer ? _srcImage : _dstImage)->getMTLPixelFormat();
 	id<MTLTexture> srcMTLTex = _srcImage->getMTLTexture(mapSrcMTLPixFmt);
 	id<MTLTexture> dstMTLTex = _dstImage->getMTLTexture();
 	if ( !srcMTLTex || !dstMTLTex ) { return; }
@@ -251,9 +252,9 @@
 
 	VkResult rslt = MVKCmdCopyImage::setContent(cmdBuff, srcImage, srcImageLayout, dstImage, dstImageLayout, true, commandUse);
 
-	_blitKey.srcMTLPixelFormat = _srcMTLPixFmt;
+	_blitKey.srcMTLPixelFormat = _srcImage->getMTLPixelFormat();
 	_blitKey.srcMTLTextureType = _srcImage->getMTLTextureType();
-	_blitKey.dstMTLPixelFormat = _dstMTLPixFmt;
+	_blitKey.dstMTLPixelFormat = _dstImage->getMTLPixelFormat();
 	_blitKey.srcFilter = mvkMTLSamplerMinMagFilterFromVkFilter(filter);
 	_blitKey.dstSampleCount = _dstSampleCount;
 
@@ -265,9 +266,10 @@
 	}
 
 	// Validate
+	MTLPixelFormat srcMTLPixFmt = _srcImage->getMTLPixelFormat();
 	if ( !_mvkImageBlitRenders.empty() &&
-		(pixFmts->mtlPixelFormatIsDepthFormat(_srcMTLPixFmt) ||
-		 pixFmts->mtlPixelFormatIsStencilFormat(_srcMTLPixFmt)) ) {
+		(pixFmts->mtlPixelFormatIsDepthFormat(srcMTLPixFmt) ||
+		 pixFmts->mtlPixelFormatIsStencilFormat(srcMTLPixFmt)) ) {
 
 		_mvkImageBlitRenders.clear();
 		return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdBlitImage(): Scaling or inverting depth/stencil images is not supported.");
@@ -1118,13 +1120,9 @@
 									  bool isDepthStencilClear) {
     _image = (MVKImage*)image;
     _imgLayout = imageLayout;
+	_clearValue = clearValue;
     _isDepthStencilClear = isDepthStencilClear;
 
-	MVKPixelFormats* pixFmts = cmdBuff->getPixelFormats();
-	_mtlColorClearValue = pixFmts->getMTLClearColorFromVkClearValue(clearValue, _image->getVkFormat());
-	_mtlDepthClearValue = pixFmts->getMTLClearDepthFromVkClearValue(clearValue);
-	_mtlStencilClearValue = pixFmts->getMTLClearStencilFromVkClearValue(clearValue);
-
     // Add subresource ranges
     _subresourceRanges.clear();		// Clear for reuse
     _subresourceRanges.reserve(rangeCount);
@@ -1136,10 +1134,10 @@
 	if (_image->getImageType() == VK_IMAGE_TYPE_1D) {
 		return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClearImage(): Native 1D images cannot be cleared on this device. Consider enabling MVK_CONFIG_TEXTURE_1D_AS_2D.");
 	}
-	MVKMTLFmtCaps mtlFmtCaps = pixFmts->getMTLPixelFormatCapabilities(_image->getMTLPixelFormat());
+	MVKMTLFmtCaps mtlFmtCaps = cmdBuff->getPixelFormats()->getMTLPixelFormatCapabilities(_image->getMTLPixelFormat());
 	if ((_isDepthStencilClear && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsDSAtt)) ||
 		( !_isDepthStencilClear && !mvkAreAllFlagsEnabled(mtlFmtCaps, kMVKMTLFmtCapsColorAtt))) {
-		return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClearImage(): Format %s cannot be cleared on this device.", pixFmts->getVkFormatName(_image->getVkFormat()));
+		return reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCmdClearImage(): Format %s cannot be cleared on this device.", cmdBuff->getPixelFormats()->getVkFormatName(_image->getVkFormat()));
 	}
 
 	return VK_SUCCESS;
@@ -1155,6 +1153,7 @@
 
 	cmdEncoder->endCurrentMetalEncoding();
 
+	MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats();
 	for (auto& srRange : _subresourceRanges) {
 
 		MTLRenderPassDescriptor* mtlRPDesc = [MTLRenderPassDescriptor renderPassDescriptor];
@@ -1171,7 +1170,7 @@
 			mtlRPCADesc.texture = imgMTLTex;
 			mtlRPCADesc.loadAction = MTLLoadActionClear;
 			mtlRPCADesc.storeAction = MTLStoreActionStore;
-			mtlRPCADesc.clearColor = _mtlColorClearValue;
+			mtlRPCADesc.clearColor = pixFmts->getMTLClearColorFromVkClearValue(_clearValue, _image->getVkFormat());
 		}
 
 		if (isClearingDepth) {
@@ -1179,7 +1178,7 @@
 			mtlRPDADesc.texture = imgMTLTex;
 			mtlRPDADesc.loadAction = MTLLoadActionClear;
 			mtlRPDADesc.storeAction = MTLStoreActionStore;
-			mtlRPDADesc.clearDepth = _mtlDepthClearValue;
+			mtlRPDADesc.clearDepth = pixFmts->getMTLClearDepthFromVkClearValue(_clearValue);
 		}
 
 		if (isClearingStencil) {
@@ -1187,7 +1186,7 @@
 			mtlRPSADesc.texture = imgMTLTex;
 			mtlRPSADesc.loadAction = MTLLoadActionClear;
 			mtlRPSADesc.storeAction = MTLStoreActionStore;
-			mtlRPSADesc.clearStencil = _mtlStencilClearValue;
+			mtlRPSADesc.clearStencil = pixFmts->getMTLClearStencilFromVkClearValue(_clearValue);
 		}
 
         // Extract the mipmap levels that are to be updated
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 90ecccf..8b12e05 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -295,7 +295,7 @@
     bool supportsDynamicState(VkDynamicState state);
 
 	/** Clips the scissor to ensure it fits inside the render area.  */
-	MTLScissorRect clipToRenderArea(MTLScissorRect mtlScissor);
+	VkRect2D clipToRenderArea(VkRect2D scissor);
 
 	/** Called by each graphics draw command to establish any outstanding state just prior to performing the draw. */
 	void finalizeDrawState(MVKGraphicsStage stage);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index ec6dbae..27af9e8 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -373,19 +373,19 @@
     return !gpl || gpl->supportsDynamicState(state);
 }
 
-MTLScissorRect MVKCommandEncoder::clipToRenderArea(MTLScissorRect mtlScissor) {
+VkRect2D MVKCommandEncoder::clipToRenderArea(VkRect2D scissor) {
 
-	NSUInteger raLeft = _renderArea.offset.x;
-	NSUInteger raRight = raLeft + _renderArea.extent.width;
-	NSUInteger raBottom = _renderArea.offset.y;
-	NSUInteger raTop = raBottom + _renderArea.extent.height;
+	int32_t raLeft = _renderArea.offset.x;
+	int32_t raRight = raLeft + _renderArea.extent.width;
+	int32_t raBottom = _renderArea.offset.y;
+	int32_t raTop = raBottom + _renderArea.extent.height;
 
-	mtlScissor.x		= mvkClamp(mtlScissor.x, raLeft, max(raRight - 1, raLeft));
-	mtlScissor.y		= mvkClamp(mtlScissor.y, raBottom, max(raTop - 1, raBottom));
-	mtlScissor.width	= min(mtlScissor.width, raRight - mtlScissor.x);
-	mtlScissor.height	= min(mtlScissor.height, raTop - mtlScissor.y);
+	scissor.offset.x		= mvkClamp(scissor.offset.x, raLeft, max(raRight - 1, raLeft));
+	scissor.offset.y		= mvkClamp(scissor.offset.y, raBottom, max(raTop - 1, raBottom));
+	scissor.extent.width	= min<int32_t>(scissor.extent.width, raRight - scissor.offset.x);
+	scissor.extent.height	= min<int32_t>(scissor.extent.height, raTop - scissor.offset.y);
 
-	return mtlScissor;
+	return scissor;
 }
 
 void MVKCommandEncoder::finalizeDrawState(MVKGraphicsStage stage) {
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index eec378c..0464e7f 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -140,7 +140,7 @@
 	 * The isSettingDynamically indicates that the scissor is being changed dynamically,
 	 * which is only allowed if the pipeline was created as VK_DYNAMIC_STATE_SCISSOR.
 	 */
-	void setViewports(const MVKVector<MTLViewport> &mtlViewports,
+	void setViewports(const MVKVector<VkViewport> &viewports,
 					  uint32_t firstViewport,
 					  bool isSettingDynamically);
 
@@ -152,7 +152,7 @@
     void encodeImpl(uint32_t stage) override;
     void resetImpl() override;
 
-    MVKVectorInline<MTLViewport, kMVKCachedViewportScissorCount> _mtlViewports, _mtlDynamicViewports;
+    MVKVectorInline<VkViewport, kMVKCachedViewportScissorCount> _viewports, _dynamicViewports;
 };
 
 
@@ -169,7 +169,7 @@
 	 * The isSettingDynamically indicates that the scissor is being changed dynamically,
 	 * which is only allowed if the pipeline was created as VK_DYNAMIC_STATE_SCISSOR.
 	 */
-	void setScissors(const MVKVector<MTLScissorRect> &mtlScissors,
+	void setScissors(const MVKVector<VkRect2D> &scissors,
 					 uint32_t firstScissor,
 					 bool isSettingDynamically);
 
@@ -181,7 +181,7 @@
     void encodeImpl(uint32_t stage) override;
     void resetImpl() override;
 
-    MVKVectorInline<MTLScissorRect, kMVKCachedViewportScissorCount> _mtlScissors, _mtlDynamicScissors;
+    MVKVectorInline<VkRect2D, kMVKCachedViewportScissorCount> _scissors, _dynamicScissors;
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 03c812b..2940c9a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -58,98 +58,106 @@
 #pragma mark -
 #pragma mark MVKViewportCommandEncoderState
 
-void MVKViewportCommandEncoderState::setViewports(const MVKVector<MTLViewport> &mtlViewports,
+void MVKViewportCommandEncoderState::setViewports(const MVKVector<VkViewport> &viewports,
 												  uint32_t firstViewport,
 												  bool isSettingDynamically) {
 
+	size_t vpCnt = viewports.size();
 	uint32_t maxViewports = _cmdEncoder->getDevice()->_pProperties->limits.maxViewports;
-	if ((firstViewport + mtlViewports.size() > maxViewports) ||
+	if ((firstViewport + vpCnt > maxViewports) ||
 		(firstViewport >= maxViewports) ||
-		(isSettingDynamically && mtlViewports.size() == 0))
+		(isSettingDynamically && vpCnt == 0))
 		return;
 
-	auto& usingMTLViewports = isSettingDynamically ? _mtlDynamicViewports : _mtlViewports;
+	auto& usingViewports = isSettingDynamically ? _dynamicViewports : _viewports;
 
-	if (firstViewport + mtlViewports.size() > usingMTLViewports.size()) {
-		usingMTLViewports.resize(firstViewport + mtlViewports.size());
+	if (firstViewport + vpCnt > usingViewports.size()) {
+		usingViewports.resize(firstViewport + vpCnt);
 	}
 
 	bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_VIEWPORT);
-
-	if (isSettingDynamically ||
-		(!mustSetDynamically && mtlViewports.size() > 0))
-		std::copy(mtlViewports.begin(), mtlViewports.end(), usingMTLViewports.begin() + firstViewport);
-	else
-		usingMTLViewports.clear();
+	if (isSettingDynamically || (!mustSetDynamically && vpCnt > 0)) {
+		std::copy(viewports.begin(), viewports.end(), usingViewports.begin() + firstViewport);
+	} else {
+		usingViewports.clear();
+	}
 
 	markDirty();
 }
 
 void MVKViewportCommandEncoderState::encodeImpl(uint32_t stage) {
     if (stage != kMVKGraphicsStageRasterization) { return; }
-	auto& usingMTLViewports = _mtlViewports.size() > 0 ? _mtlViewports : _mtlDynamicViewports;
-	if (usingMTLViewports.empty()) { return; }
+	auto& usingViewports = _viewports.size() > 0 ? _viewports : _dynamicViewports;
+	if (usingViewports.empty()) { return; }
+
     if (_cmdEncoder->_pDeviceFeatures->multiViewport) {
-        [_cmdEncoder->_mtlRenderEncoder setViewports: &usingMTLViewports[0] count: usingMTLViewports.size()];
+		size_t vpCnt = usingViewports.size();
+		MTLViewport mtlViewports[vpCnt];
+		for (uint32_t vpIdx = 0; vpIdx < vpCnt; vpIdx++) {
+			mtlViewports[vpIdx] = mvkMTLViewportFromVkViewport(usingViewports[vpIdx]);
+		}
+        [_cmdEncoder->_mtlRenderEncoder setViewports: mtlViewports count: vpCnt];
     } else {
-        [_cmdEncoder->_mtlRenderEncoder setViewport: usingMTLViewports[0]];
+        [_cmdEncoder->_mtlRenderEncoder setViewport: mvkMTLViewportFromVkViewport(usingViewports[0])];
     }
 }
 
 void MVKViewportCommandEncoderState::resetImpl() {
-    _mtlViewports.clear();
-	_mtlDynamicViewports.clear();
+    _viewports.clear();
+	_dynamicViewports.clear();
 }
 
 
 #pragma mark -
 #pragma mark MVKScissorCommandEncoderState
 
-void MVKScissorCommandEncoderState::setScissors(const MVKVector<MTLScissorRect> &mtlScissors,
+void MVKScissorCommandEncoderState::setScissors(const MVKVector<VkRect2D> &scissors,
                                                 uint32_t firstScissor,
 												bool isSettingDynamically) {
 
+	size_t sCnt = scissors.size();
 	uint32_t maxScissors = _cmdEncoder->getDevice()->_pProperties->limits.maxViewports;
-	if ((firstScissor + mtlScissors.size() > maxScissors) ||
+	if ((firstScissor + sCnt > maxScissors) ||
 		(firstScissor >= maxScissors) ||
-		(isSettingDynamically && mtlScissors.size() == 0))
+		(isSettingDynamically && sCnt == 0))
 		return;
 
-	auto& usingMTLScissors = isSettingDynamically ? _mtlDynamicScissors : _mtlScissors;
+	auto& usingScissors = isSettingDynamically ? _dynamicScissors : _scissors;
 
-	if (firstScissor + mtlScissors.size() > usingMTLScissors.size()) {
-		usingMTLScissors.resize(firstScissor + mtlScissors.size());
+	if (firstScissor + sCnt > usingScissors.size()) {
+		usingScissors.resize(firstScissor + sCnt);
 	}
 
 	bool mustSetDynamically = _cmdEncoder->supportsDynamicState(VK_DYNAMIC_STATE_SCISSOR);
-
-	if (isSettingDynamically ||
-		(!mustSetDynamically && mtlScissors.size() > 0))
-		std::copy(mtlScissors.begin(), mtlScissors.end(), usingMTLScissors.begin() + firstScissor);
-	else
-		usingMTLScissors.clear();
+	if (isSettingDynamically || (!mustSetDynamically && sCnt > 0)) {
+		std::copy(scissors.begin(), scissors.end(), usingScissors.begin() + firstScissor);
+	} else {
+		usingScissors.clear();
+	}
 
 	markDirty();
 }
 
 void MVKScissorCommandEncoderState::encodeImpl(uint32_t stage) {
 	if (stage != kMVKGraphicsStageRasterization) { return; }
-	auto& usingMTLScissors = _mtlScissors.size() > 0 ? _mtlScissors : _mtlDynamicScissors;
-	if (usingMTLScissors.empty()) { return; }
-	auto clippedScissors(usingMTLScissors);
-	std::for_each(clippedScissors.begin(), clippedScissors.end(), [this](MTLScissorRect& scissor) {
-		scissor = _cmdEncoder->clipToRenderArea(scissor);
-	});
+	auto& usingScissors = _scissors.size() > 0 ? _scissors : _dynamicScissors;
+	if (usingScissors.empty()) { return; }
+
 	if (_cmdEncoder->_pDeviceFeatures->multiViewport) {
-		[_cmdEncoder->_mtlRenderEncoder setScissorRects: &clippedScissors[0] count: clippedScissors.size()];
+		size_t sCnt = usingScissors.size();
+		MTLScissorRect mtlScissors[sCnt];
+		for (uint32_t sIdx = 0; sIdx < sCnt; sIdx++) {
+			mtlScissors[sIdx] = mvkMTLScissorRectFromVkRect2D(_cmdEncoder->clipToRenderArea(usingScissors[sIdx]));
+		}
+		[_cmdEncoder->_mtlRenderEncoder setScissorRects: mtlScissors count: sCnt];
 	} else {
-		[_cmdEncoder->_mtlRenderEncoder setScissorRect: clippedScissors[0]];
+		[_cmdEncoder->_mtlRenderEncoder setScissorRect: mvkMTLScissorRectFromVkRect2D(_cmdEncoder->clipToRenderArea(usingScissors[0]))];
 	}
 }
 
 void MVKScissorCommandEncoderState::resetImpl() {
-    _mtlScissors.clear();
-	_mtlDynamicScissors.clear();
+    _scissors.clear();
+	_dynamicScissors.clear();
 }
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 8bc35c8..431f02f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -269,8 +269,8 @@
 	VkPipelineRasterizationStateCreateInfo _rasterInfo;
 	VkPipelineDepthStencilStateCreateInfo _depthStencilInfo;
 
-	MVKVectorInline<MTLViewport, kMVKCachedViewportScissorCount> _mtlViewports;
-	MVKVectorInline<MTLScissorRect, kMVKCachedViewportScissorCount> _mtlScissors;
+	MVKVectorInline<VkViewport, kMVKCachedViewportScissorCount> _viewports;
+	MVKVectorInline<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
 
 	MTLComputePipelineDescriptor* _mtlTessControlStageDesc = nil;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 6fe1bb2..ab6f1e8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -254,8 +254,8 @@
             cmdEncoder->_blendColorState.setBlendColor(_blendConstants[0], _blendConstants[1],
                                                        _blendConstants[2], _blendConstants[3], false);
             cmdEncoder->_depthBiasState.setDepthBias(_rasterInfo);
-            cmdEncoder->_viewportState.setViewports(_mtlViewports, 0, false);
-            cmdEncoder->_scissorState.setScissors(_mtlScissors, 0, false);
+            cmdEncoder->_viewportState.setViewports(_viewports, 0, false);
+            cmdEncoder->_scissorState.setScissors(_scissors, 0, false);
             cmdEncoder->_mtlPrimitiveType = _mtlPrimitiveType;
 
             [mtlCmdEnc setCullMode: _mtlCullMode];
@@ -376,24 +376,24 @@
 	_hasDepthStencilInfo = mvkSetOrClear(&_depthStencilInfo, pCreateInfo->pDepthStencilState);
 
 	// Viewports and scissors
-	if (pCreateInfo->pViewportState) {
-		_mtlViewports.reserve(pCreateInfo->pViewportState->viewportCount);
-		for (uint32_t i = 0; i < pCreateInfo->pViewportState->viewportCount; i++) {
+	auto pVPState = pCreateInfo->pViewportState;
+	if (pVPState) {
+		uint32_t vpCnt = pVPState->viewportCount;
+		_viewports.reserve(vpCnt);
+		for (uint32_t vpIdx = 0; vpIdx < vpCnt; vpIdx++) {
 			// If viewport is dyanamic, we still add a dummy so that the count will be tracked.
-			MTLViewport mtlVP;
-			if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_VIEWPORT] ) {
-				mtlVP = mvkMTLViewportFromVkViewport(pCreateInfo->pViewportState->pViewports[i]);
-			}
-			_mtlViewports.push_back(mtlVP);
+			VkViewport vp;
+			if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_VIEWPORT] ) { vp = pVPState->pViewports[vpIdx]; }
+			_viewports.push_back(vp);
 		}
-		_mtlScissors.reserve(pCreateInfo->pViewportState->scissorCount);
-		for (uint32_t i = 0; i < pCreateInfo->pViewportState->scissorCount; i++) {
+
+		uint32_t sCnt = pVPState->scissorCount;
+		_scissors.reserve(sCnt);
+		for (uint32_t sIdx = 0; sIdx < sCnt; sIdx++) {
 			// If scissor is dyanamic, we still add a dummy so that the count will be tracked.
-			MTLScissorRect mtlSc;
-			if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_SCISSOR] ) {
-				mtlSc = mvkMTLScissorRectFromVkRect2D(pCreateInfo->pViewportState->pScissors[i]);
-			}
-			_mtlScissors.push_back(mtlSc);
+			VkRect2D sc;
+			if ( !_dynamicStateEnabled[VK_DYNAMIC_STATE_SCISSOR] ) { sc = pVPState->pScissors[sIdx]; }
+			_scissors.push_back(sc);
 		}
 	}
 }