Defer marking overridden descriptor buffer bindings to encoding time.

- MVKPushConstantsCommandEncoderState move marking descriptor buffer
  binding override from markDirty() to encodeImpl().
- MVKCommandEncoder::setXXXBytes() calls optionally mark overridden descriptor
  buffer bindings as dirty, allowing this functionality to be generalized.
- MVKMTLBufferBinding::update() inline buffers never update just offset
  because inline contents may have changed.
- MVKCmdClearAttachments mark specific overridden buffer bindings dirty
  instead of marking entire MVKGraphicsResourcesCommandEncoderState dirty.
- MVKResourcesCommandEncoderState::bind() don't mark entire
  MVKResourcesCommandEncoderState dirty unless the binding
  itself was marked dirty (unrelated optimization).
- Rename markPushConstantBinding() to markBufferIndexDirty().
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
index e432115..3253aa9 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdTransfer.mm
@@ -478,8 +478,6 @@
             bool isBlittingStencil = mvkIsAnyFlagEnabled(blitKey.srcAspect, (VK_IMAGE_ASPECT_STENCIL_BIT));
             id<MTLDepthStencilState> mtlDSS = cmdEncoder->getCommandEncodingPool()->getMTLDepthStencilState(isBlittingDepth, isBlittingStencil);
             
-            uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex);
-            
             mtlColorAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
             mtlDepthAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
             mtlStencilAttDesc.level = mvkIBR.region.dstSubresource.mipLevel;
@@ -540,7 +538,8 @@
                 [mtlRendEnc pushDebugGroup: @"vkCmdBlitImage"];
                 [mtlRendEnc setRenderPipelineState: mtlRPS];
                 [mtlRendEnc setDepthStencilState: mtlDSS];
-                cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices), vtxBuffIdx);
+                cmdEncoder->setVertexBytes(mtlRendEnc, mvkIBR.vertices, sizeof(mvkIBR.vertices),
+										   cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex));
                 if (isLayeredBlit) {
                     cmdEncoder->setVertexBytes(mtlRendEnc, &zIncr, sizeof(zIncr), 0);
                 }
@@ -1250,7 +1249,6 @@
 
 	MVKPixelFormats* pixFmts = cmdEncoder->getPixelFormats();
     MVKRenderSubpass* subpass = cmdEncoder->getSubpass();
-    uint32_t vtxBuffIdx = cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex);
 
     // Populate the render pipeline state attachment key with info from the subpass and framebuffer.
 	_rpsKey.mtlSampleCount = mvkSampleCountFromVkSampleCountFlagBits(subpass->getSampleCount());
@@ -1304,9 +1302,10 @@
     [mtlRendEnc setViewport: {0, 0, (double) fbExtent.width, (double) fbExtent.height, 0.0, 1.0}];
     [mtlRendEnc setScissorRect: {0, 0, fbExtent.width, fbExtent.height}];
 
-    cmdEncoder->setVertexBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0);
-    cmdEncoder->setFragmentBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0);
-    cmdEncoder->setVertexBytes(mtlRendEnc, vertices, vtxCnt * sizeof(vertices[0]), vtxBuffIdx);
+    cmdEncoder->setVertexBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0, true);
+    cmdEncoder->setFragmentBytes(mtlRendEnc, clearColors, sizeof(clearColors), 0, true);
+    cmdEncoder->setVertexBytes(mtlRendEnc, vertices, vtxCnt * sizeof(vertices[0]),
+							   cmdEncoder->getDevice()->getMetalBufferIndexForVertexAttributeBinding(kMVKVertexContentBufferIndex), true);
     [mtlRendEnc drawPrimitives: MTLPrimitiveTypeTriangle vertexStart: 0 vertexCount: vtxCnt];
     [mtlRendEnc popDebugGroup];
 
@@ -1334,7 +1333,6 @@
     cmdEncoder->_depthBiasState.markDirty();
     cmdEncoder->_viewportState.markDirty();
     cmdEncoder->_scissorState.markDirty();
