Merge pull request #623 from cdavis5e/no-viewports-assertion

Don't assert if no viewports or scissor rects were given.
diff --git a/ExternalRevisions/SPIRV-Cross_repo_revision b/ExternalRevisions/SPIRV-Cross_repo_revision
index b7fe472..2b892c4 100644
--- a/ExternalRevisions/SPIRV-Cross_repo_revision
+++ b/ExternalRevisions/SPIRV-Cross_repo_revision
@@ -1 +1 @@
-c125bbd2482815f2f1b6a92ec4f06e1c01b47c5b
+cb686a5dba9a7086a778fe21900383beed9ea5d3
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
index 45ee566..ae28ec3 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdDebug.mm
@@ -41,9 +41,14 @@
 #pragma mark MVKCmdDebugMarkerBegin
 
 // Vulkan debug groups are more general than Metal's.
-// Always push on command buffer instead of the encoder.
+// If a renderpass is active, push on the render command encoder, otherwise push on the command buffer.
 void MVKCmdDebugMarkerBegin::encode(MVKCommandEncoder* cmdEncoder) {
-	[cmdEncoder->_mtlCmdBuffer pushDebugGroup: _markerName];
+	id<MTLRenderCommandEncoder> mtlCmdEnc = cmdEncoder->_mtlRenderEncoder;
+	if (mtlCmdEnc) {
+		[mtlCmdEnc pushDebugGroup: _markerName];
+	} else {
+		[cmdEncoder->_mtlCmdBuffer pushDebugGroup: _markerName];
+	}
 }
 
 MVKCmdDebugMarkerBegin::MVKCmdDebugMarkerBegin(MVKCommandTypePool<MVKCmdDebugMarkerBegin>* pool)
@@ -54,9 +59,14 @@
 #pragma mark MVKCmdDebugMarkerEnd
 
 // Vulkan debug groups are more general than Metal's.
-// Always pop from command buffer instead of the encoder.
+// If a renderpass is active, pop from the render command encoder, otherwise pop from the command buffer.
 void MVKCmdDebugMarkerEnd::encode(MVKCommandEncoder* cmdEncoder) {
-	[cmdEncoder->_mtlCmdBuffer popDebugGroup];
+	id<MTLRenderCommandEncoder> mtlCmdEnc = cmdEncoder->_mtlRenderEncoder;
+	if (mtlCmdEnc) {
+		[mtlCmdEnc popDebugGroup];
+	} else {
+		[cmdEncoder->_mtlCmdBuffer popDebugGroup];
+	}
 }
 
 MVKCmdDebugMarkerEnd::MVKCmdDebugMarkerEnd(MVKCommandTypePool<MVKCmdDebugMarkerEnd>* pool)
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
index 3646abf..4d46a5b 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdRenderPass.mm
@@ -74,7 +74,7 @@
 
 void MVKCmdEndRenderPass::encode(MVKCommandEncoder* cmdEncoder) {
 //	MVKLogDebug("Encoding vkCmdEndRenderPass(). Elapsed time: %.6f ms.", mvkGetElapsedMilliseconds());
-	cmdEncoder->endMetalRenderEncoding();
+	cmdEncoder->endRenderpass();
 }
 
 MVKCmdEndRenderPass::MVKCmdEndRenderPass(MVKCommandTypePool<MVKCmdEndRenderPass>* pool)
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index ba86b4b..2591f74 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -301,6 +301,9 @@
     /** Called by each compute dispatch command to establish any outstanding state just prior to performing the dispatch. */
     void finalizeDispatchState();
 
+	/** Ends the current renderpass. */
+	void endRenderpass();
+
 	/** 
 	 * Ends all encoding operations on the current Metal command encoder.
 	 *
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index 7548626..7df3be2 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -332,7 +332,12 @@
 
 // Returns a name for use as a MTLRenderCommandEncoder label
 NSString* MVKCommandEncoder::getMTLRenderCommandEncoderName() {
-	NSString* rpName = _renderPass ? _renderPass->getDebugName() : nil;
+	NSString* rpName;
+
+	rpName = _renderPass->getDebugName();
+	if (rpName) { return rpName; }
+
+	rpName = _cmdBuffer->getDebugName();
 	if (rpName) { return rpName; }
 
 	MVKCommandUse cmdUse = (_renderSubpassIndex == 0) ? kMVKCommandUseBeginRenderPass : kMVKCommandUseNextSubpass;
@@ -418,6 +423,14 @@
     _computePushConstants.encode();
 }
 
+void MVKCommandEncoder::endRenderpass() {
+	endMetalRenderEncoding();
+
+	_renderPass = nullptr;
+	_framebuffer = nullptr;
+	_renderSubpassIndex = 0;
+}
+
 void MVKCommandEncoder::endMetalRenderEncoding() {
 //    MVKLogDebugIf(_mtlRenderEncoder, "Render subpass end MTLRenderCommandEncoder.");
     [_mtlRenderEncoder endEncoding];
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index 843c209..901f457 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -403,7 +403,7 @@
         }
     }
 
-	void updateSwizzle(MVKVector<uint32_t> &constants, uint32_t index, uint32_t swizzle);
+	void updateImplicitBuffer(MVKVector<uint32_t> &contents, uint32_t index, uint32_t value);
 	void assertMissingSwizzles(bool needsSwizzle, const char* stageName, MVKVector<MVKMTLTextureBinding>& texBindings);
 
 };
@@ -434,18 +434,25 @@
         _mtlIndexBufferBinding = binding;   // No need to track dirty state
     }
 
-    /** Sets the current auxiliary buffer state. */
-    void bindAuxBuffer(const MVKShaderImplicitRezBinding& binding,
-                       bool needVertexAuxBuffer,
-                       bool needTessCtlAuxBuffer,
-                       bool needTessEvalAuxBuffer,
-                       bool needFragmentAuxBuffer);
+    /** Sets the current swizzle buffer state. */
+    void bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
+                           bool needVertexSwizzleBuffer,
+                           bool needTessCtlSwizzleBuffer,
+                           bool needTessEvalSwizzleBuffer,
+                           bool needFragmentSwizzleBuffer);
+
+    /** Sets the current buffer size buffer state. */
+    void bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
+                              bool needVertexSizeBuffer,
+                              bool needTessCtlSizeBuffer,
+                              bool needTessEvalSizeBuffer,
+                              bool needFragmentSizeBuffer);
 
     void encodeBindings(MVKShaderStage stage,
                         const char* pStageName,
                         bool fullImageViewSwizzle,
                         std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&)> bindBuffer,
-                        std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, MVKVector<uint32_t>&)> bindAuxBuffer,
+                        std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, MVKVector<uint32_t>&)> bindImplicitBuffer,
                         std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
                         std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler);
 
@@ -464,7 +471,9 @@
         MVKVectorInline<MVKMTLTextureBinding, 8> textureBindings;
         MVKVectorInline<MVKMTLSamplerStateBinding, 8> samplerStateBindings;
         MVKVectorInline<uint32_t, 8> swizzleConstants;
-        MVKMTLBufferBinding auxBufferBinding;
+        MVKVectorInline<uint32_t, 8> bufferSizes;
+        MVKMTLBufferBinding swizzleBufferBinding;
+        MVKMTLBufferBinding bufferSizeBufferBinding;
 
         bool areBufferBindingsDirty = false;
         bool areTextureBindingsDirty = false;
