Consolidate tracking of resource bindings in MVKResourcesCommandEncoderState subclasses.

Redefine MVKGraphicsResourcesCommandEncoderState::ShaderStage
as MVKResourcesCommandEncoderState::ResourceBindings.
Add ResourceBindings::reset() function.
MVKComputeResourcesCommandEncoderState replace discrete member variables
with a single ResourceBindings member variable.
Rename MVKGraphicsResourcesCommandEncoderState::_shaderStage
member variable to _shaderStageResourceBindings.
In stage iterations, replace < kMVKShaderStageCompute with <= kMVKShaderStageFragment.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index 5ae018b..660c74e 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -417,6 +417,40 @@
 
 	void assertMissingSwizzles(bool needsSwizzle, const char* stageName, const MVKArrayRef<MVKMTLTextureBinding>& texBindings);
 
+	template<size_t N>
+	struct ResourceBindings {
+		MVKSmallVector<MVKMTLBufferBinding, N> bufferBindings;
+		MVKSmallVector<MVKMTLTextureBinding, N> textureBindings;
+		MVKSmallVector<MVKMTLSamplerStateBinding, N> samplerStateBindings;
+		MVKSmallVector<uint32_t, N> swizzleConstants;
+		MVKSmallVector<uint32_t, N> bufferSizes;
+
+		MVKMTLBufferBinding swizzleBufferBinding;
+		MVKMTLBufferBinding bufferSizeBufferBinding;
+
+		bool areBufferBindingsDirty = false;
+		bool areTextureBindingsDirty = false;
+		bool areSamplerStateBindingsDirty = false;
+
+		bool needsSwizzle = false;
+
+		void reset() {
+			bufferBindings.clear();
+			textureBindings.clear();
+			samplerStateBindings.clear();
+			swizzleConstants.clear();
+			bufferSizes.clear();
+
+			areBufferBindingsDirty = false;
+			areTextureBindingsDirty = false;
+			areSamplerStateBindingsDirty = false;
+			swizzleBufferBinding.isDirty = false;
+			bufferSizeBufferBinding.isDirty = false;
+
+			needsSwizzle = false;
+		}
+	};
+
 };
 
 
@@ -477,23 +511,7 @@
     void resetImpl() override;
     void markDirty() override;
 
-    struct ShaderStage {
-        MVKSmallVector<MVKMTLBufferBinding, 8> bufferBindings;
-        MVKSmallVector<MVKMTLTextureBinding, 8> textureBindings;
-        MVKSmallVector<MVKMTLSamplerStateBinding, 8> samplerStateBindings;
-        MVKSmallVector<uint32_t, 8> swizzleConstants;
-        MVKSmallVector<uint32_t, 8> bufferSizes;
-        MVKMTLBufferBinding swizzleBufferBinding;
-        MVKMTLBufferBinding bufferSizeBufferBinding;
-
-        bool areBufferBindingsDirty = false;
-        bool areTextureBindingsDirty = false;
-        bool areSamplerStateBindingsDirty = false;
-
-        bool needsSwizzle = false;
-    };
-
-    ShaderStage _shaderStages[4];
+    ResourceBindings<8> _shaderStageResourceBindings[4];
 };
 
 
@@ -531,19 +549,7 @@
     void encodeImpl(uint32_t) override;
     void resetImpl() override;
 
-    MVKSmallVector<MVKMTLBufferBinding, 4> _bufferBindings;
-    MVKSmallVector<MVKMTLTextureBinding, 4> _textureBindings;
-    MVKSmallVector<MVKMTLSamplerStateBinding, 4> _samplerStateBindings;
-    MVKSmallVector<uint32_t, 4> _swizzleConstants;
-    MVKSmallVector<uint32_t, 4> _bufferSizes;
-    MVKMTLBufferBinding _swizzleBufferBinding;
-    MVKMTLBufferBinding _bufferSizeBufferBinding;
-
-    bool _areBufferBindingsDirty = false;
-    bool _areTextureBindingsDirty = false;
-    bool _areSamplerStateBindingsDirty = false;
-
-    bool _needsSwizzle = false;
+	ResourceBindings<4> _resourceBindings;
 };
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 691303b..d63ba37 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -513,15 +513,15 @@
 #pragma mark MVKGraphicsResourcesCommandEncoderState
 
 void MVKGraphicsResourcesCommandEncoderState::bindBuffer(MVKShaderStage stage, const MVKMTLBufferBinding& binding) {
-    bind(binding, _shaderStages[stage].bufferBindings, _shaderStages[stage].areBufferBindingsDirty);
+    bind(binding, _shaderStageResourceBindings[stage].bufferBindings, _shaderStageResourceBindings[stage].areBufferBindingsDirty);
 }
 
 void MVKGraphicsResourcesCommandEncoderState::bindTexture(MVKShaderStage stage, const MVKMTLTextureBinding& binding) {
-    bind(binding, _shaderStages[stage].textureBindings, _shaderStages[stage].areTextureBindingsDirty, _shaderStages[stage].needsSwizzle);
+    bind(binding, _shaderStageResourceBindings[stage].textureBindings, _shaderStageResourceBindings[stage].areTextureBindingsDirty, _shaderStageResourceBindings[stage].needsSwizzle);
 }
 
 void MVKGraphicsResourcesCommandEncoderState::bindSamplerState(MVKShaderStage stage, const MVKMTLSamplerStateBinding& binding) {
-    bind(binding, _shaderStages[stage].samplerStateBindings, _shaderStages[stage].areSamplerStateBindingsDirty);
+    bind(binding, _shaderStageResourceBindings[stage].samplerStateBindings, _shaderStageResourceBindings[stage].areSamplerStateBindingsDirty);
 }
 
 void MVKGraphicsResourcesCommandEncoderState::bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
