Refactor preallocated descriptor handling.

Add MVKBitArray class.
Consolidate MVKPreallocatedDescriptors into MVKDescriptorPool
and remove MVKPreallocatedDescriptors.
diff --git a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
index 6f2da81..7c72906 100644
--- a/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
+++ b/MoltenVK/MoltenVK.xcodeproj/project.pbxproj
@@ -325,6 +325,9 @@
 		A9C96DD31DDC20C20053187F /* MVKMTLBufferAllocation.mm in Sources */ = {isa = PBXBuildFile; fileRef = A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */; };
 		A9CEAAD5227378D400FAF779 /* mvk_datatypes.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9CEAAD1227378D400FAF779 /* mvk_datatypes.hpp */; };
 		A9CEAAD6227378D400FAF779 /* mvk_datatypes.hpp in Headers */ = {isa = PBXBuildFile; fileRef = A9CEAAD1227378D400FAF779 /* mvk_datatypes.hpp */; };
+		A9D7104F25CDE05E00E38106 /* MVKBitArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D7104E25CDE05E00E38106 /* MVKBitArray.h */; };
+		A9D7105025CDE05E00E38106 /* MVKBitArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D7104E25CDE05E00E38106 /* MVKBitArray.h */; };
+		A9D7105125CDE05E00E38106 /* MVKBitArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A9D7104E25CDE05E00E38106 /* MVKBitArray.h */; };
 		A9E4B7891E1D8AF10046A4CE /* MVKMTLResourceBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = A9E4B7881E1D8AF10046A4CE /* MVKMTLResourceBindings.h */; };
 		A9E4B78A1E1D8AF10046A4CE /* MVKMTLResourceBindings.h in Headers */ = {isa = PBXBuildFile; fileRef = A9E4B7881E1D8AF10046A4CE /* MVKMTLResourceBindings.h */; };
 		A9E53DD72100B197002781DD /* MTLSamplerDescriptor+MoltenVK.m in Sources */ = {isa = PBXBuildFile; fileRef = A9E53DCD2100B197002781DD /* MTLSamplerDescriptor+MoltenVK.m */; };
@@ -523,6 +526,7 @@
 		A9C96DCF1DDC20C20053187F /* MVKMTLBufferAllocation.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MVKMTLBufferAllocation.mm; sourceTree = "<group>"; };
 		A9CBEE011B6299D800E45FDC /* libMoltenVK.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libMoltenVK.a; sourceTree = BUILT_PRODUCTS_DIR; };
 		A9CEAAD1227378D400FAF779 /* mvk_datatypes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = mvk_datatypes.hpp; sourceTree = "<group>"; };
+		A9D7104E25CDE05E00E38106 /* MVKBitArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKBitArray.h; sourceTree = "<group>"; };
 		A9DE1083200598C500F18F80 /* icd */ = {isa = PBXFileReference; lastKnownFileType = folder; path = icd; sourceTree = "<group>"; };
 		A9E4B7881E1D8AF10046A4CE /* MVKMTLResourceBindings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MVKMTLResourceBindings.h; sourceTree = "<group>"; };
 		A9E53DCD2100B197002781DD /* MTLSamplerDescriptor+MoltenVK.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MTLSamplerDescriptor+MoltenVK.m"; sourceTree = "<group>"; };
@@ -678,6 +682,7 @@
 			children = (
 				A98149421FB6A3F7005F00B4 /* MVKBaseObject.h */,
 				A98149411FB6A3F7005F00B4 /* MVKBaseObject.mm */,
+				A9D7104E25CDE05E00E38106 /* MVKBitArray.h */,
 				4553AEFA2251617100E8EBCD /* MVKBlockObserver.h */,
 				4553AEF62251617100E8EBCD /* MVKBlockObserver.m */,
 				45557A4D21C9EFF3008868BD /* MVKCodec.cpp */,
@@ -792,6 +797,7 @@
 				2FEA0A4624902F9F00EEF3AD /* MVKSurface.h in Headers */,
 				2FEA0A4724902F9F00EEF3AD /* MTLRenderPipelineDescriptor+MoltenVK.h in Headers */,
 				2FEA0A4824902F9F00EEF3AD /* MVKInstance.h in Headers */,