@@ -494,8 +503,11 @@
     /** Binds the specified sampler state. */
     void bindSamplerState(const MVKMTLSamplerStateBinding& binding);
 
-    /** Sets the current auxiliary buffer state. */
-	void bindAuxBuffer(const MVKShaderImplicitRezBinding& binding, bool needAuxBuffer);
+    /** Sets the current swizzle buffer state. */
+    void bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding, bool needSwizzleBuffer);
+
+    /** Sets the current buffer size buffer state. */
+    void bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding, bool needSizeBuffer);
 
 #pragma mark Construction
 
@@ -511,13 +523,15 @@
     MVKVectorDefault<MVKMTLTextureBinding> _textureBindings;
     MVKVectorDefault<MVKMTLSamplerStateBinding> _samplerStateBindings;
     MVKVectorDefault<uint32_t> _swizzleConstants;
-    MVKMTLBufferBinding _auxBufferBinding;
+    MVKVectorDefault<uint32_t> _bufferSizes;
+    MVKMTLBufferBinding _swizzleBufferBinding;
+    MVKMTLBufferBinding _bufferSizeBufferBinding;
 
     bool _areBufferBindingsDirty = false;
     bool _areTextureBindingsDirty = false;
     bool _areSamplerStateBindingsDirty = false;
 
-	bool _needsSwizzle = false;
+    bool _needsSwizzle = false;
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index b80de27..328ff5c 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -447,10 +447,10 @@
 #pragma mark -
 #pragma mark MVKResourcesCommandEncoderState
 
-// Updates the swizzle for an image in the given vector.
-void MVKResourcesCommandEncoderState::updateSwizzle(MVKVector<uint32_t> &constants, uint32_t index, uint32_t swizzle) {
-	if (index >= constants.size()) { constants.resize(index + 1); }
-	constants[index] = swizzle;
+// Updates a value at the given index in the given vector.
+void MVKResourcesCommandEncoderState::updateImplicitBuffer(MVKVector<uint32_t> &contents, uint32_t index, uint32_t value) {
+	if (index >= contents.size()) { contents.resize(index + 1); }
+	contents[index] = value;
 }
 
 // If a swizzle is needed for this stage, iterates all the bindings and logs errors for those that need texture swizzling.
@@ -486,42 +486,64 @@
     bind(binding, _shaderStages[stage].samplerStateBindings, _shaderStages[stage].areSamplerStateBindingsDirty);
 }
 
-void MVKGraphicsResourcesCommandEncoderState::bindAuxBuffer(const MVKShaderImplicitRezBinding& binding,
-															bool needVertexAuxBuffer,
-															bool needTessCtlAuxBuffer,
-															bool needTessEvalAuxBuffer,
-															bool needFragmentAuxBuffer) {
+void MVKGraphicsResourcesCommandEncoderState::bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
+																bool needVertexSwizzleBuffer,
+																bool needTessCtlSwizzleBuffer,
+																bool needTessEvalSwizzleBuffer,
+																bool needFragmentSwizzleBuffer) {
     for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
-        _shaderStages[i].auxBufferBinding.index = binding.stages[i];
+        _shaderStages[i].swizzleBufferBinding.index = binding.stages[i];
     }
-    _shaderStages[kMVKShaderStageVertex].auxBufferBinding.isDirty = needVertexAuxBuffer;
-    _shaderStages[kMVKShaderStageTessCtl].auxBufferBinding.isDirty = needTessCtlAuxBuffer;
-    _shaderStages[kMVKShaderStageTessEval].auxBufferBinding.isDirty = needTessEvalAuxBuffer;
-    _shaderStages[kMVKShaderStageFragment].auxBufferBinding.isDirty = needFragmentAuxBuffer;
+    _shaderStages[kMVKShaderStageVertex].swizzleBufferBinding.isDirty = needVertexSwizzleBuffer;
+    _shaderStages[kMVKShaderStageTessCtl].swizzleBufferBinding.isDirty = needTessCtlSwizzleBuffer;
+    _shaderStages[kMVKShaderStageTessEval].swizzleBufferBinding.isDirty = needTessEvalSwizzleBuffer;
+    _shaderStages[kMVKShaderStageFragment].swizzleBufferBinding.isDirty = needFragmentSwizzleBuffer;
+}
+
+void MVKGraphicsResourcesCommandEncoderState::bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
+																   bool needVertexSizeBuffer,
+																   bool needTessCtlSizeBuffer,
+																   bool needTessEvalSizeBuffer,
+																   bool needFragmentSizeBuffer) {
+    for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
+        _shaderStages[i].bufferSizeBufferBinding.index = binding.stages[i];
+    }
+    _shaderStages[kMVKShaderStageVertex].bufferSizeBufferBinding.isDirty = needVertexSizeBuffer;
+    _shaderStages[kMVKShaderStageTessCtl].bufferSizeBufferBinding.isDirty = needTessCtlSizeBuffer;
+    _shaderStages[kMVKShaderStageTessEval].bufferSizeBufferBinding.isDirty = needTessEvalSizeBuffer;
+    _shaderStages[kMVKShaderStageFragment].bufferSizeBufferBinding.isDirty = needFragmentSizeBuffer;
 }
 
 void MVKGraphicsResourcesCommandEncoderState::encodeBindings(MVKShaderStage stage,
                                                              const char* pStageName,
                                                              bool fullImageViewSwizzle,
                                                              std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&)> bindBuffer,
-                                                             std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, MVKVector<uint32_t>&)> bindAuxBuffer,
+                                                             std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, MVKVector<uint32_t>&)> bindImplicitBuffer,
                                                              std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
                                                              std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler) {
     auto& shaderStage = _shaderStages[stage];
     encodeBinding<MVKMTLBufferBinding>(shaderStage.bufferBindings, shaderStage.areBufferBindingsDirty, bindBuffer);
 
-    if (shaderStage.auxBufferBinding.isDirty) {
+    if (shaderStage.swizzleBufferBinding.isDirty) {
 
         for (auto& b : shaderStage.textureBindings) {
-            if (b.isDirty) { updateSwizzle(shaderStage.swizzleConstants, b.index, b.swizzle); }
+            if (b.isDirty) { updateImplicitBuffer(shaderStage.swizzleConstants, b.index, b.swizzle); }
         }
 
-        bindAuxBuffer(_cmdEncoder, shaderStage.auxBufferBinding, shaderStage.swizzleConstants);
+        bindImplicitBuffer(_cmdEncoder, shaderStage.swizzleBufferBinding, shaderStage.swizzleConstants);
 
     } else {
         assertMissingSwizzles(shaderStage.needsSwizzle && !fullImageViewSwizzle, pStageName, shaderStage.textureBindings);
     }
 
+    if (shaderStage.bufferSizeBufferBinding.isDirty) {
+        for (auto& b : shaderStage.bufferBindings) {
+            if (b.isDirty) { updateImplicitBuffer(shaderStage.bufferSizes, b.index, b.size); }
+        }
+
+        bindImplicitBuffer(_cmdEncoder, shaderStage.bufferSizeBufferBinding, shaderStage.bufferSizes);
+    }
+
     encodeBinding<MVKMTLTextureBinding>(shaderStage.textureBindings, shaderStage.areTextureBindingsDirty, bindTexture);
     encodeBinding<MVKMTLSamplerStateBinding>(shaderStage.samplerStateBindings, shaderStage.areSamplerStateBindingsDirty, bindSampler);
 }
@@ -622,7 +644,7 @@
                                                                     atIndex: b.index];
                        },
                        [](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b, MVKVector<uint32_t>& s)->void {
-		                   cmdEncoder->setFragmentBytes(cmdEncoder->_mtlRenderEncoder,
+                           cmdEncoder->setFragmentBytes(cmdEncoder->_mtlRenderEncoder,
                                                         s.data(),
                                                         s.size() * sizeof(uint32_t),
                                                         b.index);
@@ -644,11 +666,13 @@
         _shaderStages[i].textureBindings.clear();
         _shaderStages[i].samplerStateBindings.clear();
         _shaderStages[i].swizzleConstants.clear();
+        _shaderStages[i].bufferSizes.clear();
 
         _shaderStages[i].areBufferBindingsDirty = false;
         _shaderStages[i].areTextureBindingsDirty = false;
         _shaderStages[i].areSamplerStateBindingsDirty = false;
-        _shaderStages[i].auxBufferBinding.isDirty = false;
+        _shaderStages[i].swizzleBufferBinding.isDirty = false;
+        _shaderStages[i].bufferSizeBufferBinding.isDirty = false;
 
         _shaderStages[i].needsSwizzle = false;
     }
@@ -663,17 +687,23 @@
 }
 
 void MVKComputeResourcesCommandEncoderState::bindTexture(const MVKMTLTextureBinding& binding) {
-	bind(binding, _textureBindings, _areTextureBindingsDirty, _needsSwizzle);
+    bind(binding, _textureBindings, _areTextureBindingsDirty, _needsSwizzle);
 }
 
 void MVKComputeResourcesCommandEncoderState::bindSamplerState(const MVKMTLSamplerStateBinding& binding) {
     bind(binding, _samplerStateBindings, _areSamplerStateBindingsDirty);
 }
 
