Reduce memory requirements for vkCmdBindDescriptorSets.

Split MVKCmdBindDescriptorSetsStatic into separate subclasses,
depending on use of dynamic offsets, and make them template classes
based on descriptor set count.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
index bf75742..ec12615 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.h
@@ -120,10 +120,49 @@
 
 
 #pragma mark -
-#pragma mark MVKCmdBindDescriptorSets
+#pragma mark MVKCmdBindDescriptorSetsStatic
 
-/** Vulkan command to bind descriptor sets. */
-class MVKCmdBindDescriptorSets : public MVKCommand {
+/**
+ * Vulkan command to bind descriptor sets without dynamic offsets.
+ * Template class to balance vector pre-allocations between very common low counts and fewer larger counts.
+ */
+template <size_t N>
+class MVKCmdBindDescriptorSetsStatic : public MVKCommand {
+
+public:
+	VkResult setContent(MVKCommandBuffer* cmdBuff,
+						VkPipelineBindPoint pipelineBindPoint,
+						VkPipelineLayout layout,
+						uint32_t firstSet,
+						uint32_t setCount,
+						const VkDescriptorSet* pDescriptorSets);
+
+	void encode(MVKCommandEncoder* cmdEncoder) override;
+
+protected:
+	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
+
+	MVKVectorInline<MVKDescriptorSet*, N> _descriptorSets;
+	MVKPipelineLayout* _pipelineLayout;
+	VkPipelineBindPoint _pipelineBindPoint;
+	uint32_t _firstSet;
+};
+
+// Concrete template class implementations.
+typedef MVKCmdBindDescriptorSetsStatic<1> MVKCmdBindDescriptorSetsStatic1;
+typedef MVKCmdBindDescriptorSetsStatic<4> MVKCmdBindDescriptorSetsStatic4;
+typedef MVKCmdBindDescriptorSetsStatic<8> MVKCmdBindDescriptorSetsStaticMulti;
+
+
+#pragma mark -
+#pragma mark MVKCmdBindDescriptorSetsDynamic
+
+/**
+ * Vulkan command to bind descriptor sets with dynamic offsets.
+ * Template class to balance vector pre-allocations between very common low counts and fewer larger counts.
+ */
+template <size_t N>
+class MVKCmdBindDescriptorSetsDynamic : public MVKCmdBindDescriptorSetsStatic<N> {
 
 public:
 	VkResult setContent(MVKCommandBuffer* cmdBuff,
@@ -140,13 +179,13 @@
 protected:
 	MVKCommandTypePool<MVKCommand>* getTypePool(MVKCommandPool* cmdPool) override;
 
-	VkPipelineBindPoint _pipelineBindPoint;
-	MVKPipelineLayout* _pipelineLayout;
-	MVKVectorInline<MVKDescriptorSet*, 8> _descriptorSets;
-	MVKVectorInline<uint32_t, 8>          _dynamicOffsets;
-	uint32_t _firstSet;
+	MVKVectorInline<uint32_t, N> _dynamicOffsets;
 };
 
+// Concrete template class implementations.
+typedef MVKCmdBindDescriptorSetsDynamic<4> MVKCmdBindDescriptorSetsDynamic4;
+typedef MVKCmdBindDescriptorSetsDynamic<8> MVKCmdBindDescriptorSetsDynamicMulti;
+
 
 #pragma mark -
 #pragma mark MVKCmdPushConstants
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index 50252ad..1658a52 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -182,16 +182,15 @@
 
 
 #pragma mark -
-#pragma mark MVKCmdBindDescriptorSets
+#pragma mark MVKCmdBindDescriptorSetsStatic
 
-VkResult MVKCmdBindDescriptorSets::setContent(MVKCommandBuffer* cmdBuff,
-											  VkPipelineBindPoint pipelineBindPoint,
-											  VkPipelineLayout layout,
-											  uint32_t firstSet,
-											  uint32_t setCount,
-											  const VkDescriptorSet* pDescriptorSets,
-											  uint32_t dynamicOffsetCount,
-											  const uint32_t* pDynamicOffsets) {
+template <size_t N>
+VkResult MVKCmdBindDescriptorSetsStatic<N>::setContent(MVKCommandBuffer* cmdBuff,
+													   VkPipelineBindPoint pipelineBindPoint,
+													   VkPipelineLayout layout,
+													   uint32_t firstSet,
+													   uint32_t setCount,
+													   const VkDescriptorSet* pDescriptorSets) {
 	_pipelineBindPoint = pipelineBindPoint;
 	_pipelineLayout = (MVKPipelineLayout*)layout;
 	_firstSet = firstSet;
@@ -203,6 +202,35 @@
 		_descriptorSets.push_back((MVKDescriptorSet*)pDescriptorSets[dsIdx]);
 	}
 
+	return VK_SUCCESS;
+}
+
+template <size_t N>
+void MVKCmdBindDescriptorSetsStatic<N>::encode(MVKCommandEncoder* cmdEncoder) {
+	_pipelineLayout->bindDescriptorSets(cmdEncoder, _descriptorSets, _firstSet, nullptr);
+}
+
+template class MVKCmdBindDescriptorSetsStatic<1>;
+template class MVKCmdBindDescriptorSetsStatic<4>;
+template class MVKCmdBindDescriptorSetsStatic<8>;
+
+
+#pragma mark -
+#pragma mark MVKCmdBindDescriptorSetsDynamic
+
+template <size_t N>
+VkResult MVKCmdBindDescriptorSetsDynamic<N>::setContent(MVKCommandBuffer* cmdBuff,
+														VkPipelineBindPoint pipelineBindPoint,
+														VkPipelineLayout layout,
+														uint32_t firstSet,
+														uint32_t setCount,
+														const VkDescriptorSet* pDescriptorSets,
+														uint32_t dynamicOffsetCount,
+														const uint32_t* pDynamicOffsets) {
+
+	MVKCmdBindDescriptorSetsStatic<N>::setContent(cmdBuff, pipelineBindPoint, layout,
+												  firstSet, setCount, pDescriptorSets);
+
 	// Add the dynamic offsets
 	_dynamicOffsets.clear();	// Clear for reuse
 	_dynamicOffsets.reserve(dynamicOffsetCount);
@@ -213,10 +241,14 @@
 	return VK_SUCCESS;
 }
 
-void MVKCmdBindDescriptorSets::encode(MVKCommandEncoder* cmdEncoder) {
-	_pipelineLayout->bindDescriptorSets(cmdEncoder, _descriptorSets, _firstSet, _dynamicOffsets);
+template <size_t N>
+void MVKCmdBindDescriptorSetsDynamic<N>::encode(MVKCommandEncoder* cmdEncoder) {
+	MVKCmdBindDescriptorSetsStatic<N>::_pipelineLayout->bindDescriptorSets(cmdEncoder, MVKCmdBindDescriptorSetsStatic<N>::_descriptorSets, MVKCmdBindDescriptorSetsStatic<N>::_firstSet, &_dynamicOffsets);
 }
 
+template class MVKCmdBindDescriptorSetsDynamic<4>;
+template class MVKCmdBindDescriptorSetsDynamic<8>;
+
 
 #pragma mark -
 #pragma mark MVKCmdPushConstants
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
index f48bd92..815ed97 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandTypePools.def
@@ -58,7 +58,8 @@
 MVK_CMD_TYPE_POOL(NextSubpass)
 MVK_CMD_TYPE_POOL(EndRenderPass)
 MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(ExecuteCommands, 1)
-MVK_CMD_TYPE_POOL(BindDescriptorSets)
+MVK_CMD_TYPE_POOLS_FROM_TWO_THRESHOLDS(BindDescriptorSetsStatic, 1, 4)
+MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(BindDescriptorSetsDynamic, 4)
 MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(SetViewport, 1)
 MVK_CMD_TYPE_POOLS_FROM_THRESHOLD(SetScissor, 1)
 MVK_CMD_TYPE_POOL(SetLineWidth)
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
index 2d53f1a..c19f2eb 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
@@ -89,7 +89,7 @@
 				  MVKDescriptorSet* descSet,
 				  uint32_t descStartIndex,
 				  MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-				  MVKVector<uint32_t>& dynamicOffsets,
+				  MVKVector<uint32_t>* pDynamicOffsets,
 				  uint32_t* pDynamicOffsetIndex);
 
     /** Encodes this binding layout and the specified descriptor on the specified command encoder immediately. */
@@ -148,7 +148,7 @@
 					  uint32_t descriptorIndex,
 					  bool stages[],
 					  MVKShaderResourceBinding& mtlIndexes,
-					  MVKVector<uint32_t>& dynamicOffsets,
+					  MVKVector<uint32_t>* pDynamicOffsets,
 					  uint32_t* pDynamicOffsetIndex) = 0;
 
 	/**
@@ -202,7 +202,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -280,7 +280,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -319,7 +319,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -391,7 +391,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex);
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -433,7 +433,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -473,7 +473,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
@@ -511,7 +511,7 @@
 			  uint32_t descriptorIndex,
 			  bool stages[],
 			  MVKShaderResourceBinding& mtlIndexes,
-			  MVKVector<uint32_t>& dynamicOffsets,
+			  MVKVector<uint32_t>* pDynamicOffsets,
 			  uint32_t* pDynamicOffsetIndex) override;
 
 	void write(MVKDescriptorSet* mvkDescSet,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
index a5cb26b..3179cc2 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
@@ -83,7 +83,7 @@
 											 MVKDescriptorSet* descSet,
 											 uint32_t descStartIndex,
 											 MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-											 MVKVector<uint32_t>& dynamicOffsets,
+											 MVKVector<uint32_t>* pDynamicOffsets,
 											 uint32_t* pDynamicOffsetIndex) {
 
 	// Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding.
@@ -93,7 +93,7 @@
     for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) {
 		MVKDescriptor* mvkDesc = descSet->getDescriptor(descStartIndex + descIdx);
 		mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage,
-					  mtlIdxs, dynamicOffsets, pDynamicOffsetIndex);
+					  mtlIdxs, pDynamicOffsets, pDynamicOffsetIndex);
     }
 	return descCnt;
 }
@@ -476,7 +476,7 @@
 							   uint32_t descriptorIndex,
 							   bool stages[],
 							   MVKShaderResourceBinding& mtlIndexes,
-							   MVKVector<uint32_t>& dynamicOffsets,
+							   MVKVector<uint32_t>* pDynamicOffsets,
 							   uint32_t* pDynamicOffsetIndex) {
 	MVKMTLBufferBinding bb;
 	NSUInteger bufferDynamicOffset = 0;
@@ -485,8 +485,10 @@
 		// After determining dynamic part of offset (zero otherwise), fall through to non-dynamic handling
 		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
 		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
-			bufferDynamicOffset = dynamicOffsets[*pDynamicOffsetIndex];
-			(*pDynamicOffsetIndex)++;           // Move on to next dynamic offset (and feedback to caller)
+			if (pDynamicOffsets) {
+				bufferDynamicOffset = (*pDynamicOffsets)[*pDynamicOffsetIndex];
+				(*pDynamicOffsetIndex)++;           // Move on to next dynamic offset (and feedback to caller)
+			}
 		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
 		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
 			if (_mvkBuffer) {
@@ -581,7 +583,7 @@
 									  uint32_t descriptorIndex,
 									  bool stages[],
 									  MVKShaderResourceBinding& mtlIndexes,
-									  MVKVector<uint32_t>& dynamicOffsets,
+									  MVKVector<uint32_t>* pDynamicOffsets,
 									  uint32_t* pDynamicOffsetIndex) {
 	MVKMTLBufferBinding bb;
 
@@ -678,7 +680,7 @@
 							  uint32_t descriptorIndex,
 							  bool stages[],
 							  MVKShaderResourceBinding& mtlIndexes,
-							  MVKVector<uint32_t>& dynamicOffsets,
+							  MVKVector<uint32_t>* pDynamicOffsets,
 							  uint32_t* pDynamicOffsetIndex) {
 	MVKMTLTextureBinding tb;
 	MVKMTLBufferBinding bb;
@@ -796,7 +798,7 @@
 									 uint32_t descriptorIndex,
 									 bool stages[],
 									 MVKShaderResourceBinding& mtlIndexes,
-									 MVKVector<uint32_t>& dynamicOffsets,
+									 MVKVector<uint32_t>* pDynamicOffsets,
 									 uint32_t* pDynamicOffsetIndex) {
 	MVKMTLSamplerStateBinding sb;
 	switch (descriptorType) {
@@ -914,12 +916,12 @@
 								uint32_t descriptorIndex,
 								bool stages[],
 								MVKShaderResourceBinding& mtlIndexes,
-								MVKVector<uint32_t>& dynamicOffsets,
+								MVKVector<uint32_t>* pDynamicOffsets,
 								uint32_t* pDynamicOffsetIndex) {
 	switch (descriptorType) {
 		case VK_DESCRIPTOR_TYPE_SAMPLER: {
 			MVKSamplerDescriptorMixin::bind(cmdEncoder, descriptorType, descriptorIndex, stages,
-											mtlIndexes, dynamicOffsets, pDynamicOffsetIndex);
+											mtlIndexes, pDynamicOffsets, pDynamicOffsetIndex);
 			break;
 		}
 
@@ -983,14 +985,14 @@
 											 uint32_t descriptorIndex,
 											 bool stages[],
 											 MVKShaderResourceBinding& mtlIndexes,
-											 MVKVector<uint32_t>& dynamicOffsets,
+											 MVKVector<uint32_t>* pDynamicOffsets,
 											 uint32_t* pDynamicOffsetIndex) {
 	switch (descriptorType) {
 		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: {
 			MVKImageDescriptor::bind(cmdEncoder, descriptorType, descriptorIndex, stages,
-									 mtlIndexes, dynamicOffsets, pDynamicOffsetIndex);
+									 mtlIndexes, pDynamicOffsets, pDynamicOffsetIndex);
 			MVKSamplerDescriptorMixin::bind(cmdEncoder, descriptorType, descriptorIndex, stages,
-											mtlIndexes, dynamicOffsets, pDynamicOffsetIndex);
+											mtlIndexes, pDynamicOffsets, pDynamicOffsetIndex);
 			break;
 		}
 
@@ -1057,7 +1059,7 @@
 									uint32_t descriptorIndex,
 									bool stages[],
 									MVKShaderResourceBinding& mtlIndexes,
-									MVKVector<uint32_t>& dynamicOffsets,
+									MVKVector<uint32_t>* pDynamicOffsets,
 									uint32_t* pDynamicOffsetIndex) {
 	MVKMTLTextureBinding tb;
 	MVKMTLBufferBinding bb;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 51756a3..a5cbe57 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -46,7 +46,7 @@
     void bindDescriptorSet(MVKCommandEncoder* cmdEncoder,
                            MVKDescriptorSet* descSet,
                            MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-                           MVKVector<uint32_t>& dynamicOffsets,
+                           MVKVector<uint32_t>* pDynamicOffsets,
                            uint32_t* pDynamicOffsetIndex);
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index 45d5ba5..59c2902 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -38,7 +38,7 @@
 void MVKDescriptorSetLayout::bindDescriptorSet(MVKCommandEncoder* cmdEncoder,
                                                MVKDescriptorSet* descSet,
                                                MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-                                               MVKVector<uint32_t>& dynamicOffsets,
+                                               MVKVector<uint32_t>* pDynamicOffsets,
                                                uint32_t* pDynamicOffsetIndex) {
     if (_isPushDescriptorLayout) return;
 
@@ -46,7 +46,7 @@
     uint32_t bindCnt = (uint32_t)_bindings.size();
     for (uint32_t descIdx = 0, bindIdx = 0; bindIdx < bindCnt; bindIdx++) {
 		descIdx += _bindings[bindIdx].bind(cmdEncoder, descSet, descIdx,
-										   dslMTLRezIdxOffsets, dynamicOffsets,
+										   dslMTLRezIdxOffsets, pDynamicOffsets,
 										   pDynamicOffsetIndex);
     }
 }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index 431f02f..3221299 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -56,7 +56,7 @@
     void bindDescriptorSets(MVKCommandEncoder* cmdEncoder,
                             MVKVector<MVKDescriptorSet*>& descriptorSets,
                             uint32_t firstSet,
-                            MVKVector<uint32_t>& dynamicOffsets);
+                            MVKVector<uint32_t>* pDynamicOffsets);
 
 	/** Updates a descriptor set in a command encoder. */
 	void pushDescriptorSet(MVKCommandEncoder* cmdEncoder,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index c67ab8c..d270906 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -40,7 +40,7 @@
 void MVKPipelineLayout::bindDescriptorSets(MVKCommandEncoder* cmdEncoder,
                                            MVKVector<MVKDescriptorSet*>& descriptorSets,
                                            uint32_t firstSet,
-                                           MVKVector<uint32_t>& dynamicOffsets) {
+                                           MVKVector<uint32_t>* pDynamicOffsets) {
 	clearConfigurationResult();
 	uint32_t pDynamicOffsetIndex = 0;
 	uint32_t dsCnt = (uint32_t)descriptorSets.size();
@@ -50,7 +50,7 @@
 		MVKDescriptorSetLayout* dsl = _descriptorSetLayouts[dslIdx];
 		dsl->bindDescriptorSet(cmdEncoder, descSet,
 							   _dslMTLResourceIndexOffsets[dslIdx],
-							   dynamicOffsets, &pDynamicOffsetIndex);
+							   pDynamicOffsets, &pDynamicOffsetIndex);
 		setConfigurationResult(dsl->getConfigurationResult());
 	}
 }
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 9254cf4..6428509 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -1449,8 +1449,13 @@
     const uint32_t*                             pDynamicOffsets) {
 	
 	MVKTraceVulkanCallStart();
-	MVKAddCmd(BindDescriptorSets, commandBuffer, pipelineBindPoint, layout,
-			  firstSet, setCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
+	if (dynamicOffsetCount) {
+		MVKAddCmdFromThreshold(BindDescriptorSetsDynamic, setCount, 4, commandBuffer, pipelineBindPoint, layout,
+				  firstSet, setCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets);
+	} else {
+		MVKAddCmdFromTwoThresholds(BindDescriptorSetsStatic, setCount, 1, 4, commandBuffer, pipelineBindPoint, layout,
+				  firstSet, setCount, pDescriptorSets);
+	}
 	MVKTraceVulkanCallEnd();
 }