+				A9D7105025CDE05E00E38106 /* MVKBitArray.h in Headers */,
 				2FEA0A4924902F9F00EEF3AD /* MVKCommandResourceFactory.h in Headers */,
 				2FEA0A4A24902F9F00EEF3AD /* MVKQueryPool.h in Headers */,
 				2FEA0A4B24902F9F00EEF3AD /* MVKCommandEncoderState.h in Headers */,
@@ -868,6 +874,7 @@
 				A95B7D691D3EE486003183D3 /* MVKCommandEncoderState.h in Headers */,
 				A94FB7D81C7DFB4800632CA3 /* MVKCommandPipelineStateFactoryShaderSource.h in Headers */,
 				A94FB7E01C7DFB4800632CA3 /* MVKDescriptorSet.h in Headers */,
+				A9D7104F25CDE05E00E38106 /* MVKBitArray.h in Headers */,
 				A9E53DE12100B197002781DD /* NSString+MoltenVK.h in Headers */,
 				A9E53DDF2100B197002781DD /* CAMetalLayer+MoltenVK.h in Headers */,
 				45557A5421C9EFF3008868BD /* MVKCodec.h in Headers */,
@@ -940,6 +947,7 @@
 				A95B7D6A1D3EE486003183D3 /* MVKCommandEncoderState.h in Headers */,
 				A94FB7D91C7DFB4800632CA3 /* MVKCommandPipelineStateFactoryShaderSource.h in Headers */,
 				A94FB7E11C7DFB4800632CA3 /* MVKDescriptorSet.h in Headers */,
+				A9D7105125CDE05E00E38106 /* MVKBitArray.h in Headers */,
 				A9E53DE22100B197002781DD /* NSString+MoltenVK.h in Headers */,
 				A9E53DE02100B197002781DD /* CAMetalLayer+MoltenVK.h in Headers */,
 				45557A5521C9EFF3008868BD /* MVKCodec.h in Headers */,
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index ae6048e..82287ef 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -20,6 +20,7 @@
 
 #include "MVKDescriptor.h"
 #include "MVKSmallVector.h"
+#include "MVKBitArray.h"
 #include <unordered_set>
 #include <unordered_map>
 #include <vector>
@@ -155,59 +156,20 @@
 
 public:
 
-	/** Returns the Vulkan API opaque object controlling this object. */
 	MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; };
 
-	MVKDescriptorTypePreallocation(const VkDescriptorPoolCreateInfo* pCreateInfo,
-								   VkDescriptorType descriptorType);
-
-protected:
-	friend class MVKPreallocatedDescriptors;
-
-	VkResult allocateDescriptor(MVKDescriptor** pMVKDesc);
-	bool findDescriptor(uint32_t endIndex, MVKDescriptor** pMVKDesc);
-	void freeDescriptor(MVKDescriptor* mvkDesc);
-	void reset();
-
-	MVKSmallVector<DescriptorClass> _descriptors;
-	MVKSmallVector<bool> _availability;
-	uint32_t _nextAvailableIndex;
-	bool _supportAvailability;
-};
-
-
-#pragma mark -
-#pragma mark MVKPreallocatedDescriptors
-
-/** Support class for MVKDescriptorPool that holds preallocated instances of all concrete descriptor classes. */
-class MVKPreallocatedDescriptors : public MVKBaseObject {
-
-public:
-
-	/** Returns the Vulkan API opaque object controlling this object. */
-	MVKVulkanAPIObject* getVulkanAPIObject() override { return nullptr; };
-
-	MVKPreallocatedDescriptors(const VkDescriptorPoolCreateInfo* pCreateInfo);
+	MVKDescriptorTypePreallocation(size_t poolSize);
 
 protected:
 	friend class MVKDescriptorPool;
 
-	VkResult allocateDescriptor(VkDescriptorType descriptorType, MVKDescriptor** pMVKDesc);
+	inline bool isPreallocated() { return _availability.size() > 0; }
+	VkResult allocateDescriptor(MVKDescriptor** pMVKDesc);
 	void freeDescriptor(MVKDescriptor* mvkDesc);
 	void reset();
 
-	MVKDescriptorTypePreallocation<MVKUniformBufferDescriptor> _uniformBufferDescriptors;
-	MVKDescriptorTypePreallocation<MVKStorageBufferDescriptor> _storageBufferDescriptors;
-	MVKDescriptorTypePreallocation<MVKUniformBufferDynamicDescriptor> _uniformBufferDynamicDescriptors;
-	MVKDescriptorTypePreallocation<MVKStorageBufferDynamicDescriptor> _storageBufferDynamicDescriptors;
-	MVKDescriptorTypePreallocation<MVKInlineUniformBlockDescriptor> _inlineUniformBlockDescriptors;
-	MVKDescriptorTypePreallocation<MVKSampledImageDescriptor> _sampledImageDescriptors;
-	MVKDescriptorTypePreallocation<MVKStorageImageDescriptor> _storageImageDescriptors;
-	MVKDescriptorTypePreallocation<MVKInputAttachmentDescriptor> _inputAttachmentDescriptors;
-	MVKDescriptorTypePreallocation<MVKSamplerDescriptor> _samplerDescriptors;
-	MVKDescriptorTypePreallocation<MVKCombinedImageSamplerDescriptor> _combinedImageSamplerDescriptors;
-	MVKDescriptorTypePreallocation<MVKUniformTexelBufferDescriptor> _uniformTexelBufferDescriptors;
-	MVKDescriptorTypePreallocation<MVKStorageTexelBufferDescriptor> _storageTexelBufferDescriptors;
+	MVKSmallVector<DescriptorClass> _descriptors;
+	MVKBitArray _availability;
 };
 
 