-void MVKComputeResourcesCommandEncoderState::bindAuxBuffer(const MVKShaderImplicitRezBinding& binding,
-														   bool needAuxBuffer) {
-    _auxBufferBinding.index = binding.stages[kMVKShaderStageCompute];
-    _auxBufferBinding.isDirty = needAuxBuffer;
+void MVKComputeResourcesCommandEncoderState::bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
+															   bool needSwizzleBuffer) {
+    _swizzleBufferBinding.index = binding.stages[kMVKShaderStageCompute];
+    _swizzleBufferBinding.isDirty = needSwizzleBuffer;
+}
+
+void MVKComputeResourcesCommandEncoderState::bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
+																  bool needBufferSizeBuffer) {
+    _bufferSizeBufferBinding.index = binding.stages[kMVKShaderStageCompute];
+    _bufferSizeBufferBinding.isDirty = needBufferSizeBuffer;
 }
 
 // Mark everything as dirty
@@ -698,21 +728,33 @@
 																									   atIndex: b.index];
                                        });
 
-    if (_auxBufferBinding.isDirty) {
+    if (_swizzleBufferBinding.isDirty) {
 
 		for (auto& b : _textureBindings) {
-			if (b.isDirty) { updateSwizzle(_swizzleConstants, b.index, b.swizzle); }
+			if (b.isDirty) { updateImplicitBuffer(_swizzleConstants, b.index, b.swizzle); }
 		}
 
 		_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
                                      _swizzleConstants.data(),
                                      _swizzleConstants.size() * sizeof(uint32_t),
-                                     _auxBufferBinding.index);
+                                     _swizzleBufferBinding.index);
 
 	} else {
 		assertMissingSwizzles(_needsSwizzle && !fullImageViewSwizzle, "compute", _textureBindings);
     }
 
+    if (_bufferSizeBufferBinding.isDirty) {
+		for (auto& b : _bufferBindings) {
+			if (b.isDirty) { updateImplicitBuffer(_bufferSizes, b.index, b.size); }
+		}
+
+		_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
+                                     _bufferSizes.data(),
+                                     _bufferSizes.size() * sizeof(uint32_t),
+                                     _bufferSizeBufferBinding.index);
+
+    }
+
     encodeBinding<MVKMTLTextureBinding>(_textureBindings, _areTextureBindingsDirty,
                                         [](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
                                             [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setTexture: b.mtlTexture
@@ -731,11 +773,13 @@
     _textureBindings.clear();
     _samplerStateBindings.clear();
     _swizzleConstants.clear();
+    _bufferSizes.clear();
 
     _areBufferBindingsDirty = false;
     _areTextureBindingsDirty = false;
     _areSamplerStateBindingsDirty = false;
-    _auxBufferBinding.isDirty = false;
+    _swizzleBufferBinding.isDirty = false;
+    _bufferSizeBufferBinding.isDirty = false;
 
 	_needsSwizzle = false;
 }
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
index 3316594..cfdf82c 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
@@ -40,6 +40,7 @@
     union { id<MTLBuffer> mtlBuffer = nil; id<MTLBuffer> mtlResource; }; // aliases
     NSUInteger offset = 0;
     uint32_t index = 0;
+    uint32_t size = 0;
     bool isDirty = true;
 } MVKMTLBufferBinding;
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index 3a62682..28a1ff6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -105,6 +105,7 @@
             case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
                 bb.mtlBuffer = descBinding._mtlBuffers[rezIdx];
                 bb.offset = descBinding._mtlBufferOffsets[rezIdx] + bufferDynamicOffset;
+				bb.size = (uint32_t)((MVKBuffer*)descBinding._bufferBindings[rezIdx].buffer)->getByteCount();
                 for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) {
                     if (_applyToStage[i]) {
                         bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx;
@@ -236,6 +237,7 @@
                 MVKBuffer* buffer = (MVKBuffer*)bufferInfo.buffer;
                 bb.mtlBuffer = buffer->getMTLBuffer();
                 bb.offset = bufferInfo.offset;
+				bb.size = (uint32_t)buffer->getByteCount();
                 for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) {
                     if (_applyToStage[i]) {
                         bb.index = mtlIdxs.stages[i].bufferIndex + rezIdx;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
index bdaba80..f84d7b7 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.h
@@ -69,8 +69,11 @@
 						   uint32_t set,
 						   const void* pData);
 
-	/** Returns the current auxiliary buffer bindings. */
-	const MVKShaderImplicitRezBinding& getAuxBufferIndex() { return _auxBufferIndex; }
+	/** Returns the current swizzle buffer bindings. */
+	const MVKShaderImplicitRezBinding& getSwizzleBufferIndex() { return _swizzleBufferIndex; }
+
+	/** Returns the current buffer size buffer bindings. */
+	const MVKShaderImplicitRezBinding& getBufferSizeBufferIndex() { return _bufferSizeBufferIndex; }
 
 	/** Returns the current indirect parameter buffer bindings. */
 	const MVKShaderImplicitRezBinding& getIndirectParamsIndex() { return _indirectParamsIndex; }
@@ -84,9 +87,12 @@
 	/** Returns the current tessellation level buffer binding for the tess. control shader. */
 	uint32_t getTessCtlLevelBufferIndex() { return _tessCtlLevelBufferIndex; }
 
-	/** Returns the number of textures in this layout. This is used to calculate the size of the auxiliary buffer. */
+	/** Returns the number of textures in this layout. This is used to calculate the size of the swizzle buffer. */
 	uint32_t getTextureCount() { return _pushConstantsMTLResourceIndexes.getMaxTextureIndex(); }
 
+	/** Returns the number of buffers in this layout. This is used to calculate the size of the buffer size buffer. */
+	uint32_t getBufferCount() { return _pushConstantsMTLResourceIndexes.getMaxBufferIndex(); }
+
 	/** Constructs an instance for the specified device. */
 	MVKPipelineLayout(MVKDevice* device, const VkPipelineLayoutCreateInfo* pCreateInfo);
 
@@ -97,7 +103,8 @@
 	MVKVectorInline<MVKShaderResourceBinding, 8> _dslMTLResourceIndexOffsets;
 	MVKVectorInline<VkPushConstantRange, 8> _pushConstants;
 	MVKShaderResourceBinding _pushConstantsMTLResourceIndexes;
-	MVKShaderImplicitRezBinding _auxBufferIndex;
+	MVKShaderImplicitRezBinding _swizzleBufferIndex;
+	MVKShaderImplicitRezBinding _bufferSizeBufferIndex;
 	MVKShaderImplicitRezBinding _indirectParamsIndex;
 	MVKShaderImplicitRezBinding _outputBufferIndex;
 	uint32_t _tessCtlPatchOutputBufferIndex = 0;
@@ -131,8 +138,11 @@
 	/** Binds this pipeline to the specified command encoder. */
 	virtual void encode(MVKCommandEncoder* cmdEncoder, uint32_t stage = 0) = 0;
 
-	/** Returns the current auxiliary buffer bindings. */
-	const MVKShaderImplicitRezBinding& getAuxBufferIndex() { return _auxBufferIndex; }
+	/** Returns the current swizzle buffer bindings. */
+	const MVKShaderImplicitRezBinding& getSwizzleBufferIndex() { return _swizzleBufferIndex; }
+
+	/** Returns the current buffer size buffer bindings. */
+	const MVKShaderImplicitRezBinding& getBufferSizeBufferIndex() { return _bufferSizeBufferIndex; }
 
 	/** Returns whether or not full image view swizzling is enabled for this pipeline. */
 	bool fullImageViewSwizzle() const { return _fullImageViewSwizzle; }
@@ -146,7 +156,8 @@
 	void propogateDebugName() override {}
 
 	MVKPipelineCache* _pipelineCache;
-	MVKShaderImplicitRezBinding _auxBufferIndex;
+	MVKShaderImplicitRezBinding _swizzleBufferIndex;
+	MVKShaderImplicitRezBinding _bufferSizeBufferIndex;
 	bool _fullImageViewSwizzle;
 
 };
@@ -230,6 +241,7 @@
     void addTessellationToPipeline(MTLRenderPipelineDescriptor* plDesc, const SPIRVTessReflectionData& reflectData, const VkPipelineTessellationStateCreateInfo* pTS);
     void addFragmentOutputToPipeline(MTLRenderPipelineDescriptor* plDesc, const SPIRVTessReflectionData& reflectData, const VkGraphicsPipelineCreateInfo* pCreateInfo, bool isTessellationVertexPipeline = false);
     bool isRenderingPoints(const VkGraphicsPipelineCreateInfo* pCreateInfo, const SPIRVTessReflectionData& reflectData);
+	bool verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name, uint32_t reservedBuffers);
 
 	const VkPipelineShaderStageCreateInfo* _pVertexSS = nullptr;
 	const VkPipelineShaderStageCreateInfo* _pTessCtlSS = nullptr;