-	cmdEncoder->_graphicsResourcesState.beginMetalRenderPass();
 }
 
 template class MVKCmdClearAttachments<1>;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 5502dc2..c5fb691 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -354,14 +354,29 @@
 	/** Returns the push constants associated with the specified shader stage. */
 	MVKPushConstantsCommandEncoderState* getPushConstants(VkShaderStageFlagBits shaderStage);
 
-    /** Copy bytes into the Metal encoder at a Metal vertex buffer index. */
-    void setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+    /**
+	 * Copy bytes into the Metal encoder at a Metal vertex buffer index, and optionally indicate
+	 * that this binding might override a desriptor binding. If so, the descriptor binding will
+	 * be marked dirty so that it will rebind before the next usage.
+	 */
+    void setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes,
+						NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
 
-    /** Copy bytes into the Metal encoder at a Metal fragment buffer index. */
-    void setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+	/**
+	 * Copy bytes into the Metal encoder at a Metal fragment buffer index, and optionally indicate
+	 * that this binding might override a desriptor binding. If so, the descriptor binding will
+	 * be marked dirty so that it will rebind before the next usage.
+	 */
+    void setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder, const void* bytes,
+						  NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
 
-    /** Copy bytes into the Metal encoder at a Metal compute buffer index. */
-    void setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder, const void* bytes, NSUInteger length, uint32_t mtlBuffIndex);
+	/**
+	 * Copy bytes into the Metal encoder at a Metal compute buffer index, and optionally indicate
+	 * that this binding might override a desriptor binding. If so, the descriptor binding will
+	 * be marked dirty so that it will rebind before the next usage.
+	 */
+    void setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder, const void* bytes,
+						 NSUInteger length, uint32_t mtlBuffIndex, bool descOverride = false);
 
     /** Get a temporary MTLBuffer that will be returned to a pool after the command buffer is finished. */
     const MVKMTLBufferAllocation* getTempMTLBuffer(NSUInteger length, bool isPrivate = false, bool isDedicated = false);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index e37a89a..0f4c5f5 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -836,37 +836,52 @@
 void MVKCommandEncoder::setVertexBytes(id<MTLRenderCommandEncoder> mtlEncoder,
                                        const void* bytes,
                                        NSUInteger length,
-                                       uint32_t mtlBuffIndex) {
+									   uint32_t mtlBuffIndex,
+									   bool descOverride) {
     if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setVertexBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
         [mtlEncoder setVertexBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
     }
+
+	if (descOverride) {
+		_graphicsResourcesState.markBufferIndexDirty(kMVKShaderStageVertex, mtlBuffIndex);
+	}
 }
 
 void MVKCommandEncoder::setFragmentBytes(id<MTLRenderCommandEncoder> mtlEncoder,
                                          const void* bytes,
                                          NSUInteger length,
-                                         uint32_t mtlBuffIndex) {
+										 uint32_t mtlBuffIndex,
+										 bool descOverride) {
     if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setFragmentBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
         [mtlEncoder setFragmentBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
     }
+
+	if (descOverride) {
+		_graphicsResourcesState.markBufferIndexDirty(kMVKShaderStageFragment, mtlBuffIndex);
+	}
 }
 
 void MVKCommandEncoder::setComputeBytes(id<MTLComputeCommandEncoder> mtlEncoder,
                                         const void* bytes,
                                         NSUInteger length,
-                                        uint32_t mtlBuffIndex) {
+                                        uint32_t mtlBuffIndex,
+										bool descOverride) {
     if (_pDeviceMetalFeatures->dynamicMTLBufferSize && length <= _pDeviceMetalFeatures->dynamicMTLBufferSize) {
         [mtlEncoder setBytes: bytes length: length atIndex: mtlBuffIndex];
     } else {
         const MVKMTLBufferAllocation* mtlBuffAlloc = copyToTempMTLBufferAllocation(bytes, length);
         [mtlEncoder setBuffer: mtlBuffAlloc->_mtlBuffer offset: mtlBuffAlloc->_offset atIndex: mtlBuffIndex];
     }
+
+	if (descOverride) {
+		_computeResourcesState.markBufferIndexDirty(mtlBuffIndex);
+	}
 }
 
 // Return the MTLBuffer allocation to the pool once the command buffer is done with it
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index f682927..166ae74 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -200,8 +200,6 @@
     /** Sets the index of the Metal buffer used to hold the push constants. */
     void setMTLBufferIndex(uint32_t mtlBufferIndex, bool pipelineStageUsesPushConstants);
 