@@ -251,7 +213,19 @@
 
 	uint32_t _maxSets;
 	std::unordered_set<MVKDescriptorSet*> _allocatedSets;
-	MVKPreallocatedDescriptors* _preallocatedDescriptors;
+
+	MVKDescriptorTypePreallocation<MVKUniformBufferDescriptor> _uniformBufferDescriptors;
+	MVKDescriptorTypePreallocation<MVKStorageBufferDescriptor> _storageBufferDescriptors;
+	MVKDescriptorTypePreallocation<MVKUniformBufferDynamicDescriptor> _uniformBufferDynamicDescriptors;
+	MVKDescriptorTypePreallocation<MVKStorageBufferDynamicDescriptor> _storageBufferDynamicDescriptors;
+	MVKDescriptorTypePreallocation<MVKInlineUniformBlockDescriptor> _inlineUniformBlockDescriptors;
+	MVKDescriptorTypePreallocation<MVKSampledImageDescriptor> _sampledImageDescriptors;
+	MVKDescriptorTypePreallocation<MVKStorageImageDescriptor> _storageImageDescriptors;
+	MVKDescriptorTypePreallocation<MVKInputAttachmentDescriptor> _inputAttachmentDescriptors;
+	MVKDescriptorTypePreallocation<MVKSamplerDescriptor> _samplerDescriptors;
+	MVKDescriptorTypePreallocation<MVKCombinedImageSamplerDescriptor> _combinedImageSamplerDescriptors;
+	MVKDescriptorTypePreallocation<MVKUniformTexelBufferDescriptor> _uniformTexelBufferDescriptors;
+	MVKDescriptorTypePreallocation<MVKStorageTexelBufferDescriptor> _storageTexelBufferDescriptors;
 };
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index b7a5f96..5630f4e 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -312,217 +312,47 @@
 #pragma mark -
 #pragma mark MVKDescriptorTypePreallocation
 