@@ -265,14 +277,18 @@
 
 	bool _dynamicStateEnabled[VK_DYNAMIC_STATE_RANGE_SIZE];
 	bool _hasDepthStencilInfo;
-	bool _needsVertexAuxBuffer = false;
+	bool _needsVertexSwizzleBuffer = false;
+	bool _needsVertexBufferSizeBuffer = false;
 	bool _needsVertexOutputBuffer = false;
-	bool _needsTessCtlAuxBuffer = false;
+	bool _needsTessCtlSwizzleBuffer = false;
+	bool _needsTessCtlBufferSizeBuffer = false;
 	bool _needsTessCtlOutputBuffer = false;
 	bool _needsTessCtlPatchOutputBuffer = false;
 	bool _needsTessCtlInput = false;
-	bool _needsTessEvalAuxBuffer = false;
-	bool _needsFragmentAuxBuffer = false;
+	bool _needsTessEvalSwizzleBuffer = false;
+	bool _needsTessEvalBufferSizeBuffer = false;
+	bool _needsFragmentSwizzleBuffer = false;
+	bool _needsFragmentBufferSizeBuffer = false;
 };
 
 
@@ -303,7 +319,8 @@
 
     id<MTLComputePipelineState> _mtlPipelineState;
     MTLSize _mtlThreadgroupSize;
-    bool _needsAuxBuffer = false;
+    bool _needsSwizzleBuffer = false;
+    bool _needsBufferSizeBuffer = false;
 };
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 9136263..21976c6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -135,9 +135,14 @@
 	}
 
 	// Set implicit buffer indices
+	// FIXME: Many of these are optional. We shouldn't set the ones that aren't
+	// present--or at least, we should move the ones that are down to avoid
+	// running over the limit of available buffers. But we can't know that
+	// until we compile the shaders.
 	for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageMax; i++) {
-		_auxBufferIndex.stages[i] = _pushConstantsMTLResourceIndexes.stages[i].bufferIndex + 1;
-		_indirectParamsIndex.stages[i] = _auxBufferIndex.stages[i] + 1;
+		_swizzleBufferIndex.stages[i] = _pushConstantsMTLResourceIndexes.stages[i].bufferIndex + 1;
+		_bufferSizeBufferIndex.stages[i] = _swizzleBufferIndex.stages[i] + 1;
+		_indirectParamsIndex.stages[i] = _bufferSizeBufferIndex.stages[i] + 1;
 		_outputBufferIndex.stages[i] = _indirectParamsIndex.stages[i] + 1;
 		if (i == kMVKShaderStageTessCtl) {
 			_tessCtlPatchOutputBufferIndex = _outputBufferIndex.stages[i] + 1;
@@ -232,7 +237,8 @@
 
             break;
     }
-    cmdEncoder->_graphicsResourcesState.bindAuxBuffer(_auxBufferIndex, _needsVertexAuxBuffer, _needsTessCtlAuxBuffer, _needsTessEvalAuxBuffer, _needsFragmentAuxBuffer);
+    cmdEncoder->_graphicsResourcesState.bindSwizzleBuffer(_swizzleBufferIndex, _needsVertexSwizzleBuffer, _needsTessCtlSwizzleBuffer, _needsTessEvalSwizzleBuffer, _needsFragmentSwizzleBuffer);
+    cmdEncoder->_graphicsResourcesState.bindBufferSizeBuffer(_bufferSizeBufferIndex, _needsVertexBufferSizeBuffer, _needsTessCtlBufferSizeBuffer, _needsTessEvalBufferSizeBuffer, _needsFragmentBufferSizeBuffer);
 }
 
 bool MVKGraphicsPipeline::supportsDynamicState(VkDynamicState state) {
@@ -700,14 +706,29 @@
 	return plDesc;
 }
 
