Merge pull request #1117 from billhollings/master

Allow binding descriptor set using layout different than it was created with.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 8a81724..eef87ee 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -25,6 +25,7 @@
   performance on *iOS* by allowing Metal to use lossless texture compression.
 - Move *Metal* drawable presentation from `MTLCommandBuffer` to `MTLDrawable`
   to improve performance and reduce blocking.
+- Allow binding descriptor set using layout different than it was created with.
 - Clarify documentation on mapping limitations for host-coherent image memory on *macOS*.
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
index 424a07f..d88c1f8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.h
@@ -86,17 +86,12 @@
 	/** Returns the immutable sampler at the index, or nullptr if immutable samplers are not used. */
 	MVKSampler* getImmutableSampler(uint32_t index);
 
-	/**
-	 * Encodes the descriptors in the descriptor set that are specified by this layout,
-	 * starting with the descriptor at the index, on the the command encoder.
-	 * Returns the number of descriptors that were encoded.
-	 */
-	uint32_t bind(MVKCommandEncoder* cmdEncoder,
-				  MVKDescriptorSet* descSet,
-				  uint32_t descStartIndex,
-				  MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-				  MVKArrayRef<uint32_t> dynamicOffsets,
-				  uint32_t baseDynamicOffsetIndex);
+	/** Encodes the descriptors in the descriptor set that are specified by this layout, */
+	void bind(MVKCommandEncoder* cmdEncoder,
+			  MVKDescriptorSet* descSet,
+			  MVKShaderResourceBinding& dslMTLRezIdxOffsets,
+			  MVKArrayRef<uint32_t> dynamicOffsets,
+			  uint32_t baseDynamicOffsetIndex);
 
     /** Encodes this binding layout and the specified descriptor on the specified command encoder immediately. */
     void push(MVKCommandEncoder* cmdEncoder,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
index 81e1387..74ce403 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptor.mm
@@ -79,23 +79,21 @@
 }
 
 // A null cmdEncoder can be passed to perform a validation pass
-uint32_t MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
-											 MVKDescriptorSet* descSet,
-											 uint32_t descStartIndex,
-											 MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-											 MVKArrayRef<uint32_t> dynamicOffsets,
-											 uint32_t baseDynamicOffsetIndex) {
+void MVKDescriptorSetLayoutBinding::bind(MVKCommandEncoder* cmdEncoder,
+										 MVKDescriptorSet* descSet,
+										 MVKShaderResourceBinding& dslMTLRezIdxOffsets,
+										 MVKArrayRef<uint32_t> dynamicOffsets,
+										 uint32_t baseDynamicOffsetIndex) {
 
 	// Establish the resource indices to use, by combining the offsets of the DSL and this DSL binding.
     MVKShaderResourceBinding mtlIdxs = _mtlResourceIndexOffsets + dslMTLRezIdxOffsets;
 
     uint32_t descCnt = getDescriptorCount();
     for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) {
-        MVKDescriptor* mvkDesc = descSet->getDescriptor(descStartIndex + descIdx);
-        mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage, mtlIdxs, dynamicOffsets,
-					  baseDynamicOffsetIndex + _dynamicOffsetIndex + descIdx);
+		MVKDescriptor* mvkDesc = descSet->getDescriptor(getBinding(), descIdx);
+        mvkDesc->bind(cmdEncoder, _info.descriptorType, descIdx, _applyToStage, mtlIdxs,
+					  dynamicOffsets, baseDynamicOffsetIndex + _dynamicOffsetIndex + descIdx);
     }
-    return descCnt;
 }
 
 template<typename T>
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 3f1c0ca..1f2ce42 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -85,11 +85,12 @@
 
 	void propagateDebugName() override {}
 	inline uint32_t getDescriptorCount() { return _descriptorCount; }
-	uint32_t getDescriptorIndex(uint32_t binding, uint32_t elementIndex);
+	inline uint32_t getDescriptorIndex(uint32_t binding, uint32_t elementIndex = 0) { return _bindingToDescriptorIndex[binding] + elementIndex; }
 	inline MVKDescriptorSetLayoutBinding* getBinding(uint32_t binding) { return &_bindings[_bindingToIndex[binding]]; }
 
 	MVKSmallVector<MVKDescriptorSetLayoutBinding> _bindings;
 	std::unordered_map<uint32_t, uint32_t> _bindingToIndex;