+// If preallocated, find the next availalble descriptor.
+// If not preallocated, create one on the fly.
 template<class DescriptorClass>
 VkResult MVKDescriptorTypePreallocation<DescriptorClass>::allocateDescriptor(MVKDescriptor** pMVKDesc) {
-
-	uint32_t descCnt = (uint32_t)_descriptors.size();
-
-	// Preallocated descriptors that CANNOT be freed.
-	// Next available index can only monotonically increase towards the limit.
-	if ( !_supportAvailability ) {
-		if (_nextAvailableIndex < descCnt) {
-			*pMVKDesc = &_descriptors[_nextAvailableIndex++];
-			return VK_SUCCESS;
-		} else {
-			return VK_ERROR_OUT_OF_POOL_MEMORY;
-		}
+	DescriptorClass* mvkDesc;
+	if (isPreallocated()) {
+		size_t availDescIdx = _availability.getIndexOfFirstSetBit(true);
+		if (availDescIdx >= _availability.size()) { return VK_ERROR_OUT_OF_POOL_MEMORY; }
+		mvkDesc = &_descriptors[availDescIdx];
+		mvkDesc->reset();		// Clear before reusing.
+	} else {
+		mvkDesc = new DescriptorClass();
 	}
-
-	// Descriptors that CAN be freed.
-	// An available index might exist anywhere in the pool of descriptors.
-	uint32_t origNextAvailPoolIdx = _nextAvailableIndex;
-
-	// First start looking from most recently found available slot
-	if (findDescriptor(descCnt, pMVKDesc)) { return VK_SUCCESS; }
-
-	// Then look from beginning of the collection, in case any previous descriptors were freed
-	_nextAvailableIndex = 0;
-	if (findDescriptor(origNextAvailPoolIdx, pMVKDesc)) { return VK_SUCCESS; }
-
-	return VK_ERROR_OUT_OF_POOL_MEMORY;
+	*pMVKDesc = mvkDesc;
+	return VK_SUCCESS;
 }
 
-// Find a descriptor within a range in a preallocated collection based on availability,
-// and return true if found, false if not
-template<typename DescriptorClass>
-bool MVKDescriptorTypePreallocation<DescriptorClass>::findDescriptor(uint32_t endIndex,
-																	 MVKDescriptor** pMVKDesc) {
-	while (_nextAvailableIndex < endIndex) {
-		if (_availability[_nextAvailableIndex]) {
-			_availability[_nextAvailableIndex] = false;
-			*pMVKDesc = &_descriptors[_nextAvailableIndex];
-			_nextAvailableIndex++;
-			return true;
-		}
-		_nextAvailableIndex++;
-	}
-	return false;
-}
-
-// Reset a descriptor and mark it available, if applicable
+// If preallocated, descriptors are held in contiguous memory, so the index of the returning
+// descriptor can be calculated by pointer differences, and it can be marked as available.
+// The descriptor will be reset when it is re-allocated. This streamlines the reset() of this pool.
+// If not preallocated, simply destroy returning descriptor.
 template<typename DescriptorClass>
 void MVKDescriptorTypePreallocation<DescriptorClass>::freeDescriptor(MVKDescriptor* mvkDesc) {
-
-	mvkDesc->reset();
-
-	if (_supportAvailability) {
-		bool found = false;
-		size_t descCnt = _descriptors.size();
-		for (uint32_t descIdx = 0; !found && descIdx < descCnt; descIdx++) {
-			if (&_descriptors[descIdx] == mvkDesc) {
-				found = true;
-				_availability[descIdx] = true;
-			}
-		}
+	if (isPreallocated()) {
+		size_t descIdx = (DescriptorClass*)mvkDesc - _descriptors.data();
+		_availability.setBit(descIdx);
+	} else {
+		mvkDesc->destroy();
 	}
 }
 
+// Preallocated descriptors will be reset when they are reused
 template<typename DescriptorClass>
 void MVKDescriptorTypePreallocation<DescriptorClass>::reset() {
-	_nextAvailableIndex = 0;
+	_availability.setAllBits();
 }
 
 template<typename DescriptorClass>
-MVKDescriptorTypePreallocation<DescriptorClass>::MVKDescriptorTypePreallocation(const VkDescriptorPoolCreateInfo* pCreateInfo,
-																				VkDescriptorType descriptorType) {
-	// There may be more than  one poolSizeCount instance for the desired VkDescriptorType.
-	// Accumulate the descriptor count for the desired VkDescriptorType, and size the collections accordingly.
-	uint32_t descriptorCount = 0;
-	uint32_t poolCnt = pCreateInfo->poolSizeCount;
-	for (uint32_t poolIdx = 0; poolIdx < poolCnt; poolIdx++) {
-		auto& poolSize = pCreateInfo->pPoolSizes[poolIdx];
-		if (poolSize.type == descriptorType) { descriptorCount += poolSize.descriptorCount; }
-	}
-	_descriptors.resize(descriptorCount);
-
-	// Determine whether we need to track the availability of previously freed descriptors.
-	_supportAvailability = mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT);
-	if (_supportAvailability) { _availability.resize(descriptorCount, true); }
-	_nextAvailableIndex = 0;
-}
-
-
-#pragma mark -
-#pragma mark MVKPreallocatedDescriptors
-
-// Allocate a descriptor of the specified type
-VkResult MVKPreallocatedDescriptors::allocateDescriptor(VkDescriptorType descriptorType,
-														MVKDescriptor** pMVKDesc) {
-	switch (descriptorType) {
-		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
-			return _uniformBufferDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
-			return _storageBufferDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
-			return _uniformBufferDynamicDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
-			return _storageBufferDynamicDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
-			return _inlineUniformBlockDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
-			return _sampledImageDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
-			return _storageImageDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
-			return _inputAttachmentDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_SAMPLER:
-			return _samplerDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
-			return _combinedImageSamplerDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
-			return _uniformTexelBufferDescriptors.allocateDescriptor(pMVKDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
-			return _storageTexelBufferDescriptors.allocateDescriptor(pMVKDesc);
-
-		default:
-			return reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType);
-	}
-}
-
-void MVKPreallocatedDescriptors::freeDescriptor(MVKDescriptor* mvkDesc) {
-	VkDescriptorType descriptorType = mvkDesc->getDescriptorType();
-	switch (descriptorType) {
-		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
-			return _uniformBufferDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
-			return _storageBufferDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
-			return _uniformBufferDynamicDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
-			return _storageBufferDynamicDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
-			return _inlineUniformBlockDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
-			return _sampledImageDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
-			return _storageImageDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
-			return _inputAttachmentDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_SAMPLER:
-			return _samplerDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
-			return _combinedImageSamplerDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
-			return _uniformTexelBufferDescriptors.freeDescriptor(mvkDesc);
-
-		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
-			return _storageTexelBufferDescriptors.freeDescriptor(mvkDesc);
-
-		default:
-			reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType);
-	}
-}
-
-void MVKPreallocatedDescriptors::reset() {
-	_uniformBufferDescriptors.reset();
-	_storageBufferDescriptors.reset();
-	_uniformBufferDynamicDescriptors.reset();
-	_storageBufferDynamicDescriptors.reset();
-	_inlineUniformBlockDescriptors.reset();
-	_sampledImageDescriptors.reset();
-	_storageImageDescriptors.reset();
-	_inputAttachmentDescriptors.reset();
-	_samplerDescriptors.reset();
-	_combinedImageSamplerDescriptors.reset();
-	_uniformTexelBufferDescriptors.reset();
-	_storageTexelBufferDescriptors.reset();
-}
-
-MVKPreallocatedDescriptors::MVKPreallocatedDescriptors(const VkDescriptorPoolCreateInfo* pCreateInfo) :
-	_uniformBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER),
-	_storageBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER),
-	_uniformBufferDynamicDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC),
-	_storageBufferDynamicDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC),
-	_inlineUniformBlockDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT),
-	_sampledImageDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE),
-	_storageImageDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE),
-	_inputAttachmentDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT),
-	_samplerDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLER),
-	_combinedImageSamplerDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER),
-	_uniformTexelBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER),
-	_storageTexelBufferDescriptors(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER) {
-}
+MVKDescriptorTypePreallocation<DescriptorClass>::MVKDescriptorTypePreallocation(size_t poolSize) :
+	_descriptors(poolSize),
+	_availability(poolSize, true) {}
 
 
 #pragma mark -