@@ -529,13 +529,13 @@
 																bool needTessCtlSwizzleBuffer,
 																bool needTessEvalSwizzleBuffer,
 																bool needFragmentSwizzleBuffer) {
-    for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
-        _shaderStages[i].swizzleBufferBinding.index = binding.stages[i];
+    for (uint32_t i = kMVKShaderStageVertex; i <= kMVKShaderStageFragment; i++) {
+        _shaderStageResourceBindings[i].swizzleBufferBinding.index = binding.stages[i];
     }
-    _shaderStages[kMVKShaderStageVertex].swizzleBufferBinding.isDirty = needVertexSwizzleBuffer;
-    _shaderStages[kMVKShaderStageTessCtl].swizzleBufferBinding.isDirty = needTessCtlSwizzleBuffer;
-    _shaderStages[kMVKShaderStageTessEval].swizzleBufferBinding.isDirty = needTessEvalSwizzleBuffer;
-    _shaderStages[kMVKShaderStageFragment].swizzleBufferBinding.isDirty = needFragmentSwizzleBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageVertex].swizzleBufferBinding.isDirty = needVertexSwizzleBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageTessCtl].swizzleBufferBinding.isDirty = needTessCtlSwizzleBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageTessEval].swizzleBufferBinding.isDirty = needTessEvalSwizzleBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageFragment].swizzleBufferBinding.isDirty = needFragmentSwizzleBuffer;
 }
 
 void MVKGraphicsResourcesCommandEncoderState::bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
@@ -543,13 +543,13 @@
 																   bool needTessCtlSizeBuffer,
 																   bool needTessEvalSizeBuffer,
 																   bool needFragmentSizeBuffer) {
-    for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
-        _shaderStages[i].bufferSizeBufferBinding.index = binding.stages[i];
+    for (uint32_t i = kMVKShaderStageVertex; i <= kMVKShaderStageFragment; i++) {
+        _shaderStageResourceBindings[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;
+    _shaderStageResourceBindings[kMVKShaderStageVertex].bufferSizeBufferBinding.isDirty = needVertexSizeBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageTessCtl].bufferSizeBufferBinding.isDirty = needTessCtlSizeBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageTessEval].bufferSizeBufferBinding.isDirty = needTessEvalSizeBuffer;
+    _shaderStageResourceBindings[kMVKShaderStageFragment].bufferSizeBufferBinding.isDirty = needFragmentSizeBuffer;
 }
 
 void MVKGraphicsResourcesCommandEncoderState::encodeBindings(MVKShaderStage stage,
@@ -559,7 +559,7 @@
                                                              std::function<void(MVKCommandEncoder*, MVKMTLBufferBinding&, const MVKArrayRef<uint32_t>&)> bindImplicitBuffer,
                                                              std::function<void(MVKCommandEncoder*, MVKMTLTextureBinding&)> bindTexture,
                                                              std::function<void(MVKCommandEncoder*, MVKMTLSamplerStateBinding&)> bindSampler) {
-    auto& shaderStage = _shaderStages[stage];
+    auto& shaderStage = _shaderStageResourceBindings[stage];
     encodeBinding<MVKMTLBufferBinding>(shaderStage.bufferBindings, shaderStage.areBufferBindingsDirty, bindBuffer);
 
     if (shaderStage.swizzleBufferBinding.isDirty) {
@@ -589,10 +589,10 @@
 // Mark everything as dirty
 void MVKGraphicsResourcesCommandEncoderState::markDirty() {
     MVKCommandEncoderState::markDirty();
-    for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
-        MVKResourcesCommandEncoderState::markDirty(_shaderStages[i].bufferBindings, _shaderStages[i].areBufferBindingsDirty);
-        MVKResourcesCommandEncoderState::markDirty(_shaderStages[i].textureBindings, _shaderStages[i].areTextureBindingsDirty);
-        MVKResourcesCommandEncoderState::markDirty(_shaderStages[i].samplerStateBindings, _shaderStages[i].areSamplerStateBindingsDirty);
+    for (uint32_t i = kMVKShaderStageVertex; i <= kMVKShaderStageFragment; i++) {
+        MVKResourcesCommandEncoderState::markDirty(_shaderStageResourceBindings[i].bufferBindings, _shaderStageResourceBindings[i].areBufferBindingsDirty);
+        MVKResourcesCommandEncoderState::markDirty(_shaderStageResourceBindings[i].textureBindings, _shaderStageResourceBindings[i].areTextureBindingsDirty);
+        MVKResourcesCommandEncoderState::markDirty(_shaderStageResourceBindings[i].samplerStateBindings, _shaderStageResourceBindings[i].areSamplerStateBindingsDirty);
     }
 }
 
@@ -723,21 +723,9 @@
 }
 
 void MVKGraphicsResourcesCommandEncoderState::resetImpl() {
-    for (uint32_t i = kMVKShaderStageVertex; i < kMVKShaderStageCompute; i++) {
-        _shaderStages[i].bufferBindings.clear();
-        _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].swizzleBufferBinding.isDirty = false;
-        _shaderStages[i].bufferSizeBufferBinding.isDirty = false;
-
-        _shaderStages[i].needsSwizzle = false;
-    }
+	for (uint32_t i = kMVKShaderStageVertex; i <= kMVKShaderStageFragment; i++) {
+		_shaderStageResourceBindings[i].reset();
+	}
 }
 
 