+bool MVKGraphicsPipeline::verifyImplicitBuffer(bool needsBuffer, MVKShaderImplicitRezBinding& index, MVKShaderStage stage, const char* name, uint32_t reservedBuffers) {
+	const char* stageNames[] = {
+		"Vertex",
+		"Tessellation control",
+		"Tessellation evaluation",
+		"Fragment"
+	};
+	if (needsBuffer && index.stages[stage] >= _device->_pMetalFeatures->maxPerStageBufferCount - reservedBuffers) {
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "%s shader requires %s buffer, but there is no free slot to pass it.", stageNames[stage], name));
+		return false;
+	}
+	return true;
+}
+
 // Adds a vertex shader to the pipeline description.
 bool MVKGraphicsPipeline::addVertexShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext) {
 	uint32_t vbCnt = pCreateInfo->pVertexInputState->vertexBindingDescriptionCount;
 	shaderContext.options.entryPointStage = spv::ExecutionModelVertex;
 	shaderContext.options.entryPointName = _pVertexSS->pName;
-	shaderContext.options.auxBufferIndex = _auxBufferIndex.stages[kMVKShaderStageVertex];
+	shaderContext.options.swizzleBufferIndex = _swizzleBufferIndex.stages[kMVKShaderStageVertex];
 	shaderContext.options.indirectParamsBufferIndex = _indirectParamsIndex.stages[kMVKShaderStageVertex];
 	shaderContext.options.outputBufferIndex = _outputBufferIndex.stages[kMVKShaderStageVertex];
+	shaderContext.options.bufferSizeBufferIndex = _bufferSizeBufferIndex.stages[kMVKShaderStageVertex];
 	shaderContext.options.shouldCaptureOutput = isTessellationPipeline();
 	shaderContext.options.isRasterizationDisabled = isTessellationPipeline() || (pCreateInfo->pRasterizationState && (pCreateInfo->pRasterizationState->rasterizerDiscardEnable));
     addVertexInputToShaderConverterContext(shaderContext, pCreateInfo);
@@ -718,20 +739,22 @@
 	}
 	plDesc.vertexFunction = mtlFunction;
 	plDesc.rasterizationEnabled = !shaderContext.options.isRasterizationDisabled;
-	_needsVertexAuxBuffer = shaderContext.options.needsAuxBuffer;
+	_needsVertexSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
+	_needsVertexBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
 	_needsVertexOutputBuffer = shaderContext.options.needsOutputBuffer;
-	// If we need the auxiliary buffer and there's no place to put it, we're in serious trouble.
-	if (_needsVertexAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires auxiliary buffer, but there is no free slot to pass it."));
+	// If we need the swizzle buffer and there's no place to put it, we're in serious trouble.
+	if (!verifyImplicitBuffer(_needsVertexSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageVertex, "swizzle", vbCnt)) {
+		return false;
+	}
+	// Ditto buffer size buffer.
+	if (!verifyImplicitBuffer(_needsVertexBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageVertex, "buffer size", vbCnt)) {
 		return false;
 	}
 	// Ditto captured output buffer.
-	if (_needsVertexOutputBuffer && _outputBufferIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires output buffer, but there is no free slot to pass it."));
+	if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _outputBufferIndex, kMVKShaderStageVertex, "output", vbCnt)) {
 		return false;
 	}
-	if (_needsVertexOutputBuffer && _indirectParamsIndex.stages[kMVKShaderStageVertex] >= _device->_pMetalFeatures->maxPerStageBufferCount - vbCnt) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Vertex shader requires indirect parameters buffer, but there is no free slot to pass it."));
+	if (!verifyImplicitBuffer(_needsVertexOutputBuffer, _indirectParamsIndex, kMVKShaderStageVertex, "indirect parameters", vbCnt)) {
 		return false;
 	}
 	return true;
@@ -740,11 +763,12 @@
 bool MVKGraphicsPipeline::addTessCtlShaderToPipeline(MTLComputePipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext, std::vector<SPIRVShaderOutput>& vtxOutputs) {
 	shaderContext.options.entryPointStage = spv::ExecutionModelTessellationControl;
 	shaderContext.options.entryPointName = _pTessCtlSS->pName;
-	shaderContext.options.auxBufferIndex = _auxBufferIndex.stages[kMVKShaderStageTessCtl];
+	shaderContext.options.swizzleBufferIndex = _swizzleBufferIndex.stages[kMVKShaderStageTessCtl];
 	shaderContext.options.indirectParamsBufferIndex = _indirectParamsIndex.stages[kMVKShaderStageTessCtl];
 	shaderContext.options.outputBufferIndex = _outputBufferIndex.stages[kMVKShaderStageTessCtl];
 	shaderContext.options.patchOutputBufferIndex = _tessCtlPatchOutputBufferIndex;
 	shaderContext.options.tessLevelBufferIndex = _tessCtlLevelBufferIndex;
+	shaderContext.options.bufferSizeBufferIndex = _bufferSizeBufferIndex.stages[kMVKShaderStageTessCtl];
 	shaderContext.options.shouldCaptureOutput = true;
 	addPrevStageOutputToShaderConverterContext(shaderContext, vtxOutputs);
 	id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pTessCtlSS->module)->getMTLFunction(&shaderContext, _pTessCtlSS->pSpecializationInfo, _pipelineCache).mtlFunction;
@@ -753,20 +777,21 @@
 		return false;
 	}
 	plDesc.computeFunction = mtlFunction;
-	_needsTessCtlAuxBuffer = shaderContext.options.needsAuxBuffer;
+	_needsTessCtlSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
+	_needsTessCtlBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
 	_needsTessCtlOutputBuffer = shaderContext.options.needsOutputBuffer;
 	_needsTessCtlPatchOutputBuffer = shaderContext.options.needsPatchOutputBuffer;
 	_needsTessCtlInput = shaderContext.options.needsInputThreadgroupMem;