-	void markDirty() override;
-
 	MVKPushConstantsCommandEncoderState(MVKCommandEncoder* cmdEncoder,
                                         VkShaderStageFlagBits shaderStage)
         : MVKCommandEncoderState(cmdEncoder), _shaderStage(shaderStage) {}
@@ -379,13 +377,14 @@
         bindingsDirtyFlag = true;
     }
 
-	// Find and mark dirty the binding that uses the index.
+	// Template function to find and mark dirty the binding that uses the index.
 	template<class T>
 	void markIndexDirty(T& bindings, bool& bindingsDirtyFlag, uint32_t index) {
 		for (auto& b : bindings) {
 			if (b.index == index) {
 				b.markDirty();
 				bindingsDirtyFlag = true;
+				MVKCommandEncoderState::markDirty();
 				return;
 			}
 		}
@@ -395,21 +394,23 @@
     // of bindings, and marks the binding, the vector, and this instance as dirty
     template<class T, class V>
     void bind(const T& b, V& bindings, bool& bindingsDirtyFlag) {
-
         if ( !b.mtlResource ) { return; }
 
-        MVKCommandEncoderState::markDirty();
-        bindingsDirtyFlag = true;
-
         for (auto& rb : bindings) {
 			if (rb.index == b.index) {
                 rb.update(b);
+				if (rb.isDirty) {
+					bindingsDirtyFlag = true;
+					MVKCommandEncoderState::markDirty();
+				}
                 return;
             }
         }
 
         bindings.push_back(b);
         bindings.back().markDirty();
+		bindingsDirtyFlag = true;
+		MVKCommandEncoderState::markDirty();
     }
 
 	// For texture bindings, we also keep track of whether any bindings need a texture swizzle
@@ -547,8 +548,8 @@
 	/** Offset all buffers for vertex attribute bindings with zero divisors by the given number of strides. */
 	void offsetZeroDivisorVertexBuffers(MVKGraphicsStage stage, MVKGraphicsPipeline* pipeline, uint32_t firstInstance);
 
-	/** Marks the use of the MTLBuffer binding index by a push constant. */
-	void markPushConstantBinding(MVKShaderStage stage, uint32_t mtlBufferIndex);
+	/** Marks dirty the buffer binding using the index. */
+	void markBufferIndexDirty(MVKShaderStage stage, uint32_t mtlBufferIndex);
 
 	void markDirty() override;
 
@@ -599,8 +600,8 @@
 										   MTLResourceUsage mtlUsage,
 										   MTLRenderStages mtlStages) override;
 
-	/** Marks the use of the MTLBuffer binding index by a push constant. */
-	void markPushConstantBinding(uint32_t mtlBufferIndex);
+	/** Marks dirty the buffer binding using the index. */
+	void markBufferIndexDirty(uint32_t mtlBufferIndex);
 
     void markDirty() override;
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 44440bc..8cf763a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -180,27 +180,6 @@
 	}
 }
 
-void MVKPushConstantsCommandEncoderState::markDirty() {
-	MVKCommandEncoderState::markDirty();
-	if (_pipelineStageUsesPushConstants) {
-		switch (_shaderStage) {
-			case VK_SHADER_STAGE_VERTEX_BIT:
-			case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
-			case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
-			case VK_SHADER_STAGE_FRAGMENT_BIT:
-				_cmdEncoder->_graphicsResourcesState.markPushConstantBinding(mvkShaderStageFromVkShaderStageFlagBits(_shaderStage),
-																			 _mtlBufferIndex);
-				break;
-			case VK_SHADER_STAGE_COMPUTE_BIT:
-				_cmdEncoder->_computeResourcesState.markPushConstantBinding(_mtlBufferIndex);
-				break;
-			default:
-				MVKAssert(false, "Unsupported shader stage: %d", _shaderStage);
-				break;
-		}
-	}
-}
-
 // At this point, I have been marked not-dirty, under the assumption that I will make changes to the encoder.
 // However, some of the paths below decide not to actually make any changes to the encoder. In that case,
 // I should remain dirty until I actually do make encoder changes.
