Merge pull request #869 from billhollings/master

Reduce common memory requirements of MVKCmdSetViewport and MVKCmdSetScissor.
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index e3edc7f..cf3c94c 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -401,6 +401,7 @@
 		A9B51BD2225E986A00AC74D2 /* MVKOSExtensions.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKOSExtensions.mm; sourceTree = "<group>"; };
 		A9B51BD6225E986A00AC74D2 /* MVKOSExtensions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKOSExtensions.h; sourceTree = "<group>"; };
 		A9B8EE0A1A98D796009C5A02 /* libMoltenVK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMoltenVK.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		A9C83DCD24533E22003E5261 /* MVKCommandTypePools.def */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.h; fileEncoding = 4; path = MVKCommandTypePools.def; sourceTree = "<group>"; };
 		A9C86CB61C55B8350096CAF2 /* MoltenVKShaderConverter.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = MoltenVKShaderConverter.xcodeproj; path = ../MoltenVKShaderConverter/MoltenVKShaderConverter.xcodeproj; sourceTree = "<group>"; };
 		A9C96DCE1DDC20C20053187F /* MVKMTLBufferAllocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKMTLBufferAllocation.h; sourceTree = "<group>"; };
 		A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKMTLBufferAllocation.mm; sourceTree = "<group>"; };
@@ -479,6 +480,7 @@
 				A94FB77B1C7DFB4800632CA3 /* MVKCommandPool.mm */,
 				A95870F61C90D29F009EB096 /* MVKCommandResourceFactory.h */,
 				A95870F71C90D29F009EB096 /* MVKCommandResourceFactory.mm */,
+				A9C83DCD24533E22003E5261 /* MVKCommandTypePools.def */,
 				A9C96DCE1DDC20C20053187F /* MVKMTLBufferAllocation.h */,
 				A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */,
 				A9E4B7881E1D8AF10046A4CE /* MVKMTLResourceBindings.h */,
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
index b05a43e..1694441 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
@@ -41,8 +41,6 @@
 #pragma mark -
 #pragma mark MVKCmdDebugMarkerBegin
 