-	if (_needsTessCtlAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires auxiliary buffer, but there is no free slot to pass it."));
+	if (!verifyImplicitBuffer(_needsTessCtlSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessCtl, "swizzle", kMVKTessCtlNumReservedBuffers)) {
 		return false;
 	}
-	if (_indirectParamsIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires indirect parameters buffer, but there is no free slot to pass it."));
+	if (!verifyImplicitBuffer(_needsTessCtlBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessCtl, "buffer size", kMVKTessCtlNumReservedBuffers)) {
 		return false;
 	}
-	if (_needsTessCtlOutputBuffer && _outputBufferIndex.stages[kMVKShaderStageTessCtl] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation control shader requires per-vertex output buffer, but there is no free slot to pass it."));
+	if (!verifyImplicitBuffer(true, _indirectParamsIndex, kMVKShaderStageTessCtl, "indirect parameters", kMVKTessCtlNumReservedBuffers)) {
+		return false;
+	}
+	if (!verifyImplicitBuffer(_needsTessCtlOutputBuffer, _outputBufferIndex, kMVKShaderStageTessCtl, "per-vertex output", kMVKTessCtlNumReservedBuffers)) {
 		return false;
 	}
 	if (_needsTessCtlPatchOutputBuffer && _tessCtlPatchOutputBufferIndex >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessCtlNumReservedBuffers) {
@@ -783,7 +808,8 @@
 bool MVKGraphicsPipeline::addTessEvalShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext, std::vector<SPIRVShaderOutput>& tcOutputs) {
 	shaderContext.options.entryPointStage = spv::ExecutionModelTessellationEvaluation;
 	shaderContext.options.entryPointName = _pTessEvalSS->pName;
-	shaderContext.options.auxBufferIndex = _auxBufferIndex.stages[kMVKShaderStageTessEval];
+	shaderContext.options.swizzleBufferIndex = _swizzleBufferIndex.stages[kMVKShaderStageTessEval];
+	shaderContext.options.bufferSizeBufferIndex = _bufferSizeBufferIndex.stages[kMVKShaderStageTessEval];
 	shaderContext.options.shouldCaptureOutput = false;
 	shaderContext.options.isRasterizationDisabled = (pCreateInfo->pRasterizationState && (pCreateInfo->pRasterizationState->rasterizerDiscardEnable));
 	addPrevStageOutputToShaderConverterContext(shaderContext, tcOutputs);
@@ -795,10 +821,12 @@
 	// Yeah, you read that right. Tess. eval functions are a kind of vertex function in Metal.
 	plDesc.vertexFunction = mtlFunction;
 	plDesc.rasterizationEnabled = !shaderContext.options.isRasterizationDisabled;
-	_needsTessEvalAuxBuffer = shaderContext.options.needsAuxBuffer;
-	// If we need the auxiliary buffer and there's no place to put it, we're in serious trouble.
-	if (_needsTessEvalAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageTessEval] >= _device->_pMetalFeatures->maxPerStageBufferCount - kMVKTessEvalNumReservedBuffers) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Tessellation evaluation shader requires auxiliary buffer, but there is no free slot to pass it."));
+	_needsTessEvalSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
+	_needsTessEvalBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
+	if (!verifyImplicitBuffer(_needsTessEvalSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageTessEval, "swizzle", kMVKTessEvalNumReservedBuffers)) {
+		return false;
+	}
+	if (!verifyImplicitBuffer(_needsTessEvalBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageTessEval, "buffer size", kMVKTessEvalNumReservedBuffers)) {
 		return false;
 	}
 	return true;
@@ -807,7 +835,8 @@
 bool MVKGraphicsPipeline::addFragmentShaderToPipeline(MTLRenderPipelineDescriptor* plDesc, const VkGraphicsPipelineCreateInfo* pCreateInfo, SPIRVToMSLConverterContext& shaderContext) {
 	if (_pFragmentSS) {
 		shaderContext.options.entryPointStage = spv::ExecutionModelFragment;
-		shaderContext.options.auxBufferIndex = _auxBufferIndex.stages[kMVKShaderStageFragment];
+		shaderContext.options.swizzleBufferIndex = _swizzleBufferIndex.stages[kMVKShaderStageFragment];
+		shaderContext.options.bufferSizeBufferIndex = _bufferSizeBufferIndex.stages[kMVKShaderStageFragment];
 		shaderContext.options.entryPointName = _pFragmentSS->pName;
 		shaderContext.options.shouldCaptureOutput = false;
 		id<MTLFunction> mtlFunction = ((MVKShaderModule*)_pFragmentSS->module)->getMTLFunction(&shaderContext, _pFragmentSS->pSpecializationInfo, _pipelineCache).mtlFunction;
@@ -816,9 +845,12 @@
 			return false;
 		}
 		plDesc.fragmentFunction = mtlFunction;
-		_needsFragmentAuxBuffer = shaderContext.options.needsAuxBuffer;
-		if (_needsFragmentAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageFragment] >= _device->_pMetalFeatures->maxPerStageBufferCount) {
-			setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Fragment shader requires auxiliary buffer, but there is no free slot to pass it."));
+		_needsFragmentSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
+		_needsFragmentBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
+		if (!verifyImplicitBuffer(_needsFragmentSwizzleBuffer, _swizzleBufferIndex, kMVKShaderStageFragment, "swizzle", 0)) {
+			return false;
+		}
+		if (!verifyImplicitBuffer(_needsFragmentBufferSizeBuffer, _bufferSizeBufferIndex, kMVKShaderStageFragment, "buffer size", 0)) {
 			return false;
 		}
 	}
@@ -1029,7 +1061,8 @@
 
     MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
     layout->populateShaderConverterContext(shaderContext);
-    _auxBufferIndex = layout->getAuxBufferIndex();
+    _swizzleBufferIndex = layout->getSwizzleBufferIndex();
+    _bufferSizeBufferIndex = layout->getBufferSizeBufferIndex();
     _indirectParamsIndex = layout->getIndirectParamsIndex();
     _outputBufferIndex = layout->getOutputBufferIndex();
     _tessCtlPatchOutputBufferIndex = layout->getTessCtlPatchOutputBufferIndex();
@@ -1164,7 +1197,8 @@
 void MVKComputePipeline::encode(MVKCommandEncoder* cmdEncoder, uint32_t) {
     [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setComputePipelineState: _mtlPipelineState];
     cmdEncoder->_mtlThreadgroupSize = _mtlThreadgroupSize;
-	cmdEncoder->_computeResourcesState.bindAuxBuffer(_auxBufferIndex, _needsAuxBuffer);
+	cmdEncoder->_computeResourcesState.bindSwizzleBuffer(_swizzleBufferIndex, _needsSwizzleBuffer);
+	cmdEncoder->_computeResourcesState.bindBufferSizeBuffer(_bufferSizeBufferIndex, _needsBufferSizeBuffer);
 }
 
 MVKComputePipeline::MVKComputePipeline(MVKDevice* device,
@@ -1191,8 +1225,11 @@
 		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader function could not be compiled into pipeline. See previous logged error."));
 	}
 
-	if (_needsAuxBuffer && _auxBufferIndex.stages[kMVKShaderStageCompute] > _device->_pMetalFeatures->maxPerStageBufferCount) {
-		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader requires auxiliary buffer, but there is no free slot to pass it."));
+	if (_needsSwizzleBuffer && _swizzleBufferIndex.stages[kMVKShaderStageCompute] > _device->_pMetalFeatures->maxPerStageBufferCount) {
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader requires swizzle buffer, but there is no free slot to pass it."));
+	}
+	if (_needsBufferSizeBuffer && _bufferSizeBufferIndex.stages[kMVKShaderStageCompute] > _device->_pMetalFeatures->maxPerStageBufferCount) {
+		setConfigurationResult(reportError(VK_ERROR_INVALID_SHADER_NV, "Compute shader requires buffer size buffer, but there is no free slot to pass it."));
 	}
 }
 
@@ -1211,12 +1248,15 @@
 
     MVKPipelineLayout* layout = (MVKPipelineLayout*)pCreateInfo->layout;
     layout->populateShaderConverterContext(shaderContext);
-    _auxBufferIndex = layout->getAuxBufferIndex();
-    shaderContext.options.auxBufferIndex = _auxBufferIndex.stages[kMVKShaderStageCompute];
+    _swizzleBufferIndex = layout->getSwizzleBufferIndex();
+    _bufferSizeBufferIndex = layout->getBufferSizeBufferIndex();
+    shaderContext.options.swizzleBufferIndex = _swizzleBufferIndex.stages[kMVKShaderStageCompute];
+    shaderContext.options.bufferSizeBufferIndex = _bufferSizeBufferIndex.stages[kMVKShaderStageCompute];
 
     MVKShaderModule* mvkShdrMod = (MVKShaderModule*)pSS->module;
     MVKMTLFunction func = mvkShdrMod->getMTLFunction(&shaderContext, pSS->pSpecializationInfo, _pipelineCache);
-    _needsAuxBuffer = shaderContext.options.needsAuxBuffer;
+    _needsSwizzleBuffer = shaderContext.options.needsSwizzleBuffer;
+    _needsBufferSizeBuffer = shaderContext.options.needsBufferSizeBuffer;
     return func;
 }
 
@@ -1241,6 +1281,7 @@
 
 	bool wasAdded = false;
 	MVKShaderLibraryCache* slCache = getShaderLibraryCache(shaderModule->getKey());
+	slCache->setShaderModule(shaderModule);
 	MVKShaderLibrary* shLib = slCache->getShaderLibrary(pContext, shaderModule, &wasAdded);
 	if (wasAdded) { markDirty(); }
 	return shLib;
@@ -1289,14 +1330,28 @@
 	void serialize(Archive & archive, SPIRVToMSLConverterOptions& opt) {
 		archive(opt.entryPointName,
 				opt.entryPointStage,
+				opt.tessPatchKind,
 				opt.mslVersion,
 				opt.texelBufferTextureWidth,
-				opt.auxBufferIndex,
+				opt.swizzleBufferIndex,
+				opt.indirectParamsBufferIndex,
+				opt.outputBufferIndex,
+				opt.patchOutputBufferIndex,
+				opt.tessLevelBufferIndex,
+				opt.bufferSizeBufferIndex,
+				opt.inputThreadgroupMemIndex,
+				opt.numTessControlPoints,
 				opt.shouldFlipVertexY,
 				opt.isRenderingPoints,
 				opt.shouldSwizzleTextureSamples,
+				opt.shouldCaptureOutput,
+				opt.tessDomainOriginInLowerLeft,
 				opt.isRasterizationDisabled,
-				opt.needsAuxBuffer);
+				opt.needsSwizzleBuffer,
+				opt.needsOutputBuffer,
+				opt.needsPatchOutputBuffer,
+				opt.needsBufferSizeBuffer,
+				opt.needsInputThreadgroupMem);
 	}
 
 	template<class Archive>
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
index d6fbbbe..b01add4 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.h
@@ -55,9 +55,6 @@
 	/** Returns the Vulkan API opaque object controlling this object. */
 	MVKVulkanAPIObject* getVulkanAPIObject() override { return _owner->getVulkanAPIObject(); };
 
-	/** Returns the Metal shader function, possibly specialized. */
-	MVKMTLFunction getMTLFunction(const VkSpecializationInfo* pSpecializationInfo);
-
 	/** Constructs an instance from the specified MSL source code. */
 	MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner,
 					 const std::string& mslSourceCode,
@@ -79,10 +76,14 @@
 	friend MVKShaderModule;
 
 	void propogateDebugName();
+	NSString* getDebugName();
+	void setShaderModule(MVKShaderModule* shaderModule);
+	MVKMTLFunction getMTLFunction(const VkSpecializationInfo* pSpecializationInfo);
 	void handleCompilationError(NSError* err, const char* opDesc);
     MTLFunctionConstant* getFunctionConstant(NSArray<MTLFunctionConstant*>* mtlFCs, NSUInteger mtlFCID);
 
 	MVKVulkanAPIDeviceObject* _owner;
+	MVKShaderModule* _shaderModule = nullptr;
 	id<MTLLibrary> _mtlLibrary;
 	SPIRVEntryPoint _entryPoint;
 	std::string _msl;
@@ -110,6 +111,11 @@
 	MVKShaderLibrary* getShaderLibrary(SPIRVToMSLConverterContext* pContext,
 									   MVKShaderModule* shaderModule,
 									   bool* pWasAdded = nullptr);
+	/**
+	 * Sets the shader module associated with this library cache.
+	 * This is set after creation because libraries can be loaded from a pipeline cache.
+	 */
+	void setShaderModule(MVKShaderModule* shaderModule);
 
 	MVKShaderLibraryCache(MVKVulkanAPIDeviceObject* owner) : _owner(owner) {};
 
@@ -128,6 +134,7 @@
 	void merge(MVKShaderLibraryCache* other);
 
 	MVKVulkanAPIDeviceObject* _owner;
+	MVKShaderModule* _shaderModule = nullptr;
 	std::vector<std::pair<SPIRVToMSLConverterContext, MVKShaderLibrary*>> _shaderLibraries;
 };
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
index 94f366f..974a29d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKShaderModule.mm
@@ -31,7 +31,15 @@
 #pragma mark -
 #pragma mark MVKShaderLibrary
 