@@ -215,13 +194,13 @@
                 _cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl),
                                              _pushConstants.data(),
                                              _pushConstants.size(),
-                                             _mtlBufferIndex);
+                                             _mtlBufferIndex, true);
 				_isDirty = false;	// Okay, I changed the encoder
 			} else if (!isTessellating() && stage == kMVKGraphicsStageRasterization) {
                 _cmdEncoder->setVertexBytes(_cmdEncoder->_mtlRenderEncoder,
                                             _pushConstants.data(),
                                             _pushConstants.size(),
-                                            _mtlBufferIndex);
+                                            _mtlBufferIndex, true);
 				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
@@ -230,7 +209,7 @@
                 _cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseTessellationVertexTessCtl),
                                              _pushConstants.data(),
                                              _pushConstants.size(),
-                                             _mtlBufferIndex);
+                                             _mtlBufferIndex, true);
 				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
@@ -239,7 +218,7 @@
                 _cmdEncoder->setVertexBytes(_cmdEncoder->_mtlRenderEncoder,
                                             _pushConstants.data(),
                                             _pushConstants.size(),
-                                            _mtlBufferIndex);
+                                            _mtlBufferIndex, true);
 				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
@@ -248,7 +227,7 @@
                 _cmdEncoder->setFragmentBytes(_cmdEncoder->_mtlRenderEncoder,
                                               _pushConstants.data(),
                                               _pushConstants.size(),
-                                              _mtlBufferIndex);
+                                              _mtlBufferIndex, true);
 				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
@@ -256,7 +235,7 @@
             _cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
                                          _pushConstants.data(),
                                          _pushConstants.size(),
-                                         _mtlBufferIndex);
+                                         _mtlBufferIndex, true);
 			_isDirty = false;	// Okay, I changed the encoder
             break;
         default:
@@ -1003,7 +982,7 @@
 	}
 }
 
-void MVKGraphicsResourcesCommandEncoderState::markPushConstantBinding(MVKShaderStage stage, uint32_t mtlBufferIndex) {
+void MVKGraphicsResourcesCommandEncoderState::markBufferIndexDirty(MVKShaderStage stage, uint32_t mtlBufferIndex) {
 	auto& stageRezBinds = _shaderStageResourceBindings[stage];
 	markIndexDirty(stageRezBinds.bufferBindings, stageRezBinds.areBufferBindingsDirty, mtlBufferIndex);
 }
@@ -1141,7 +1120,7 @@
 	}
 }
 
-void MVKComputeResourcesCommandEncoderState::markPushConstantBinding(uint32_t mtlBufferIndex) {
+void MVKComputeResourcesCommandEncoderState::markBufferIndexDirty(uint32_t mtlBufferIndex) {
 	markIndexDirty(_resourceBindings.bufferBindings, _resourceBindings.areBufferBindingsDirty, mtlBufferIndex);
 }
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
index a0f71d0..f250230 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLResourceBindings.h
@@ -75,12 +75,11 @@
     inline void markDirty() { justOffset = false; isDirty = true; }
 
     inline void update(const MVKMTLBufferBinding &other) {
-        if (mtlBuffer != other.mtlBuffer || size != other.size || isInline != other.isInline) {
+        if (mtlBuffer != other.mtlBuffer || size != other.size || other.isInline) {
             mtlBuffer = other.mtlBuffer;
             size = other.size;
             isInline = other.isInline;
             offset = other.offset;
-
             justOffset = false;
             isDirty = true;
         } else if (offset != other.offset) {