+	std::unordered_map<uint32_t, uint32_t> _bindingToDescriptorIndex;
 	MVKShaderResourceBinding _mtlResourceCounts;
 	uint32_t _descriptorCount;
 	uint32_t _dynamicDescriptorCount;
@@ -137,7 +138,7 @@
 	friend class MVKDescriptorPool;
 
 	void propagateDebugName() override {}
-	inline MVKDescriptor* getDescriptor(uint32_t index) { return _descriptors[index]; }
+	MVKDescriptor* getDescriptor(uint32_t binding, uint32_t elementIndex = 0);
 
 	MVKDescriptorSetLayout* _layout;
 	MVKDescriptorPool* _pool;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index a7aa0d7..ac6be1f 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -24,32 +24,18 @@
 #pragma mark -
 #pragma mark MVKDescriptorSetLayout
 
-// Look through the layout bindings looking for the binding number, accumulating the number
-// of descriptors in each layout binding as we go, then add the element index.
-uint32_t MVKDescriptorSetLayout::getDescriptorIndex(uint32_t binding, uint32_t elementIndex) {
-	uint32_t descIdx = 0;
-	for (auto& dslBind : _bindings) {
-		if (dslBind.getBinding() == binding) { break; }
-		descIdx += dslBind.getDescriptorCount();
-	}
-	return descIdx + elementIndex;
-}
-
 // A null cmdEncoder can be passed to perform a validation pass
 void MVKDescriptorSetLayout::bindDescriptorSet(MVKCommandEncoder* cmdEncoder,
-                                               MVKDescriptorSet* descSet,
-                                               MVKShaderResourceBinding& dslMTLRezIdxOffsets,
-                                               MVKArrayRef<uint32_t> dynamicOffsets,
-                                               uint32_t baseDynamicOffsetIndex) {
-    if (_isPushDescriptorLayout) return;
+											   MVKDescriptorSet* descSet,
+											   MVKShaderResourceBinding& dslMTLRezIdxOffsets,
+											   MVKArrayRef<uint32_t> dynamicOffsets,
+											   uint32_t baseDynamicOffsetIndex) {
+	if (_isPushDescriptorLayout) return;
 
 	clearConfigurationResult();
-    size_t bindCnt = _bindings.size();
-    for (uint32_t descIdx = 0, bindIdx = 0; bindIdx < bindCnt; bindIdx++) {
-		descIdx += _bindings[bindIdx].bind(cmdEncoder, descSet, descIdx,
-										   dslMTLRezIdxOffsets, dynamicOffsets,
-										   baseDynamicOffsetIndex);
-    }
+	for (auto& dslBind : _bindings) {
+		dslBind.bind(cmdEncoder, descSet, dslMTLRezIdxOffsets, dynamicOffsets, baseDynamicOffsetIndex);
+	}
 }
 
 static const void* getWriteParameters(VkDescriptorType type, const VkDescriptorImageInfo* pImageInfo,
@@ -186,40 +172,35 @@
                                                const VkDescriptorSetLayoutCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
     _isPushDescriptorLayout = (pCreateInfo->flags & VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR) != 0;
 
-	// Create the descriptor layout bindings
 	_descriptorCount = 0;
     _bindings.reserve(pCreateInfo->bindingCount);
-	struct SortInfo
-	{
-		const VkDescriptorSetLayoutBinding* binding;
-		uint32_t index;
-	};
-	std::vector<SortInfo> dynamicBindings;
-
+	MVKSmallVector<MVKDescriptorSetLayoutBinding*, 32> dynamicBindings;
     for (uint32_t i = 0; i < pCreateInfo->bindingCount; i++) {
-        _bindings.emplace_back(_device, this, &pCreateInfo->pBindings[i]);
-		_descriptorCount += _bindings.back().getDescriptorCount();
-        _bindingToIndex[pCreateInfo->pBindings[i].binding] = i;
-		if (pCreateInfo->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
-			pCreateInfo->pBindings[i].descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) {
-			dynamicBindings.push_back(SortInfo{ &pCreateInfo->pBindings[i], i });
+		auto* pBind = &pCreateInfo->pBindings[i];
+        _bindings.emplace_back(_device, this, pBind);
+		_bindingToIndex[pBind->binding] = i;
+		_bindingToDescriptorIndex[pBind->binding] = _descriptorCount;
+
+		MVKDescriptorSetLayoutBinding* mvkBind = &_bindings.back();
+		_descriptorCount += mvkBind->getDescriptorCount();
+		if (pBind->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC ||
+			pBind->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) {
+			dynamicBindings.push_back(mvkBind);
 		}
 	}
 
-	// Dynamic offsets are ordered by binding index when given to vkCmdBindDescriptorSets
-	// not by the order they are provided in the layout.
-	// So we want to sort by binding, and then count how many dynamic offsets the binding
-	// has so we can assign each binding it's first dynamic offset index
+	// Dynamic offsets in vkCmdBindDescriptorSets() are ordered by binding number, not by the
+	// order they are provided in the layout. To prepare for this, sort the dynamic bindings by
+	// binding number and assign to each the index it will use into the array of dynamic offsets.
 	std::sort(dynamicBindings.begin(), dynamicBindings.end(),
-				[](const SortInfo &info1, const SortInfo &info2) {
-					return info1.binding->binding < info2.binding->binding; });
+				[](MVKDescriptorSetLayoutBinding* mvkBind1, MVKDescriptorSetLayoutBinding* mvkBind2) {
+					return mvkBind1->getBinding() < mvkBind2->getBinding(); });
 
-	uint32_t curOffsetIndex = 0;
-	for (auto &i : dynamicBindings) {
-		_bindings[i.index].setDynamicOffsetIndex(curOffsetIndex);
-		curOffsetIndex += i.binding->descriptorCount;
+	_dynamicDescriptorCount = 0;
+	for (auto mvkBind : dynamicBindings) {
+		mvkBind->setDynamicOffsetIndex(_dynamicDescriptorCount);
+		_dynamicDescriptorCount += mvkBind->getDescriptorCount();
 	}
-	_dynamicDescriptorCount = curOffsetIndex;
 }
 
 
@@ -230,6 +211,10 @@
 	return _layout->getBinding(binding)->getDescriptorType();
 }
 