-void MVKShaderLibrary::propogateDebugName() { setLabelIfNotNil(_mtlLibrary, _owner->getDebugName()); }
+void MVKShaderLibrary::propogateDebugName() { setLabelIfNotNil(_mtlLibrary, getDebugName()); }
+
+// First choice is to name after shader module if it exists, otherwise name after owner.
+NSString* MVKShaderLibrary::getDebugName() {
+	NSString* dbName = _shaderModule ? _shaderModule-> getDebugName() : nil;
+	if (dbName) { return dbName; }
+
+	return _owner ? _owner-> getDebugName() : nil;
+}
 
 // If the size of the workgroup dimension is specialized, extract it from the
 // specialization info, otherwise use the value specified in the SPIR-V shader code.
@@ -89,11 +97,12 @@
 				fs->destroy();
             }
         }
-		setLabelIfNotNil(mtlFunc, _owner->getDebugName());
     } else {
         reportError(VK_ERROR_INVALID_SHADER_NV, "Shader module does not contain an entry point named '%s'.", mtlFuncName.UTF8String);
     }
 
+	setLabelIfNotNil(mtlFunc, getDebugName());
+
 	return { mtlFunc, MTLSizeMake(getWorkgroupDimensionSize(_entryPoint.workgroupSize.width, pSpecializationInfo),
 								  getWorkgroupDimensionSize(_entryPoint.workgroupSize.height, pSpecializationInfo),
 								  getWorkgroupDimensionSize(_entryPoint.workgroupSize.depth, pSpecializationInfo)) };
@@ -107,6 +116,13 @@
     return nil;
 }
 
+void MVKShaderLibrary::setShaderModule(MVKShaderModule* shaderModule) {
+	if (shaderModule == _shaderModule) { return; }
+
+	_shaderModule = shaderModule;
+	propogateDebugName();
+}
+
 MVKShaderLibrary::MVKShaderLibrary(MVKVulkanAPIDeviceObject* owner, const string& mslSourceCode, const SPIRVEntryPoint& entryPoint) : _owner(owner) {
 	MVKShaderLibraryCompiler* slc = new MVKShaderLibraryCompiler(_owner);
 	_mtlLibrary = slc->newMTLLibrary(@(mslSourceCode.c_str()));	// retained
@@ -140,6 +156,7 @@
 	_mtlLibrary = [other._mtlLibrary retain];
 	_entryPoint = other._entryPoint;
 	_msl = other._msl;
+	setShaderModule(other._shaderModule);
 }
 
 // If err object is nil, the compilation succeeded without any warnings.
@@ -204,6 +221,7 @@
 														  const string& mslSourceCode,
 														  const SPIRVEntryPoint& entryPoint) {
 	MVKShaderLibrary* shLib = new MVKShaderLibrary(_owner, mslSourceCode, entryPoint);
+	shLib->setShaderModule(_shaderModule);
 	_shaderLibraries.emplace_back(*pContext, shLib);
 	return shLib;
 }
@@ -218,6 +236,13 @@
 	}
 }
 
+void MVKShaderLibraryCache::setShaderModule(MVKShaderModule* shaderModule) {
+	if (shaderModule == _shaderModule) { return; }
+
+	_shaderModule = shaderModule;
+	for (auto& slPair : _shaderLibraries) { slPair.second->setShaderModule(_shaderModule); }
+}
+
 MVKShaderLibraryCache::~MVKShaderLibraryCache() {
 	for (auto& slPair : _shaderLibraries) { slPair.second->destroy(); }
 }