@@ -578,11 +408,24 @@
 	return VK_SUCCESS;
 }
 
-// Destroy all allocated descriptor sets
+// Destroy all allocated descriptor sets and reset descriptor pools
 VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) {
 	for (auto& mvkDS : _allocatedSets) { freeDescriptorSet(mvkDS); }
 	_allocatedSets.clear();
-	if (_preallocatedDescriptors) { _preallocatedDescriptors->reset(); }
+
+	_uniformBufferDescriptors.reset();
+	_storageBufferDescriptors.reset();
+	_uniformBufferDynamicDescriptors.reset();
+	_storageBufferDynamicDescriptors.reset();
+	_inlineUniformBlockDescriptors.reset();
+	_sampledImageDescriptors.reset();
+	_storageImageDescriptors.reset();
+	_inputAttachmentDescriptors.reset();
+	_samplerDescriptors.reset();
+	_combinedImageSamplerDescriptors.reset();
+	_uniformTexelBufferDescriptors.reset();
+	_storageTexelBufferDescriptors.reset();
+
 	return VK_SUCCESS;
 }
 
@@ -606,87 +449,126 @@
 // Allocate a descriptor of the specified type
 VkResult MVKDescriptorPool::allocateDescriptor(VkDescriptorType descriptorType,
 											   MVKDescriptor** pMVKDesc) {
-
-	// If descriptors are preallocated allocate from the preallocated pools
-	if (_preallocatedDescriptors) {
-		return _preallocatedDescriptors->allocateDescriptor(descriptorType, pMVKDesc);
-	}
-
-	// Otherwise instantiate one of the appropriate type now
 	switch (descriptorType) {
 		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
-			*pMVKDesc = new MVKUniformBufferDescriptor();
-			break;
+			return _uniformBufferDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
-			*pMVKDesc = new MVKStorageBufferDescriptor();
-			break;
+			return _storageBufferDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
-			*pMVKDesc = new MVKUniformBufferDynamicDescriptor();
-			break;
+			return _uniformBufferDynamicDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
-			*pMVKDesc = new MVKStorageBufferDynamicDescriptor();
-			break;
+			return _storageBufferDynamicDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
-			*pMVKDesc = new MVKInlineUniformBlockDescriptor();
-			break;
+			return _inlineUniformBlockDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
-			*pMVKDesc = new MVKSampledImageDescriptor();
-			break;
+			return _sampledImageDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
-			*pMVKDesc = new MVKStorageImageDescriptor();
-			break;
+			return _storageImageDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
-			*pMVKDesc = new MVKInputAttachmentDescriptor();
-			break;
+			return _inputAttachmentDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_SAMPLER:
-			*pMVKDesc = new MVKSamplerDescriptor();
-			break;
+			return _samplerDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
-			*pMVKDesc = new MVKCombinedImageSamplerDescriptor();
-			break;
+			return _combinedImageSamplerDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
-			*pMVKDesc = new MVKUniformTexelBufferDescriptor();
-			break;
+			return _uniformTexelBufferDescriptors.allocateDescriptor(pMVKDesc);
 
 		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
-			*pMVKDesc = new MVKStorageTexelBufferDescriptor();
-			break;
+			return _storageTexelBufferDescriptors.allocateDescriptor(pMVKDesc);
 
 		default:
 			return reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType);
 	}