+MVKDescriptor* MVKDescriptorSet::getDescriptor(uint32_t binding, uint32_t elementIndex) {
+	return _descriptors[_layout->getDescriptorIndex(binding, elementIndex)];
+}
+
 template<typename DescriptorAction>
 void MVKDescriptorSet::write(const DescriptorAction* pDescriptorAction,
 							 size_t stride,
@@ -238,12 +223,10 @@
 	VkDescriptorType descType = getDescriptorType(pDescriptorAction->dstBinding);
 	uint32_t descCnt = pDescriptorAction->descriptorCount;
     if (descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) {
-        uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, 0);
-        // For inline buffers we are using the index argument as dst offset not as src descIdx
-        _descriptors[dstStartIdx]->write(this, descType, pDescriptorAction->dstArrayElement, stride, pData);
+        // For inline buffers dstArrayElement is a byte offset
+		getDescriptor(pDescriptorAction->dstBinding)->write(this, descType, pDescriptorAction->dstArrayElement, stride, pData);
     } else {
-        uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding,
-        pDescriptorAction->dstArrayElement);
+        uint32_t dstStartIdx = _layout->getDescriptorIndex(pDescriptorAction->dstBinding, pDescriptorAction->dstArrayElement);
         for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) {
             _descriptors[dstStartIdx + descIdx]->write(this, descType, descIdx, stride, pData);
         }
@@ -268,12 +251,10 @@
 	uint32_t descCnt = pDescriptorCopy->descriptorCount;
     if (descType == VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT) {
         pInlineUniformBlock->dataSize = pDescriptorCopy->descriptorCount;
-        uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, 0);
-        // For inline buffers we are using the index argument as src offset not as dst descIdx
-        _descriptors[srcStartIdx]->read(this, descType, pDescriptorCopy->srcArrayElement, pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock);
+		// For inline buffers dstArrayElement is a byte offset
+		getDescriptor(pDescriptorCopy->srcBinding)->read(this, descType, pDescriptorCopy->srcArrayElement, pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock);
     } else {
-        uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding,
-                                                           pDescriptorCopy->srcArrayElement);
+        uint32_t srcStartIdx = _layout->getDescriptorIndex(pDescriptorCopy->srcBinding, pDescriptorCopy->srcArrayElement);
         for (uint32_t descIdx = 0; descIdx < descCnt; descIdx++) {
             _descriptors[srcStartIdx + descIdx]->read(this, descType, descIdx, pImageInfo, pBufferInfo, pTexelBufferView, pInlineUniformBlock);
         }