-MVKFuncionOverride_getTypePool(DebugMarkerBegin)
-
 // Vulkan debug groups are more general than Metal's.
 // If a renderpass is active, push on the render command encoder, otherwise push on the command buffer.
 void MVKCmdDebugMarkerBegin::encode(MVKCommandEncoder* cmdEncoder) {
@@ -58,8 +56,6 @@
 #pragma mark -
 #pragma mark MVKCmdDebugMarkerEnd
 
-MVKFuncionOverride_getTypePool(DebugMarkerEnd)
-
 VkResult MVKCmdDebugMarkerEnd::setContent(MVKCommandBuffer* cmdBuff) { return VK_SUCCESS; }
 
 // Vulkan debug groups are more general than Metal's.
@@ -77,8 +73,6 @@
 #pragma mark -
 #pragma mark MVKCmdDebugMarkerInsert
 
-MVKFuncionOverride_getTypePool(DebugMarkerInsert)
-
 void MVKCmdDebugMarkerInsert::encode(MVKCommandEncoder* cmdEncoder) {
 	[cmdEncoder->getMTLEncoder() insertDebugSignpost: _markerName];
 }
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
index 4a4fc48..6ae55f1 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDispatch.mm
@@ -28,8 +28,6 @@
 #pragma mark -
 #pragma mark MVKCmdDispatch
 
-MVKFuncionOverride_getTypePool(Dispatch)
-
 VkResult MVKCmdDispatch::setContent(MVKCommandBuffer* cmdBuff,
 									uint32_t baseGroupX, uint32_t baseGroupY, uint32_t baseGroupZ,
 									uint32_t groupCountX, uint32_t groupCountY, uint32_t groupCountZ) {
@@ -70,8 +68,6 @@
 #pragma mark -
 #pragma mark MVKCmdDispatchIndirect
 
-MVKFuncionOverride_getTypePool(DispatchIndirect)
-
 VkResult MVKCmdDispatchIndirect::setContent(MVKCommandBuffer* cmdBuff, VkBuffer buffer, VkDeviceSize offset) {
 	MVKBuffer* mvkBuffer = (MVKBuffer*)buffer;
 	_mtlIndirectBuffer = mvkBuffer->getMTLBuffer();
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
index df5de95..e8fbe51 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDraw.mm
@@ -28,8 +28,6 @@
 #pragma mark -
 #pragma mark MVKCmdBindVertexBuffers
 
-MVKFuncionOverride_getTypePool(BindVertexBuffers)
-
 VkResult MVKCmdBindVertexBuffers::setContent(MVKCommandBuffer* cmdBuff,
 											 uint32_t startBinding,
 											 uint32_t bindingCount,
@@ -59,8 +57,6 @@
 #pragma mark -
 #pragma mark MVKCmdBindIndexBuffer
 
-MVKFuncionOverride_getTypePool(BindIndexBuffer)
-
 VkResult MVKCmdBindIndexBuffer::setContent(MVKCommandBuffer* cmdBuff,
 										   VkBuffer buffer,
 										   VkDeviceSize offset,
@@ -81,8 +77,6 @@
 #pragma mark -
 #pragma mark MVKCmdDraw
 
-MVKFuncionOverride_getTypePool(Draw)
-
 VkResult MVKCmdDraw::setContent(MVKCommandBuffer* cmdBuff,
 								uint32_t vertexCount,
 								uint32_t instanceCount,
@@ -271,8 +265,6 @@
 #pragma mark -
 #pragma mark MVKCmdDrawIndexed
 
-MVKFuncionOverride_getTypePool(DrawIndexed)
-
 VkResult MVKCmdDrawIndexed::setContent(MVKCommandBuffer* cmdBuff,
 									   uint32_t indexCount,
 									   uint32_t instanceCount,
@@ -508,8 +500,6 @@
 #pragma mark -
 #pragma mark MVKCmdDrawIndirect
 
-MVKFuncionOverride_getTypePool(DrawIndirect)
-
 VkResult MVKCmdDrawIndirect::setContent(MVKCommandBuffer* cmdBuff,
 										VkBuffer buffer,
 										VkDeviceSize offset,
@@ -754,8 +744,6 @@
 #pragma mark -
 #pragma mark MVKCmdDrawIndexedIndirect
 
-MVKFuncionOverride_getTypePool(DrawIndexedIndirect)
-
 VkResult MVKCmdDrawIndexedIndirect::setContent(MVKCommandBuffer* cmdBuff,
 											   VkBuffer buffer,
 											   VkDeviceSize offset,
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index e93e65f..01b8daf 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -30,8 +30,6 @@
 #pragma mark -
 #pragma mark MVKCmdPipelineBarrier
 
-MVKFuncionOverride_getTypePool(PipelineBarrier)
-
 VkResult MVKCmdPipelineBarrier::setContent(MVKCommandBuffer* cmdBuff,
 										   VkPipelineStageFlags srcStageMask,
 										   VkPipelineStageFlags dstStageMask,
@@ -130,8 +128,6 @@
 #pragma mark -
 #pragma mark MVKCmdBindPipeline
 
-MVKFuncionOverride_getTypePool(BindPipeline)
-
 VkResult MVKCmdBindPipeline::setContent(MVKCommandBuffer* cmdBuff,
 										VkPipelineBindPoint pipelineBindPoint,
 										VkPipeline pipeline) {
@@ -158,8 +154,6 @@
 #pragma mark -
 #pragma mark MVKCmdBindDescriptorSets
 
-MVKFuncionOverride_getTypePool(BindDescriptorSets)
-
 VkResult MVKCmdBindDescriptorSets::setContent(MVKCommandBuffer* cmdBuff,
 											  VkPipelineBindPoint pipelineBindPoint,
 											  VkPipelineLayout layout,
@@ -197,8 +191,6 @@
 #pragma mark -
 #pragma mark MVKCmdPushConstants
 
-MVKFuncionOverride_getTypePool(PushConstants)
-
 VkResult MVKCmdPushConstants::setContent(MVKCommandBuffer* cmdBuff,
 										 VkPipelineLayout layout,
 										 VkShaderStageFlags stageFlags,
@@ -234,8 +226,6 @@
 #pragma mark -
 #pragma mark MVKCmdPushDescriptorSet
 
-MVKFuncionOverride_getTypePool(PushDescriptorSet)
-
 VkResult MVKCmdPushDescriptorSet::setContent(MVKCommandBuffer* cmdBuff,
 											 VkPipelineBindPoint pipelineBindPoint,
 											 VkPipelineLayout layout,
@@ -330,8 +320,6 @@
 #pragma mark -
 #pragma mark MVKCmdPushDescriptorSetWithTemplate
 
-MVKFuncionOverride_getTypePool(PushDescriptorSetWithTemplate)
-
 VkResult MVKCmdPushDescriptorSetWithTemplate::setContent(MVKCommandBuffer* cmdBuff,
 														 VkDescriptorUpdateTemplateKHR descUpdateTemplate,
 														 VkPipelineLayout layout,
@@ -394,8 +382,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetResetEvent
 
-MVKFuncionOverride_getTypePool(SetResetEvent)
-
 VkResult MVKCmdSetResetEvent::setContent(MVKCommandBuffer* cmdBuff,
 										 VkEvent event,
 										 VkPipelineStageFlags stageMask,
@@ -414,8 +400,6 @@
 #pragma mark -
 #pragma mark MVKCmdWaitEvents
 
-MVKFuncionOverride_getTypePool(WaitEvents)
-
 VkResult MVKCmdWaitEvents::setContent(MVKCommandBuffer* cmdBuff,
 									  uint32_t eventCount,
 									  const VkEvent* pEvents,
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm
index 7fa703a..f5360ac 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdQueries.mm
@@ -38,8 +38,6 @@
 #pragma mark -
 #pragma mark MVKCmdBeginQuery
 
-MVKFuncionOverride_getTypePool(BeginQuery)
-
 VkResult MVKCmdBeginQuery::setContent(MVKCommandBuffer* cmdBuff,
 									  VkQueryPool queryPool,
 									  uint32_t query,
@@ -61,8 +59,6 @@
 #pragma mark -
 #pragma mark MVKCmdEndQuery
 
-MVKFuncionOverride_getTypePool(EndQuery)
-
 void MVKCmdEndQuery::encode(MVKCommandEncoder* cmdEncoder) {
     _queryPool->endQuery(_query, cmdEncoder);
 }
@@ -71,8 +67,6 @@
 #pragma mark -
 #pragma mark MVKCmdWriteTimestamp
 
-MVKFuncionOverride_getTypePool(WriteTimestamp)
-
 VkResult MVKCmdWriteTimestamp::setContent(MVKCommandBuffer* cmdBuff,
 										  VkPipelineStageFlagBits pipelineStage,
 										  VkQueryPool queryPool,
@@ -93,8 +87,6 @@
 #pragma mark -
 #pragma mark MVKCmdResetQueryPool
 
-MVKFuncionOverride_getTypePool(ResetQueryPool)
-
 VkResult MVKCmdResetQueryPool::setContent(MVKCommandBuffer* cmdBuff,
 										  VkQueryPool queryPool,
 										  uint32_t firstQuery,
@@ -115,8 +107,6 @@
 #pragma mark -
 #pragma mark MVKCmdCopyQueryPoolResults
 
-MVKFuncionOverride_getTypePool(CopyQueryPoolResults)
-
 VkResult MVKCmdCopyQueryPoolResults::setContent(MVKCommandBuffer* cmdBuff,
 												VkQueryPool queryPool,
 												uint32_t firstQuery,
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
index 330c21e..a41b4b9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.h
@@ -107,10 +107,17 @@
 	MVKVectorInline<MVKCommandBuffer*, 64> _secondaryCommandBuffers;
 };
 
+
 #pragma mark -
 #pragma mark MVKCmdSetViewport
 
-/** Vulkan command to set the viewports. */
+/**
+ * Vulkan command to set the viewports.
+ * This is a template class to support different vector pre-allocations, so we can balance
+ * in-line memory allocation betweeen the very common case of a single viewport, and the
+ * maximal number, by choosing which concrete implementation to use based on viewport count.
+ */
+template <size_t N>
 class MVKCmdSetViewport : public MVKCommand {
 
 public:
@@ -125,14 +132,24 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	uint32_t _firstViewport;
-	MVKVectorInline<VkViewport, kMVKCachedViewportScissorCount> _viewports;
+	MVKVectorInline<VkViewport, N> _viewports;
 };
 
+// Concrete template class implemenations.
+typedef MVKCmdSetViewport<1> MVKCmdSetViewport1;
+typedef MVKCmdSetViewport<kMVKCachedViewportScissorCount> MVKCmdSetViewportMulti;
+
 
 #pragma mark -
 #pragma mark MVKCmdSetScissor
 
-/** Vulkan command to set the scissor rectangles. */
+/**
+ * Vulkan command to set the scissor rectangles.
+ * This is a template class to support different vector pre-allocations, so we can balance
+ * in-line memory allocation betweeen the very common case of a single scissor, and the
+ * maximal number, by choosing which concrete implementation to use based on scissor count.
+ */
+template <size_t N>
 class MVKCmdSetScissor : public MVKCommand {
 
 public:
@@ -147,9 +164,13 @@
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
 	uint32_t _firstScissor;
-	MVKVectorInline<VkRect2D, kMVKCachedViewportScissorCount> _scissors;
+	MVKVectorInline<VkRect2D, N> _scissors;
 };
 
+// Concrete template class implemenations.
+typedef MVKCmdSetScissor<1> MVKCmdSetScissor1;
+typedef MVKCmdSetScissor<kMVKCachedViewportScissorCount> MVKCmdSetScissorMulti;
+
 
 #pragma mark -
 #pragma mark MVKCmdSetLineWidth
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
index d4a58a1..393341f 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
@@ -28,8 +28,6 @@
 #pragma mark -
 #pragma mark MVKCmdBeginRenderPass
 
-MVKFuncionOverride_getTypePool(BeginRenderPass)
-
 VkResult MVKCmdBeginRenderPass::setContent(MVKCommandBuffer* cmdBuff,
 										   const VkRenderPassBeginInfo* pRenderPassBegin,
 										   VkSubpassContents contents) {
@@ -61,8 +59,6 @@
 #pragma mark -
 #pragma mark MVKCmdNextSubpass
 
-MVKFuncionOverride_getTypePool(NextSubpass)
-
 VkResult MVKCmdNextSubpass::setContent(MVKCommandBuffer* cmdBuff,
 									   VkSubpassContents contents) {
 	_contents = contents;
@@ -78,8 +74,6 @@
 #pragma mark -
 #pragma mark MVKCmdEndRenderPass
 
-MVKFuncionOverride_getTypePool(EndRenderPass)
-
 VkResult MVKCmdEndRenderPass::setContent(MVKCommandBuffer* cmdBuff) {
 	cmdBuff->recordEndRenderPass(this);
 	return VK_SUCCESS;
@@ -94,8 +88,6 @@
 #pragma mark -
 #pragma mark MVKCmdExecuteCommands
 
-MVKFuncionOverride_getTypePool(ExecuteCommands)
-
 VkResult MVKCmdExecuteCommands::setContent(MVKCommandBuffer* cmdBuff,
 										   uint32_t commandBuffersCount,
 										   const VkCommandBuffer* pCommandBuffers) {
@@ -117,12 +109,11 @@
 #pragma mark -
 #pragma mark MVKCmdSetViewport
 
-MVKFuncionOverride_getTypePool(SetViewport)
-
-VkResult MVKCmdSetViewport::setContent(MVKCommandBuffer* cmdBuff,
-									   uint32_t firstViewport,
-									   uint32_t viewportCount,
-									   const VkViewport* pViewports) {
+template <size_t N>
+VkResult MVKCmdSetViewport<N>::setContent(MVKCommandBuffer* cmdBuff,
+										  uint32_t firstViewport,
+										  uint32_t viewportCount,
+										  const VkViewport* pViewports) {
 	_firstViewport = firstViewport;
 	_viewports.clear();	// Clear for reuse
 	_viewports.reserve(viewportCount);
@@ -133,20 +124,23 @@
 	return VK_SUCCESS;
 }
 
-void MVKCmdSetViewport::encode(MVKCommandEncoder* cmdEncoder) {
-    cmdEncoder->_viewportState.setViewports(_viewports, _firstViewport, true);
+template <size_t N>
+void MVKCmdSetViewport<N>::encode(MVKCommandEncoder* cmdEncoder) {
+	cmdEncoder->_viewportState.setViewports(_viewports, _firstViewport, true);
 }
 
+template class MVKCmdSetViewport<1>;
+template class MVKCmdSetViewport<kMVKCachedViewportScissorCount>;
+
 
 #pragma mark -
 #pragma mark MVKCmdSetScissor
 
-MVKFuncionOverride_getTypePool(SetScissor)
-
-VkResult MVKCmdSetScissor::setContent(MVKCommandBuffer* cmdBuff,
-									  uint32_t firstScissor,
-									  uint32_t scissorCount,
-									  const VkRect2D* pScissors) {
+template <size_t N>
+VkResult MVKCmdSetScissor<N>::setContent(MVKCommandBuffer* cmdBuff,
+										 uint32_t firstScissor,
+										 uint32_t scissorCount,
+										 const VkRect2D* pScissors) {
 	_firstScissor = firstScissor;
 	_scissors.clear();	// Clear for reuse
 	_scissors.reserve(scissorCount);
@@ -157,16 +151,18 @@
 	return VK_SUCCESS;
 }
 
-void MVKCmdSetScissor::encode(MVKCommandEncoder* cmdEncoder) {
+template <size_t N>
+void MVKCmdSetScissor<N>::encode(MVKCommandEncoder* cmdEncoder) {
     cmdEncoder->_scissorState.setScissors(_scissors, _firstScissor, true);
 }
 
+template class MVKCmdSetScissor<1>;
+template class MVKCmdSetScissor<kMVKCachedViewportScissorCount>;
+
 
 #pragma mark -
 #pragma mark MVKCmdSetLineWidth
 
-MVKFuncionOverride_getTypePool(SetLineWidth)
-
 VkResult MVKCmdSetLineWidth::setContent(MVKCommandBuffer* cmdBuff,
 										float lineWidth) {
     _lineWidth = lineWidth;
@@ -185,8 +181,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetDepthBias
 
-MVKFuncionOverride_getTypePool(SetDepthBias)
-
 VkResult MVKCmdSetDepthBias::setContent(MVKCommandBuffer* cmdBuff,
 										float depthBiasConstantFactor,
 										float depthBiasClamp,
@@ -208,8 +202,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetBlendConstants
 
-MVKFuncionOverride_getTypePool(SetBlendConstants)
-
 VkResult MVKCmdSetBlendConstants::setContent(MVKCommandBuffer* cmdBuff,
 											 const float blendConst[4]) {
     _red = blendConst[0];
@@ -228,8 +220,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetDepthBounds
 
-MVKFuncionOverride_getTypePool(SetDepthBounds)
-
 VkResult MVKCmdSetDepthBounds::setContent(MVKCommandBuffer* cmdBuff,
 										  float minDepthBounds,
 										  float maxDepthBounds) {
@@ -250,8 +240,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetStencilCompareMask
 
-MVKFuncionOverride_getTypePool(SetStencilCompareMask)
-
 VkResult MVKCmdSetStencilCompareMask::setContent(MVKCommandBuffer* cmdBuff,
 												 VkStencilFaceFlags faceMask,
 												 uint32_t stencilCompareMask) {
@@ -269,8 +257,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetStencilWriteMask
 
-MVKFuncionOverride_getTypePool(SetStencilWriteMask)
-
 VkResult MVKCmdSetStencilWriteMask::setContent(MVKCommandBuffer* cmdBuff,
 											   VkStencilFaceFlags faceMask,
 											   uint32_t stencilWriteMask) {
@@ -288,8 +274,6 @@
 #pragma mark -
 #pragma mark MVKCmdSetStencilReference
 
-MVKFuncionOverride_getTypePool(SetStencilReference)
-
 VkResult MVKCmdSetStencilReference::setContent(MVKCommandBuffer* cmdBuff,
 											   VkStencilFaceFlags faceMask,
 											   uint32_t stencilReference) {
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index 37dfa34..cb9f410 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -47,8 +47,6 @@
 #pragma mark -
 #pragma mark MVKCmdCopyImage
 
-MVKFuncionOverride_getTypePool(CopyImage)
-
 VkResult MVKCmdCopyImage::setContent(MVKCommandBuffer* cmdBuff,
 									 VkImage srcImage,
 									 VkImageLayout srcImageLayout,
@@ -238,8 +236,6 @@
 #pragma mark -
 #pragma mark MVKCmdBlitImage
 
-MVKFuncionOverride_getTypePool(BlitImage)
-
 VkResult MVKCmdBlitImage::setContent(MVKCommandBuffer* cmdBuff,
 									 VkImage srcImage,
 									 VkImageLayout srcImageLayout,
@@ -449,8 +445,6 @@
 #pragma mark -
 #pragma mark MVKCmdResolveImage
 
-MVKFuncionOverride_getTypePool(ResolveImage)
-
 VkResult MVKCmdResolveImage::setContent(MVKCommandBuffer* cmdBuff,
 										VkImage srcImage,
 										VkImageLayout srcImageLayout,
@@ -643,8 +637,6 @@
 #pragma mark -
 #pragma mark MVKCmdCopyBuffer
 
-MVKFuncionOverride_getTypePool(CopyBuffer)
-
 // Matches shader struct.
 typedef struct {
 	uint32_t srcOffset;
@@ -715,8 +707,6 @@
 #pragma mark -
 #pragma mark MVKCmdBufferImageCopy
 
-MVKFuncionOverride_getTypePool(BufferImageCopy)
-
 // Matches shader struct.
 typedef struct {
     uint32_t srcRowStride;
@@ -937,8 +927,6 @@
 #pragma mark -
 #pragma mark MVKCmdClearAttachments
 
-MVKFuncionOverride_getTypePool(ClearAttachments)
-
 VkResult MVKCmdClearAttachments::setContent(MVKCommandBuffer* cmdBuff,
 											uint32_t attachmentCount,
 											const VkClearAttachment* pAttachments,
@@ -1107,8 +1095,6 @@
 #pragma mark -
 #pragma mark MVKCmdClearImage
 
-MVKFuncionOverride_getTypePool(ClearImage)
-
 VkResult MVKCmdClearImage::setContent(MVKCommandBuffer* cmdBuff,
 									  VkImage image,
 									  VkImageLayout imageLayout,
@@ -1224,8 +1210,6 @@
 #pragma mark -
 #pragma mark MVKCmdFillBuffer
 
-MVKFuncionOverride_getTypePool(FillBuffer)
-
 VkResult MVKCmdFillBuffer::setContent(MVKCommandBuffer* cmdBuff,
 									  VkBuffer dstBuffer,
 									  VkDeviceSize dstOffset,
@@ -1292,8 +1276,6 @@
 #pragma mark -
 #pragma mark MVKCmdUpdateBuffer
 
-MVKFuncionOverride_getTypePool(UpdateBuffer)
-
 VkResult MVKCmdUpdateBuffer::setContent(MVKCommandBuffer* cmdBuff,
 										VkBuffer dstBuffer,
 										VkDeviceSize dstOffset,
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommand.h b/MoltenVK/MoltenVK/Commands/MVKCommand.h
index 29564e9..d0ef1c3 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommand.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommand.h
@@ -70,13 +70,10 @@
 protected:
 	friend MVKCommandBuffer;
 
+	// Returns the command type pool used by this command, from the command pool.
+	// This function is overridden in each concrete subclass declaration, but the implementation of
+	// this function in each subclass is automatically generated in the MVKCommandPool implementation.
 	virtual MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) = 0;
-
-	// Macro to implement a subclass override of the getTypePool(MVKCommandPool* cmdPool) function.
-#	define MVKFuncionOverride_getTypePool(cmdType)					  							\
-	MVKCommandTypePool<MVKCommand>* MVKCmd ##cmdType ::getTypePool(MVKCommandPool* cmdPool) {	\
-		return (MVKCommandTypePool<MVKCommand>*)&cmdPool->_cmd  ##cmdType ##Pool;				\
-	}
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
index 1c794ba..e14ecee 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
@@ -59,99 +59,10 @@
 	/** Returns the debug report object type of this object. */
 	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT; }
 
-#pragma mark Command type pools
-
-	MVKCommandTypePool<MVKCmdPipelineBarrier> _cmdPipelineBarrierPool;
-
-	MVKCommandTypePool<MVKCmdBindPipeline> _cmdBindPipelinePool;
-
-	MVKCommandTypePool<MVKCmdBeginRenderPass> _cmdBeginRenderPassPool;
-
-	MVKCommandTypePool<MVKCmdNextSubpass> _cmdNextSubpassPool;
-
-	MVKCommandTypePool<MVKCmdEndRenderPass> _cmdEndRenderPassPool;
-
-	MVKCommandTypePool<MVKCmdExecuteCommands> _cmdExecuteCommandsPool;
-
-	MVKCommandTypePool<MVKCmdBindDescriptorSets> _cmdBindDescriptorSetsPool;
-
-	MVKCommandTypePool<MVKCmdSetViewport> _cmdSetViewportPool;
-
-	MVKCommandTypePool<MVKCmdSetScissor> _cmdSetScissorPool;
-
-    MVKCommandTypePool<MVKCmdSetLineWidth> _cmdSetLineWidthPool;
-
-    MVKCommandTypePool<MVKCmdSetDepthBias> _cmdSetDepthBiasPool;
-
-    MVKCommandTypePool<MVKCmdSetBlendConstants> _cmdSetBlendConstantsPool;
-
-    MVKCommandTypePool<MVKCmdSetDepthBounds> _cmdSetDepthBoundsPool;
-
-    MVKCommandTypePool<MVKCmdSetStencilCompareMask> _cmdSetStencilCompareMaskPool;
-
-    MVKCommandTypePool<MVKCmdSetStencilWriteMask> _cmdSetStencilWriteMaskPool;
-
-    MVKCommandTypePool<MVKCmdSetStencilReference> _cmdSetStencilReferencePool;
-
-	MVKCommandTypePool<MVKCmdBindVertexBuffers> _cmdBindVertexBuffersPool;
-
-	MVKCommandTypePool<MVKCmdBindIndexBuffer> _cmdBindIndexBufferPool;
-
-	MVKCommandTypePool<MVKCmdDraw> _cmdDrawPool;
-
-	MVKCommandTypePool<MVKCmdDrawIndexed> _cmdDrawIndexedPool;
-
-	MVKCommandTypePool<MVKCmdDrawIndirect> _cmdDrawIndirectPool;
-
-	MVKCommandTypePool<MVKCmdDrawIndexedIndirect> _cmdDrawIndexedIndirectPool;
-
-	MVKCommandTypePool<MVKCmdCopyImage> _cmdCopyImagePool;
-
-	MVKCommandTypePool<MVKCmdBlitImage> _cmdBlitImagePool;
-
-    MVKCommandTypePool<MVKCmdResolveImage> _cmdResolveImagePool;
-
-    MVKCommandTypePool<MVKCmdFillBuffer> _cmdFillBufferPool;
-
-    MVKCommandTypePool<MVKCmdUpdateBuffer> _cmdUpdateBufferPool;
-
-	MVKCommandTypePool<MVKCmdCopyBuffer> _cmdCopyBufferPool;
-
-    MVKCommandTypePool<MVKCmdBufferImageCopy> _cmdBufferImageCopyPool;
-
-	MVKCommandTypePool<MVKCmdClearAttachments> _cmdClearAttachmentsPool;
-
-	MVKCommandTypePool<MVKCmdClearImage> _cmdClearImagePool;
-
-    MVKCommandTypePool<MVKCmdBeginQuery> _cmdBeginQueryPool;
-
-    MVKCommandTypePool<MVKCmdEndQuery> _cmdEndQueryPool;
-
-	MVKCommandTypePool<MVKCmdWriteTimestamp> _cmdWriteTimestampPool;
-
-    MVKCommandTypePool<MVKCmdResetQueryPool> _cmdResetQueryPoolPool;
-
-    MVKCommandTypePool<MVKCmdCopyQueryPoolResults> _cmdCopyQueryPoolResultsPool;
-
-	MVKCommandTypePool<MVKCmdPushConstants> _cmdPushConstantsPool;
-
-    MVKCommandTypePool<MVKCmdDispatch> _cmdDispatchPool;
-
-    MVKCommandTypePool<MVKCmdDispatchIndirect> _cmdDispatchIndirectPool;
-
-    MVKCommandTypePool<MVKCmdPushDescriptorSet> _cmdPushDescriptorSetPool;
-
-    MVKCommandTypePool<MVKCmdPushDescriptorSetWithTemplate> _cmdPushDescriptorSetWithTemplatePool;
-
-	MVKCommandTypePool<MVKCmdDebugMarkerBegin> _cmdDebugMarkerBeginPool;
-
-	MVKCommandTypePool<MVKCmdDebugMarkerEnd> _cmdDebugMarkerEndPool;
-
-	MVKCommandTypePool<MVKCmdDebugMarkerInsert> _cmdDebugMarkerInsertPool;
-
-	MVKCommandTypePool<MVKCmdSetResetEvent> _cmdSetResetEventPool;
-
-	MVKCommandTypePool<MVKCmdWaitEvents> _cmdWaitEventsPool;
+	// Command type pool member variables.
+	// Each has a form similar to (here for a draw command):  MVKCommandTypePool<MVKCmdDraw> _cmdDrawPool;
+#	define MVK_CMD_TYPE_POOL(cmdType)  MVKCommandTypePool<MVKCmd ##cmdType> _cmd ##cmdType ##Pool;
+#	include "MVKCommandTypePools.def"
 
 
 #pragma mark Command resources
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
index c59f5ce..fb20ebf 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
@@ -79,55 +79,10 @@
 	return [[_device->getQueue(_queueFamilyIndex, queueIndex)->getMTLCommandQueue() commandBuffer] retain];
 }
 
+// Clear the command type pool member variables.
 void MVKCommandPool::trim() {
-	_commandBufferPool.clear();
-	_commandEncodingPool.clear();
-	_cmdPipelineBarrierPool.clear();
-	_cmdBindPipelinePool.clear();
-	_cmdBeginRenderPassPool.clear();
-	_cmdNextSubpassPool.clear();
-	_cmdExecuteCommandsPool.clear();
-	_cmdEndRenderPassPool.clear();
-	_cmdBindDescriptorSetsPool.clear();
-	_cmdSetViewportPool.clear();
-	_cmdSetScissorPool.clear();
-	_cmdSetLineWidthPool.clear();
-	_cmdSetDepthBiasPool.clear();
-	_cmdSetBlendConstantsPool.clear();
-	_cmdSetDepthBoundsPool.clear();
-	_cmdSetStencilCompareMaskPool.clear();
-	_cmdSetStencilWriteMaskPool.clear();
-	_cmdSetStencilReferencePool.clear();
-	_cmdBindVertexBuffersPool.clear();
-	_cmdBindIndexBufferPool.clear();
-	_cmdDrawPool.clear();
-	_cmdDrawIndexedPool.clear();
-	_cmdDrawIndirectPool.clear();
-	_cmdDrawIndexedIndirectPool.clear();
-	_cmdCopyImagePool.clear();
-	_cmdBlitImagePool.clear();
-	_cmdResolveImagePool.clear();
-	_cmdFillBufferPool.clear();
-	_cmdUpdateBufferPool.clear();
-	_cmdCopyBufferPool.clear();
-	_cmdBufferImageCopyPool.clear();
-	_cmdClearAttachmentsPool.clear();
-	_cmdClearImagePool.clear();
-	_cmdBeginQueryPool.clear();
-	_cmdEndQueryPool.clear();
-	_cmdWriteTimestampPool.clear();
-	_cmdResetQueryPoolPool.clear();
-	_cmdCopyQueryPoolResultsPool.clear();
-	_cmdPushConstantsPool.clear();
-	_cmdDispatchPool.clear();
-	_cmdDispatchIndirectPool.clear();
-	_cmdPushDescriptorSetPool.clear();
-	_cmdPushDescriptorSetWithTemplatePool.clear();
-	_cmdDebugMarkerBeginPool.clear();
-	_cmdDebugMarkerEndPool.clear();
-	_cmdDebugMarkerInsertPool.clear();
-	_cmdSetResetEventPool.clear();
-	_cmdWaitEventsPool.clear();
+#	define MVK_CMD_TYPE_POOL(cmdType)  _cmd ##cmdType ##Pool.clear();
+#	include "MVKCommandTypePools.def"
 }
 
 
@@ -140,53 +95,12 @@
 	_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
 	_commandBufferPool(device, usePooling),
 	_commandEncodingPool(this),
-	_cmdPipelineBarrierPool(usePooling),
-	_cmdBindPipelinePool(usePooling),
-	_cmdBeginRenderPassPool(usePooling),
-	_cmdNextSubpassPool(usePooling),
-	_cmdExecuteCommandsPool(usePooling),
-	_cmdEndRenderPassPool(usePooling),
-	_cmdBindDescriptorSetsPool(usePooling),
-	_cmdSetViewportPool(usePooling),
-	_cmdSetScissorPool(usePooling),
-	_cmdSetLineWidthPool(usePooling),
-	_cmdSetDepthBiasPool(usePooling),
-	_cmdSetBlendConstantsPool(usePooling),
-	_cmdSetDepthBoundsPool(usePooling),
-	_cmdSetStencilCompareMaskPool(usePooling),
-	_cmdSetStencilWriteMaskPool(usePooling),
-	_cmdSetStencilReferencePool(usePooling),
-	_cmdBindVertexBuffersPool(usePooling),
-	_cmdBindIndexBufferPool(usePooling),
-	_cmdDrawPool(usePooling),
-	_cmdDrawIndexedPool(usePooling),
-	_cmdDrawIndirectPool(usePooling),
-	_cmdDrawIndexedIndirectPool(usePooling),
-	_cmdCopyImagePool(usePooling),
-	_cmdBlitImagePool(usePooling),
-	_cmdResolveImagePool(usePooling),
-	_cmdFillBufferPool(usePooling),
-	_cmdUpdateBufferPool(usePooling),
-	_cmdCopyBufferPool(usePooling),
-	_cmdBufferImageCopyPool(usePooling),
-	_cmdClearAttachmentsPool(usePooling),
-	_cmdClearImagePool(usePooling),
-	_cmdBeginQueryPool(usePooling),
-	_cmdEndQueryPool(usePooling),
-	_cmdWriteTimestampPool(usePooling),
-	_cmdResetQueryPoolPool(usePooling),
-	_cmdCopyQueryPoolResultsPool(usePooling),
-	_cmdPushConstantsPool(usePooling),
-	_cmdDispatchPool(usePooling),
-	_cmdDispatchIndirectPool(usePooling),
-	_cmdPushDescriptorSetPool(usePooling),
-	_cmdPushDescriptorSetWithTemplatePool(usePooling),
-	_cmdDebugMarkerBeginPool(usePooling),
-	_cmdDebugMarkerEndPool(usePooling),
-	_cmdDebugMarkerInsertPool(usePooling),
-	_cmdSetResetEventPool(usePooling),
-	_cmdWaitEventsPool(usePooling)
-// when extending be sure to add to trim() as well
+
+// Initialize the command type pool member variables.
+#	define MVK_CMD_TYPE_POOL_LAST(cmdType)  _cmd ##cmdType ##Pool(usePooling)
+#	define MVK_CMD_TYPE_POOL(cmdType)  MVK_CMD_TYPE_POOL_LAST(cmdType),
+#	include "MVKCommandTypePools.def"
+
 {}
 
 MVKCommandPool::~MVKCommandPool() {
@@ -195,3 +109,15 @@
 	}
 }
 
+#pragma mark -
+#pragma mark MVKCommand subclass getTypePool() functions
+
+// Implementations of the MVKCommand subclass getTypePool() functions.
+#define MVK_TMPLT_DECL  template<>
+#define MVK_CMD_TYPE_POOL(cmdType)					  										\
+MVKCommandTypePool<MVKCommand>* MVKCmd ##cmdType ::getTypePool(MVKCommandPool* cmdPool) {	\
+	return (MVKCommandTypePool<MVKCommand>*)&cmdPool->_cmd  ##cmdType ##Pool;				\
+}
+#include "MVKCommandTypePools.def"
+
+
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
new file mode 100644
index 0000000..c47d949
--- /dev/null
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
@@ -0,0 +1,95 @@
+/*
+ * MVKCommandTypePools.def
+ *
+ * Copyright (c) 2015-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// To use this file, define a macro MVK_CMD_TYPE_POOL(cmdType), then #include this file.
+// If the last entry needs to be different (for example to avoid a dangling ',' at the
+// end of an initializer list, also define a macro MVK_CMD_TYPE_POOL_LAST(cmdType).
+
+// MVK_TMPLT_DECL is used to support adding a "template<>" prefix when this file is used
+// to define function implementations for a concrete implementation of a class template
+
+// To add a new command type, simply add an MVK_CMD_TYPE_POOL() line below.
+// The last line in the list must be MVK_CMD_TYPE_POOL_LAST().
+// If the command is a concrete implementation of a template class, include the
+// MVK_TMPLT_DECL prefix.
+
+#ifndef MVK_TMPLT_DECL
+#	define MVK_TMPLT_DECL
+#endif
+
+#ifndef MVK_CMD_TYPE_POOL
+#	error MVK_CMD_TYPE_POOL must be defined before including this file
+#endif
+
+#ifndef MVK_CMD_TYPE_POOL_LAST
+#	define MVK_CMD_TYPE_POOL_LAST(cmdType) MVK_CMD_TYPE_POOL(cmdType)
+#endif
+
+MVK_CMD_TYPE_POOL(PipelineBarrier)
+MVK_CMD_TYPE_POOL(BindPipeline)
+MVK_CMD_TYPE_POOL(BeginRenderPass)
+MVK_CMD_TYPE_POOL(NextSubpass)
+MVK_CMD_TYPE_POOL(EndRenderPass)
+MVK_CMD_TYPE_POOL(ExecuteCommands)
+MVK_CMD_TYPE_POOL(BindDescriptorSets)
+MVK_TMPLT_DECL MVK_CMD_TYPE_POOL(SetViewport1)
+MVK_TMPLT_DECL MVK_CMD_TYPE_POOL(SetViewportMulti)
+MVK_TMPLT_DECL MVK_CMD_TYPE_POOL(SetScissor1)
+MVK_TMPLT_DECL MVK_CMD_TYPE_POOL(SetScissorMulti)
+MVK_CMD_TYPE_POOL(SetLineWidth)
+MVK_CMD_TYPE_POOL(SetDepthBias)
+MVK_CMD_TYPE_POOL(SetBlendConstants)
+MVK_CMD_TYPE_POOL(SetDepthBounds)
+MVK_CMD_TYPE_POOL(SetStencilCompareMask)
+MVK_CMD_TYPE_POOL(SetStencilWriteMask)
+MVK_CMD_TYPE_POOL(SetStencilReference)
+MVK_CMD_TYPE_POOL(BindVertexBuffers)
+MVK_CMD_TYPE_POOL(BindIndexBuffer)
+MVK_CMD_TYPE_POOL(Draw)
+MVK_CMD_TYPE_POOL(DrawIndexed)
+MVK_CMD_TYPE_POOL(DrawIndirect)
+MVK_CMD_TYPE_POOL(DrawIndexedIndirect)
+MVK_CMD_TYPE_POOL(CopyImage)
+MVK_CMD_TYPE_POOL(BlitImage)
+MVK_CMD_TYPE_POOL(ResolveImage)
+MVK_CMD_TYPE_POOL(FillBuffer)
+MVK_CMD_TYPE_POOL(UpdateBuffer)
+MVK_CMD_TYPE_POOL(CopyBuffer)
+MVK_CMD_TYPE_POOL(BufferImageCopy)
+MVK_CMD_TYPE_POOL(ClearAttachments)
+MVK_CMD_TYPE_POOL(ClearImage)
+MVK_CMD_TYPE_POOL(BeginQuery)
+MVK_CMD_TYPE_POOL(EndQuery)
+MVK_CMD_TYPE_POOL(WriteTimestamp)
+MVK_CMD_TYPE_POOL(ResetQueryPool)
+MVK_CMD_TYPE_POOL(CopyQueryPoolResults)
+MVK_CMD_TYPE_POOL(PushConstants)
+MVK_CMD_TYPE_POOL(Dispatch)
+MVK_CMD_TYPE_POOL(DispatchIndirect)
+MVK_CMD_TYPE_POOL(PushDescriptorSet)
+MVK_CMD_TYPE_POOL(PushDescriptorSetWithTemplate)
+MVK_CMD_TYPE_POOL(DebugMarkerBegin)
+MVK_CMD_TYPE_POOL(DebugMarkerEnd)
+MVK_CMD_TYPE_POOL(DebugMarkerInsert)
+MVK_CMD_TYPE_POOL(SetResetEvent)
+MVK_CMD_TYPE_POOL_LAST(WaitEvents)
+
+#undef MVK_CMD_TYPE_POOL
+#undef MVK_CMD_TYPE_POOL_LAST
+#undef MVK_TMPLT_DECL
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index a099921..5b28ae3 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -1352,7 +1352,11 @@
 	const VkViewport*                           pViewports) {
 
 	MVKTraceVulkanCallStart();
-	MVKAddCmd(SetViewport, commandBuffer, firstViewport, viewportCount, pViewports);
+	if (viewportCount <= 1) {
+		MVKAddCmd(SetViewport1, commandBuffer, firstViewport, viewportCount, pViewports);
+	} else {
+		MVKAddCmd(SetViewportMulti, commandBuffer, firstViewport, viewportCount, pViewports);
+	}
 	MVKTraceVulkanCallEnd();
 }
 
@@ -1363,7 +1367,11 @@
 	const VkRect2D*                             pScissors) {
 
 	MVKTraceVulkanCallStart();
-	MVKAddCmd(SetScissor, commandBuffer, firstScissor, scissorCount, pScissors);
+	if (scissorCount <= 1) {
+		MVKAddCmd(SetScissor1, commandBuffer, firstScissor, scissorCount, pScissors);
+	} else {
+		MVKAddCmd(SetScissorMulti, commandBuffer, firstScissor, scissorCount, pScissors);
+	}
 	MVKTraceVulkanCallEnd();
 }