-	return VK_SUCCESS;
 }
 
-// Free a descriptor, either through the preallocated pool, or directly destroy it
 void MVKDescriptorPool::freeDescriptor(MVKDescriptor* mvkDesc) {
-	if (_preallocatedDescriptors) {
-		_preallocatedDescriptors->freeDescriptor(mvkDesc);
-	} else {
-		mvkDesc->destroy();
+	VkDescriptorType descriptorType = mvkDesc->getDescriptorType();
+	switch (descriptorType) {
+		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
+			return _uniformBufferDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
+			return _storageBufferDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
+			return _uniformBufferDynamicDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
+			return _storageBufferDynamicDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT:
+			return _inlineUniformBlockDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
+			return _sampledImageDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
+			return _storageImageDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
+			return _inputAttachmentDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_SAMPLER:
+			return _samplerDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
+			return _combinedImageSamplerDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
+			return _uniformTexelBufferDescriptors.freeDescriptor(mvkDesc);
+
+		case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
+			return _storageTexelBufferDescriptors.freeDescriptor(mvkDesc);
+
+		default:
+			reportError(VK_ERROR_INITIALIZATION_FAILED, "Unrecognized VkDescriptorType %d.", descriptorType);
 	}
 }
 
-MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device,
-									 const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
-	_maxSets = pCreateInfo->maxSets;
-	_preallocatedDescriptors = mvkGetMVKConfiguration()->preallocateDescriptors ? new MVKPreallocatedDescriptors(pCreateInfo) : nullptr;
+// Return the size of the preallocated pool for descriptors of the specified type,
+// or zero if we are not preallocating descriptors in the pool.
+// There may be more than one poolSizeCount instance for the desired VkDescriptorType.
+// Accumulate the descriptor count for the desired VkDescriptorType accordingly.
+static size_t getPoolSize(const VkDescriptorPoolCreateInfo* pCreateInfo, VkDescriptorType descriptorType) {
+	uint32_t descCnt = 0;
+	if (mvkGetMVKConfiguration()->preallocateDescriptors) {
+		uint32_t poolCnt = pCreateInfo->poolSizeCount;
+		for (uint32_t poolIdx = 0; poolIdx < poolCnt; poolIdx++) {
+			auto& poolSize = pCreateInfo->pPoolSizes[poolIdx];
+			if (poolSize.type == descriptorType) { descCnt += poolSize.descriptorCount; }
+		}
+	}
+	return descCnt;
 }
 
-// Destroy all allocated descriptor sets and preallocated descriptors
+MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo) :
+	MVKVulkanAPIDeviceObject(device),
+	_uniformBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER)),
+	_storageBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)),
+	_uniformBufferDynamicDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)),
+	_storageBufferDynamicDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC)),
+	_inlineUniformBlockDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT)),
+	_sampledImageDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE)),
+	_storageImageDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE)),
+	_inputAttachmentDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT)),
+	_samplerDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_SAMPLER)),
+	_combinedImageSamplerDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)),
+	_uniformTexelBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER)),
+	_storageTexelBufferDescriptors(getPoolSize(pCreateInfo, VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER)),
+	_maxSets(pCreateInfo->maxSets) {}
+
 MVKDescriptorPool::~MVKDescriptorPool() {
 	reset(0);
-	if (_preallocatedDescriptors) { _preallocatedDescriptors->destroy(); }
 }
 
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKBitArray.h b/MoltenVK/MoltenVK/Utility/MVKBitArray.h
new file mode 100755
index 0000000..b6e1ecb
--- /dev/null
+++ b/MoltenVK/MoltenVK/Utility/MVKBitArray.h
@@ -0,0 +1,181 @@
+/*

+ * MVKBitArray.h

+ *

+ * Copyright (c) 2020-2020 The Brenwill Workshop Ltd. (http://www.brenwill.com)

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ * 

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ * 

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+#pragma once

+

+#include "MVKFoundation.h"

+

+

+#pragma mark -

+#pragma mark MVKBitArray

+

+/** Represents an array of bits, optimized for reduced storage and fast scanning for bits that are set. */