@@ -745,35 +733,35 @@
 #pragma mark MVKComputeResourcesCommandEncoderState
 
 void MVKComputeResourcesCommandEncoderState::bindBuffer(const MVKMTLBufferBinding& binding) {
-    bind(binding, _bufferBindings, _areBufferBindingsDirty);
+	bind(binding, _resourceBindings.bufferBindings, _resourceBindings.areBufferBindingsDirty);
 }
 
 void MVKComputeResourcesCommandEncoderState::bindTexture(const MVKMTLTextureBinding& binding) {
-    bind(binding, _textureBindings, _areTextureBindingsDirty, _needsSwizzle);
+    bind(binding, _resourceBindings.textureBindings, _resourceBindings.areTextureBindingsDirty, _resourceBindings.needsSwizzle);
 }
 
 void MVKComputeResourcesCommandEncoderState::bindSamplerState(const MVKMTLSamplerStateBinding& binding) {
-    bind(binding, _samplerStateBindings, _areSamplerStateBindingsDirty);
+    bind(binding, _resourceBindings.samplerStateBindings, _resourceBindings.areSamplerStateBindingsDirty);
 }
 
 void MVKComputeResourcesCommandEncoderState::bindSwizzleBuffer(const MVKShaderImplicitRezBinding& binding,
 															   bool needSwizzleBuffer) {
-    _swizzleBufferBinding.index = binding.stages[kMVKShaderStageCompute];
-    _swizzleBufferBinding.isDirty = needSwizzleBuffer;
+    _resourceBindings.swizzleBufferBinding.index = binding.stages[kMVKShaderStageCompute];
+    _resourceBindings.swizzleBufferBinding.isDirty = needSwizzleBuffer;
 }
 
 void MVKComputeResourcesCommandEncoderState::bindBufferSizeBuffer(const MVKShaderImplicitRezBinding& binding,
 																  bool needBufferSizeBuffer) {
-    _bufferSizeBufferBinding.index = binding.stages[kMVKShaderStageCompute];
-    _bufferSizeBufferBinding.isDirty = needBufferSizeBuffer;
+    _resourceBindings.bufferSizeBufferBinding.index = binding.stages[kMVKShaderStageCompute];
+    _resourceBindings.bufferSizeBufferBinding.isDirty = needBufferSizeBuffer;
 }
 
 // Mark everything as dirty
 void MVKComputeResourcesCommandEncoderState::markDirty() {
     MVKCommandEncoderState::markDirty();
-    MVKResourcesCommandEncoderState::markDirty(_bufferBindings, _areBufferBindingsDirty);
-    MVKResourcesCommandEncoderState::markDirty(_textureBindings, _areTextureBindingsDirty);
-    MVKResourcesCommandEncoderState::markDirty(_samplerStateBindings, _areSamplerStateBindingsDirty);
+    MVKResourcesCommandEncoderState::markDirty(_resourceBindings.bufferBindings, _resourceBindings.areBufferBindingsDirty);
+    MVKResourcesCommandEncoderState::markDirty(_resourceBindings.textureBindings, _resourceBindings.areTextureBindingsDirty);
+    MVKResourcesCommandEncoderState::markDirty(_resourceBindings.samplerStateBindings, _resourceBindings.areSamplerStateBindingsDirty);
 }
 
 void MVKComputeResourcesCommandEncoderState::encodeImpl(uint32_t) {
@@ -783,7 +771,7 @@
     if (pipeline)
         fullImageViewSwizzle = pipeline->fullImageViewSwizzle();
 
-    encodeBinding<MVKMTLBufferBinding>(_bufferBindings, _areBufferBindingsDirty,
+    encodeBinding<MVKMTLBufferBinding>(_resourceBindings.bufferBindings, _resourceBindings.areBufferBindingsDirty,
 									   [](MVKCommandEncoder* cmdEncoder, MVKMTLBufferBinding& b)->void {
 											if (b.isInline)
 												cmdEncoder->setComputeBytes(cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
@@ -796,40 +784,40 @@
 																											atIndex: b.index];
                                        });
 
-    if (_swizzleBufferBinding.isDirty) {
+    if (_resourceBindings.swizzleBufferBinding.isDirty) {
 
-		for (auto& b : _textureBindings) {
-			if (b.isDirty) { updateImplicitBuffer(_swizzleConstants, b.index, b.swizzle); }
+		for (auto& b : _resourceBindings.textureBindings) {
+			if (b.isDirty) { updateImplicitBuffer(_resourceBindings.swizzleConstants, b.index, b.swizzle); }
 		}
 
 		_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
-                                     _swizzleConstants.data(),
-                                     _swizzleConstants.size() * sizeof(uint32_t),
-                                     _swizzleBufferBinding.index);
+                                     _resourceBindings.swizzleConstants.data(),
+                                     _resourceBindings.swizzleConstants.size() * sizeof(uint32_t),
+                                     _resourceBindings.swizzleBufferBinding.index);
 
 	} else {
-		assertMissingSwizzles(_needsSwizzle && !fullImageViewSwizzle, "compute", _textureBindings.contents());
+		assertMissingSwizzles(_resourceBindings.needsSwizzle && !fullImageViewSwizzle, "compute", _resourceBindings.textureBindings.contents());
     }
 
-    if (_bufferSizeBufferBinding.isDirty) {
-		for (auto& b : _bufferBindings) {
-			if (b.isDirty) { updateImplicitBuffer(_bufferSizes, b.index, b.size); }
+    if (_resourceBindings.bufferSizeBufferBinding.isDirty) {
+		for (auto& b : _resourceBindings.bufferBindings) {
+			if (b.isDirty) { updateImplicitBuffer(_resourceBindings.bufferSizes, b.index, b.size); }
 		}
 
 		_cmdEncoder->setComputeBytes(_cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch),
-                                     _bufferSizes.data(),
-                                     _bufferSizes.size() * sizeof(uint32_t),
-                                     _bufferSizeBufferBinding.index);
+                                     _resourceBindings.bufferSizes.data(),
+                                     _resourceBindings.bufferSizes.size() * sizeof(uint32_t),
+                                     _resourceBindings.bufferSizeBufferBinding.index);
 
     }
 
-    encodeBinding<MVKMTLTextureBinding>(_textureBindings, _areTextureBindingsDirty,
+    encodeBinding<MVKMTLTextureBinding>(_resourceBindings.textureBindings, _resourceBindings.areTextureBindingsDirty,
                                         [](MVKCommandEncoder* cmdEncoder, MVKMTLTextureBinding& b)->void {
                                             [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setTexture: b.mtlTexture
 																										 atIndex: b.index];
                                         });
 
-    encodeBinding<MVKMTLSamplerStateBinding>(_samplerStateBindings, _areSamplerStateBindingsDirty,
+    encodeBinding<MVKMTLSamplerStateBinding>(_resourceBindings.samplerStateBindings, _resourceBindings.areSamplerStateBindingsDirty,
                                              [](MVKCommandEncoder* cmdEncoder, MVKMTLSamplerStateBinding& b)->void {
                                                  [cmdEncoder->getMTLComputeEncoder(kMVKCommandUseDispatch) setSamplerState: b.mtlSamplerState
 																												   atIndex: b.index];
@@ -837,19 +825,7 @@
 }
 
 void MVKComputeResourcesCommandEncoderState::resetImpl() {
-    _bufferBindings.clear();
-    _textureBindings.clear();
-    _samplerStateBindings.clear();
-    _swizzleConstants.clear();
-    _bufferSizes.clear();
-
-    _areBufferBindingsDirty = false;
-    _areTextureBindingsDirty = false;
-    _areSamplerStateBindingsDirty = false;
-    _swizzleBufferBinding.isDirty = false;
-    _bufferSizeBufferBinding.isDirty = false;
-
-	_needsSwizzle = false;
+	_resourceBindings.reset();
 }