@@ -310,6 +335,7 @@
 								 const VkShaderModuleCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device), _shaderLibraryCache(this) {
 
 	_defaultLibrary = nullptr;
+	_shaderLibraryCache.setShaderModule(this);
 
 
 	size_t codeSize = pCreateInfo->codeSize;
@@ -348,6 +374,7 @@
 
 			_spvConverter.setMSL(pMSLCode, nullptr);
 			_defaultLibrary = new MVKShaderLibrary(this, _spvConverter.getMSL().c_str(), _spvConverter.getEntryPoint());
+			_defaultLibrary->setShaderModule(this);
 
 			break;
 		}
@@ -362,6 +389,7 @@
 			_device->addActivityPerformance(_device->_performanceStatistics.shaderCompilation.hashShaderCode, startTime);
 
 			_defaultLibrary = new MVKShaderLibrary(this, (void*)(pMSLCode), mslCodeLen);
+			_defaultLibrary->setShaderModule(this);
 
 			break;
 		}
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
index ca8de1a..4258216 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.cpp
@@ -27,12 +27,6 @@
 using namespace mvk;
 using namespace std;
 
-// Verify that the spvAux structure used to pass auxilliary info between MoltenVK and SPIRV-Cross has not changed.
-#define MVK_SUPPORTED_MSL_AUX_BUFFER_STRUCT_VERSION		1
-#if MVK_SUPPORTED_MSL_AUX_BUFFER_STRUCT_VERSION != SPIRV_CROSS_MSL_AUX_BUFFER_STRUCT_VERSION
-#	error "The version number of the MSL spvAux struct used to pass auxilliary info to shaders does not match between MoltenVK and SPIRV-Cross. If the spvAux struct definition is not the same between MoltenVK and shaders created by SPRIV-Cross, memory errors will occur."
-#endif
-
 
 #pragma mark -
 #pragma mark SPIRVToMSLConverterContext
@@ -48,11 +42,12 @@
 	if (entryPointStage != other.entryPointStage) { return false; }
     if (mslVersion != other.mslVersion) { return false; }
 	if (texelBufferTextureWidth != other.texelBufferTextureWidth) { return false; }
-	if (auxBufferIndex != other.auxBufferIndex) { return false; }
+	if (swizzleBufferIndex != other.swizzleBufferIndex) { return false; }
 	if (indirectParamsBufferIndex != other.indirectParamsBufferIndex) { return false; }
 	if (outputBufferIndex != other.outputBufferIndex) { return false; }
 	if (patchOutputBufferIndex != other.patchOutputBufferIndex) { return false; }
 	if (tessLevelBufferIndex != other.tessLevelBufferIndex) { return false; }
+	if (bufferSizeBufferIndex != other.bufferSizeBufferIndex) { return false; }
 	if (inputThreadgroupMemIndex != other.inputThreadgroupMemIndex) { return false; }
     if (!!shouldFlipVertexY != !!other.shouldFlipVertexY) { return false; }
     if (!!isRenderingPoints != !!other.isRenderingPoints) { return false; }
@@ -165,9 +160,10 @@
 MVK_PUBLIC_SYMBOL void SPIRVToMSLConverterContext::alignWith(const SPIRVToMSLConverterContext& srcContext) {
 
 	options.isRasterizationDisabled = srcContext.options.isRasterizationDisabled;
-	options.needsAuxBuffer = srcContext.options.needsAuxBuffer;
+	options.needsSwizzleBuffer = srcContext.options.needsSwizzleBuffer;
 	options.needsOutputBuffer = srcContext.options.needsOutputBuffer;
 	options.needsPatchOutputBuffer = srcContext.options.needsPatchOutputBuffer;
+	options.needsBufferSizeBuffer = srcContext.options.needsBufferSizeBuffer;
 	options.needsInputThreadgroupMem = srcContext.options.needsInputThreadgroupMem;
 
 	if (stageSupportsVertexAttributes()) {
@@ -249,11 +245,12 @@
 		mslOpts.platform = getCompilerMSLPlatform(context.options.platform);
 		mslOpts.msl_version = context.options.mslVersion;
 		mslOpts.texel_buffer_texture_width = context.options.texelBufferTextureWidth;
-		mslOpts.aux_buffer_index = context.options.auxBufferIndex;
+		mslOpts.swizzle_buffer_index = context.options.swizzleBufferIndex;
 		mslOpts.indirect_params_buffer_index = context.options.indirectParamsBufferIndex;
 		mslOpts.shader_output_buffer_index = context.options.outputBufferIndex;
 		mslOpts.shader_patch_output_buffer_index = context.options.patchOutputBufferIndex;
 		mslOpts.shader_tess_factor_buffer_index = context.options.tessLevelBufferIndex;
+		mslOpts.buffer_size_buffer_index = context.options.bufferSizeBufferIndex;
 		mslOpts.shader_input_wg_index = context.options.inputThreadgroupMemIndex;
 		mslOpts.enable_point_size_builtin = context.options.isRenderingPoints;
 		mslOpts.disable_rasterization = context.options.isRasterizationDisabled;
@@ -324,9 +321,10 @@
 	// which vertex attributes and resource bindings are used by the shader
 	populateEntryPoint(_entryPoint, pMSLCompiler, context.options);
 	context.options.isRasterizationDisabled = pMSLCompiler && pMSLCompiler->get_is_rasterization_disabled();
-	context.options.needsAuxBuffer = pMSLCompiler && pMSLCompiler->needs_aux_buffer();
+	context.options.needsSwizzleBuffer = pMSLCompiler && pMSLCompiler->needs_swizzle_buffer();
 	context.options.needsOutputBuffer = pMSLCompiler && pMSLCompiler->needs_output_buffer();
 	context.options.needsPatchOutputBuffer = pMSLCompiler && pMSLCompiler->needs_patch_output_buffer();
+	context.options.needsBufferSizeBuffer = pMSLCompiler && pMSLCompiler->needs_buffer_size_buffer();
 	context.options.needsInputThreadgroupMem = pMSLCompiler && pMSLCompiler->needs_input_threadgroup_mem();
 
 	if (context.stageSupportsVertexAttributes()) {
diff --git a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
index 8a90dcc..61aa863 100644
--- a/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
+++ b/MoltenVKShaderConverter/MoltenVKSPIRVToMSLConverter/SPIRVToMSLConverter.h
@@ -50,11 +50,12 @@
         uint32_t mslVersion = makeMSLVersion(2, 1);
 		Platform platform = getNativePlatform();
 		uint32_t texelBufferTextureWidth = 4096;
-		uint32_t auxBufferIndex = 0;
+		uint32_t swizzleBufferIndex = 0;
 		uint32_t indirectParamsBufferIndex = 0;
 		uint32_t outputBufferIndex = 0;
 		uint32_t patchOutputBufferIndex = 0;
 		uint32_t tessLevelBufferIndex = 0;
+		uint32_t bufferSizeBufferIndex = 0;
 		uint32_t inputThreadgroupMemIndex = 0;
 		uint32_t numTessControlPoints = 0;
 		bool shouldFlipVertexY = true;
@@ -64,9 +65,10 @@
 		bool tessDomainOriginInLowerLeft = false;
 
 		bool isRasterizationDisabled = false;
-		bool needsAuxBuffer = false;
+		bool needsSwizzleBuffer = false;
 		bool needsOutputBuffer = false;
 		bool needsPatchOutputBuffer = false;
+		bool needsBufferSizeBuffer = false;
 		bool needsInputThreadgroupMem = false;
 
         /**