+class MVKBitArray {

+

+	static constexpr size_t SectionMaskSize = 6;	// 64 bits

+	static constexpr size_t SectionBitCount = 1U << SectionMaskSize;

+	static constexpr size_t SectionByteCount = SectionBitCount / 8;

+	static constexpr uint64_t SectionMask = SectionBitCount - 1;

+

+public:

+

+	/** Returns the value of the bit. */

+	inline bool getBit(size_t bitIndex) {

+		return mvkIsAnyFlagEnabled(_pSections[getIndexOfSection(bitIndex)], getSectionSetMask(bitIndex));

+	}

+

+	/** Sets the value of the bit to 1. */

+	inline void setBit(size_t bitIndex) {

+		size_t secIdx = getIndexOfSection(bitIndex);

+		mvkEnableFlags(_pSections[secIdx], getSectionSetMask(bitIndex));

+

+		if (secIdx < _minUnclearedSectionIndex) { _minUnclearedSectionIndex = secIdx; }

+	}

+

+	/** Sets the value of the bit to 0. */

+	inline void clearBit(size_t bitIndex) {

+		size_t secIdx = getIndexOfSection(bitIndex);

+		mvkDisableFlags(_pSections[secIdx], getSectionSetMask(bitIndex));

+

+		if (secIdx == _minUnclearedSectionIndex && !_pSections[secIdx]) { _minUnclearedSectionIndex++; }

+	}

+

+	/** Sets the value of the bit to the value. */

+	inline void setBit(size_t bitIndex, bool val) {

+		if (val) {

+			setBit(bitIndex);

+		} else {

+			clearBit(bitIndex);

+		}

+	}

+

+	/** Sets all bits in the array to 1. */

+	inline void setAllBits() { setAllSections(~0); }

+

+	/** Clears all bits in the array to 0. */

+	inline void clearAllBits() { setAllSections(0); }

+

+	/**

+	 * Returns the index of the first bit that is set, at or after the specified index,

+	 * and optionally clears that bit. If no bits are set, returns the size() of this bit array.

+	 */

+	size_t getIndexOfFirstSetBit(size_t startIndex, bool shouldClear) {

+		size_t startSecIdx = std::max(getIndexOfSection(startIndex), _minUnclearedSectionIndex);

+		size_t bitIdx = startSecIdx << SectionMaskSize;

+		size_t secCnt = getSectionCount();

+		for (size_t secIdx = startSecIdx; secIdx < secCnt; secIdx++) {

+			size_t lclBitIdx = getIndexOfFirstSetBitInSection(_pSections[secIdx], getBitIndexInSection(startIndex));

+			bitIdx += lclBitIdx;

+			if (lclBitIdx < SectionBitCount) {

+				if (startSecIdx == _minUnclearedSectionIndex && !_pSections[startSecIdx]) { _minUnclearedSectionIndex = secIdx; }

+				if (shouldClear) { clearBit(bitIdx); }

+				return bitIdx;

+			}

+		}

+		return std::min(bitIdx, _bitCount);

+	}

+

+	/**

+	 * Returns the index of the first bit that is set, at or after the specified index.

+	 * If no bits are set, returns the size() of this bit array.

+	 */

+	inline size_t getIndexOfFirstSetBit(size_t startIndex) {

+		return getIndexOfFirstSetBit(startIndex, false);

+	}

+

+	/**

+	 * Returns the index of the first bit that is set and optionally clears that bit.

+	 * If no bits are set, returns the size() of this bit array.

+	 */

+	inline size_t getIndexOfFirstSetBit(bool shouldClear) {

+		return getIndexOfFirstSetBit(0, shouldClear);

+	}

+

+	/**

+	 * Returns the index of the first bit that is set.

+	 * If no bits are set, returns the size() of this bit array.

+	 */

+	inline size_t getIndexOfFirstSetBit() {

+		return getIndexOfFirstSetBit(0, false);

+	}

+

+	/** Returns the number of bits in this array. */

+	inline size_t size() { return _bitCount; }

+

+	/** Returns whether this array is empty. */

+	inline bool empty() { return !_bitCount; }

+

+	/** Constructs an instance for the specified number of bits, and sets the initial value of all the bits. */

+	MVKBitArray(size_t size, bool val = false) {

+		_bitCount = size;

+		_pSections = _bitCount ? (uint64_t*)malloc(getSectionCount() * SectionByteCount) : nullptr;

+		if (val) {

+			setAllBits();

+		} else {

+			clearAllBits();

+		}

+	}

+

+	~MVKBitArray() { free(_pSections); }

+

+protected:

+

+	// Returns the number of sections.

+	inline size_t getSectionCount() {

+		return _bitCount ? getIndexOfSection(_bitCount - 1) + 1 : 0;

+	}

+

+	// Returns the index of the section that contains the specified bit.

+	static inline size_t getIndexOfSection(size_t bitIndex) {

+		return bitIndex >> SectionMaskSize;

+	}

+

+	// Converts the bit index to a local bit index within a section, and returns that local bit index.

+	static inline size_t getBitIndexInSection(size_t bitIndex) {

+		return bitIndex & SectionMask;

+	}

+

+	// Returns a section mask containing a single 1 value in the bit in the section that

+	// corresponds to the specified global bit index, and 0 values in all other bits.

+	static inline uint64_t getSectionSetMask(size_t bitIndex) {

+		return (uint64_t)1U << ((SectionBitCount - 1) - getBitIndexInSection(bitIndex));

+	}

+

+	// Returns the local index of the first set bit in the section, starting from the highest order bit.

+	// Clears all bits ahead of the start bit so they will be ignored, then counts the number of zeros

+	// ahead of the set bit. If there are no set bits, returns the number of bits in a section.

+	static size_t getIndexOfFirstSetBitInSection(uint64_t section, size_t lclStartBitIndex) {

+		uint64_t lclStartMask = ~(uint64_t)0;

+		lclStartMask >>= lclStartBitIndex;

+		section &= lclStartMask;

+		return section ? __builtin_clzll(section) : SectionBitCount;

+	}

+

+	// Sets the content of all sections to the value

+	void setAllSections(uint64_t sectionValue) {

+		size_t secCnt = getSectionCount();

+		for (size_t secIdx = 0; secIdx < secCnt; secIdx++) {

+			_pSections[secIdx] = sectionValue;

+		}

+		_minUnclearedSectionIndex = sectionValue ? 0 : secCnt;

+	}

+

+	uint64_t* _pSections;

+	size_t _bitCount;

+	size_t _minUnclearedSectionIndex;	// Tracks where to start looking for bits that are set

+};