Merge branch 'master' of https://github.com/KhronosGroup/MoltenVK
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index d18ce65..2df61a3 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -12,6 +12,28 @@
 For best results, use a Markdown reader.*
 
 
+MoltenVK 1.0.23
+---------------
+
+Released 2018/09/28
+
+- Add support for features:
+	- shaderStorageImageMultisample
+	- shaderStorageImageReadWithoutFormat
+	- shaderStorageImageWriteWithoutFormat
+	- shaderUniformBufferArrayDynamicIndexing
+	- shaderSampledImageArrayDynamicIndexing
+	- shaderStorageBufferArrayDynamicIndexing
+	- shaderStorageImageArrayDynamicIndexing
+- Support reduced render area
+- Support rasterization to missing attachment
+- Allocate MVKCommandBuffers from a pool within MVKCommandPool.
+- Update glslang version
+- Update to latest SPIRV-Cross version:
+	- MSL: Improve coordinate handling for buffer reads.
+	- MSL: Expand arrays of buffers passed as input.
+
+
 MoltenVK 1.0.22
 ---------------
 
diff --git a/ExternalRevisions/glslang_repo_revision b/ExternalRevisions/glslang_repo_revision
index 3cf4e5d..26eb722 100644
--- a/ExternalRevisions/glslang_repo_revision
+++ b/ExternalRevisions/glslang_repo_revision
@@ -1 +1 @@
-a8453d4bc00998049db0d448764784a6a0767539
+91ac4290bcf2cb930b4fb0981f09c00c0b6797e1
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommand.h b/MoltenVK/MoltenVK/Commands/MVKCommand.h
index b3dd8f3..00ba38a 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommand.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommand.h
@@ -77,7 +77,7 @@
 
 	/**
 	 * Instances of this class can participate in a linked list or pool. When so participating,
-	 * this is a reference to the next command in the linked list. This value should only be
+	 * this is a reference to the next instance in the linked list. This value should only be
 	 * managed and set by the linked list.
 	 */
 	MVKCommand* _next = nullptr;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
index 9c06290..c28a398 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.h
@@ -21,6 +21,7 @@
 #include "MVKDevice.h"
 #include "MVKCommand.h"
 #include "MVKCommandEncoderState.h"
+#include "MVKMTLBufferAllocation.h"
 #include "MVKCmdPipeline.h"
 #include <vector>
 #include <unordered_map>
@@ -90,15 +91,15 @@
 
 	/**
 	 * Instances of this class can participate in a linked list or pool. When so participating,
-	 * this is a reference to the next command in the linked list. This value should only be
+	 * this is a reference to the next instance in the linked list. This value should only be
 	 * managed and set by the linked list.
 	 */
 	MVKCommandBuffer* _next;
 
 
 #pragma mark Construction
-	
-	MVKCommandBuffer(MVKDevice* device, const VkCommandBufferAllocateInfo* pAllocateInfo);
+
+	MVKCommandBuffer(MVKDevice* device) : MVKDispatchableDeviceObject(device) {}
 
 	~MVKCommandBuffer() override;
 
@@ -118,7 +119,9 @@
 
 protected:
 	friend class MVKCommandEncoder;
+	friend class MVKCommandPool;
 
+	void init(const VkCommandBufferAllocateInfo* pAllocateInfo);
 	bool canExecute();
 	bool canPrefill();
 	void prefill();
@@ -141,6 +144,33 @@
 
 
 #pragma mark -
+#pragma mark MVKCommandBufferPool
+
+/**
+ * A pool of MVKCommandBuffer instances.
+ *
+ * To return a MVKCommandBuffer retrieved from this pool, back to this pool,
+ * call the returnToPool() function on the MVKCommandBuffer instance.
+ */
+class MVKCommandBufferPool : public MVKObjectPool<MVKCommandBuffer> {
+
+public:
+
+	/** Returns a new command instance. */
+	MVKCommandBuffer* newObject() override { return new MVKCommandBuffer(_device); }
+
+	/**
+	 * Configures this instance to either use pooling, or not, depending on the
+	 * value of isPooling, which defaults to true if not indicated explicitly.
+	 */
+	MVKCommandBufferPool(MVKDevice* device, bool isPooling = true) : MVKObjectPool<MVKCommandBuffer>(isPooling), _device(device) {}
+
+protected:
+	MVKDevice* _device;
+};
+
+
+#pragma mark -
 #pragma mark MVKCommandEncoder
 
 // The following commands can be issued both inside and outside a renderpass and their state must
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
index 6812ceb..7c2f232 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandBuffer.mm
@@ -175,11 +175,9 @@
 
 #pragma mark Construction
 
-MVKCommandBuffer::MVKCommandBuffer(MVKDevice* device,
-								   const VkCommandBufferAllocateInfo* pAllocateInfo) : MVKDispatchableDeviceObject(device) {
-
+// Initializes this instance after it has been created retrieved from a pool.
+void MVKCommandBuffer::init(const VkCommandBufferAllocateInfo* pAllocateInfo) {
 	_commandPool = (MVKCommandPool*)pAllocateInfo->commandPool;
-	_commandPool->addCommandBuffer(this);
 	_isSecondary = (pAllocateInfo->level == VK_COMMAND_BUFFER_LEVEL_SECONDARY);
 	_head = nullptr;
 	_tail = nullptr;
@@ -190,7 +188,6 @@
 
 MVKCommandBuffer::~MVKCommandBuffer() {
 	reset(0);
-	_commandPool->removeCommandBuffer(this);
 }
 
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
index ce9779d..8e54826 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.h
@@ -18,7 +18,8 @@
 
 #pragma once
 
-#include "MVKCommandPool.h"
+#include  "MVKMTLResourceBindings.h"
+#include "MVKCommandResourceFactory.h"
 #include <vector>
 
 class MVKCommandEncoder;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h
index 37c2567..1373cb0 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.h
@@ -97,6 +97,9 @@
 	/** Returns a MTLComputePipelineState for filling a buffer. */
 	id<MTLComputePipelineState> getCmdFillBufferMTLComputePipelineState();
 
+	/** Deletes all the internal resources. */
+	void clear();
+
 #pragma mark Construction
 
 	MVKCommandEncodingPool(MVKDevice* device);
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm
index 74e82d6..b1d9462 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncodingPool.mm
@@ -105,6 +105,12 @@
 	MVK_ENC_REZ_ACCESS(_mtlFillBufferComputePipelineState, newCmdFillBufferMTLComputePipelineState());
 }
 
+void MVKCommandEncodingPool::clear() {
+	lock_guard<mutex> lock(_lock);
+	destroyMetalResources();
+}
+
+
 #pragma mark Construction
 
 MVKCommandEncodingPool::MVKCommandEncodingPool(MVKDevice* device) : MVKBaseDeviceObject(device),
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
index e8278b4..5ded1d5 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.h
@@ -19,6 +19,7 @@
 #pragma once
 
 #include "MVKDevice.h"
+#include "MVKCommandBuffer.h"
 #include "MVKCommandResourceFactory.h"
 #include "MVKCommandEncodingPool.h"
 #include "MVKCommand.h"
@@ -169,12 +170,10 @@
 
 	~MVKCommandPool() override;
 
-private:
-	friend class MVKCommandBuffer;
+protected:
+	void freeCommandBuffer(MVKCommandBuffer* mvkCmdBuff);
 
-	void addCommandBuffer(MVKCommandBuffer* cmdBuffer);
-	void removeCommandBuffer(MVKCommandBuffer* cmdBuffer);
-
+	MVKCommandBufferPool _commandBufferPool;
 	std::unordered_set<MVKCommandBuffer*> _commandBuffers;
 	MVKCommandEncodingPool _commandEncodingPool;
 	uint32_t _queueFamilyIndex;
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
index d33b9e5..559b348 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandPool.mm
@@ -33,9 +33,14 @@
 
 // Reset all of the command buffers
 VkResult MVKCommandPool::reset(VkCommandPoolResetFlags flags) {
-    for (auto& cb : _commandBuffers) {
-		cb->reset(VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
-	}
+	bool releaseRez = mvkAreFlagsEnabled(flags, VK_COMMAND_POOL_RESET_RELEASE_RESOURCES_BIT);
+
+	VkCommandBufferResetFlags cmdBuffFlags = releaseRez ? VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT : 0;
+
+	for (auto& cb : _commandBuffers) { cb->reset(cmdBuffFlags); }
+
+	if (releaseRez) { trim(); }
+
 	return VK_SUCCESS;
 }
 
@@ -47,7 +52,9 @@
 	VkResult rslt = VK_SUCCESS;
 	uint32_t cbCnt = pAllocateInfo->commandBufferCount;
 	for (uint32_t cbIdx = 0; cbIdx < cbCnt; cbIdx++) {
-		MVKCommandBuffer* mvkCmdBuff = new MVKCommandBuffer(_device, pAllocateInfo);
+		MVKCommandBuffer* mvkCmdBuff = _commandBufferPool.acquireObject();
+		mvkCmdBuff->init(pAllocateInfo);
+		_commandBuffers.insert(mvkCmdBuff);
         pCmdBuffer[cbIdx] = mvkCmdBuff->getVkCommandBuffer();
 		if (rslt == VK_SUCCESS) { rslt = mvkCmdBuff->getConfigurationResult(); }
 	}
@@ -57,25 +64,66 @@
 void MVKCommandPool::freeCommandBuffers(uint32_t commandBufferCount,
 										const VkCommandBuffer* pCommandBuffers) {
 	for (uint32_t cbIdx = 0; cbIdx < commandBufferCount; cbIdx++) {
-		VkCommandBuffer cmdBuff = pCommandBuffers[cbIdx];
-		if (cmdBuff) { MVKCommandBuffer::getMVKCommandBuffer(cmdBuff)->destroy(); }
+		freeCommandBuffer(MVKCommandBuffer::getMVKCommandBuffer(pCommandBuffers[cbIdx]));
 	}
 }
 
+void MVKCommandPool::freeCommandBuffer(MVKCommandBuffer* mvkCmdBuff) {
+	if ( !mvkCmdBuff ) { return; }
+
+	mvkCmdBuff->reset(VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT);
+	_commandBuffers.erase(mvkCmdBuff);
+	_commandBufferPool.returnObject(mvkCmdBuff);
+}
+
 id<MTLCommandBuffer> MVKCommandPool::newMTLCommandBuffer(uint32_t queueIndex) {
 	return [[_device->getQueue(_queueFamilyIndex, queueIndex)->getMTLCommandQueue() commandBuffer] retain];
 }
 
 void MVKCommandPool::trim() {
-	// TODO: Implement.
-}
-
-void MVKCommandPool::addCommandBuffer(MVKCommandBuffer* cmdBuffer) {
-	_commandBuffers.insert(cmdBuffer);
-}
-
-void MVKCommandPool::removeCommandBuffer(MVKCommandBuffer* cmdBuffer) {
-	_commandBuffers.erase(cmdBuffer);
+	_commandBufferPool.clear();
+	_commandEncodingPool.clear();
+	_cmdPipelineBarrierPool.clear();
+	_cmdBindPipelinePool.clear();
+	_cmdBeginRenderPassPool.clear();
+	_cmdNextSubpassPool.clear();
+	_cmdExecuteCommandsPool.clear();
+	_cmdEndRenderPassPool.clear();
+	_cmdBindDescriptorSetsPool.clear();
+	_cmdSetViewportPool.clear();
+	_cmdSetScissorPool.clear();
+	_cmdSetLineWidthPool.clear();
+	_cmdSetDepthBiasPool.clear();
+	_cmdSetBlendConstantsPool.clear();
+	_cmdSetDepthBoundsPool.clear();
+	_cmdSetStencilCompareMaskPool.clear();
+	_cmdSetStencilWriteMaskPool.clear();
+	_cmdSetStencilReferencePool.clear();
+	_cmdBindVertexBuffersPool.clear();
+	_cmdBindIndexBufferPool.clear();
+	_cmdDrawPool.clear();
+	_cmdDrawIndexedPool.clear();
+	_cmdDrawIndirectPool.clear();
+	_cmdDrawIndexedIndirectPool.clear();
+	_cmdCopyImagePool.clear();
+	_cmdBlitImagePool.clear();
+	_cmdResolveImagePool.clear();
+	_cmdFillBufferPool.clear();
+	_cmdUpdateBufferPool.clear();
+	_cmdCopyBufferPool.clear();
+	_cmdBufferImageCopyPool.clear();
+	_cmdClearAttachmentsPool.clear();
+	_cmdClearImagePool.clear();
+	_cmdBeginQueryPool.clear();
+	_cmdEndQueryPool.clear();
+	_cmdWriteTimestampPool.clear();
+	_cmdResetQueryPoolPool.clear();
+	_cmdCopyQueryPoolResultsPool.clear();
+	_cmdPushConstantsPool.clear();
+	_cmdDispatchPool.clear();
+	_cmdDispatchIndirectPool.clear();
+	_cmdPushDescriptorSetPool.clear();
+	_cmdPushSetWithTemplatePool.clear();
 }
 
 
@@ -83,6 +131,7 @@
 
 MVKCommandPool::MVKCommandPool(MVKDevice* device,
 							   const VkCommandPoolCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device),
+	_commandBufferPool(device),
 	_commandEncodingPool(device),
 	_queueFamilyIndex(pCreateInfo->queueFamilyIndex),
 	_cmdPipelineBarrierPool(this, true),
@@ -128,7 +177,8 @@
     _cmdPushSetWithTemplatePool(this, true)
 {}
 
-// TODO: Destroying a command pool implicitly destroys all command buffers and commands created from it.
-
-MVKCommandPool::~MVKCommandPool() {}
+MVKCommandPool::~MVKCommandPool() {
+	auto cmdBuffs = _commandBuffers;
+	for (auto& cb : cmdBuffs) { freeCommandBuffer(cb); }
+}
 
diff --git a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
index 7bcf257..d315863 100644
--- a/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
+++ b/MoltenVK/MoltenVK/Commands/MVKMTLBufferAllocation.h
@@ -55,7 +55,7 @@
 
     /**
 	 * Instances of this class can participate in a linked list or pool. When so participating,
-	 * this is a reference to the next command in the linked list. This value should only be
+	 * this is a reference to the next instance in the linked list. This value should only be
 	 * managed and set by the linked list.
 	 */
 	MVKMTLBufferAllocation* _next = nullptr;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 4c9e41c..3b7efb1 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -313,8 +313,8 @@
 
 protected:
     uint32_t _maxSets;
+	uint32_t _allocatedSetCount;
 	std::forward_list<MVKDescriptorSet*> _allocatedSets;
-	size_t _allocatedSetCount;
 };
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index 6377ce4..346dfaa 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -868,6 +868,7 @@
 VkResult MVKDescriptorPool::allocateDescriptorSets(uint32_t count,
 												   const VkDescriptorSetLayout* pSetLayouts,
 												   VkDescriptorSet* pDescriptorSets) {
+//	MVKLogDebug("Descriptor pool %p allocating %d descriptor sets for total %d.", this, count, _allocatedSetCount + count);
 	if (_allocatedSetCount + count > _maxSets) {
 		if (_device->_enabledExtensions.vk_KHR_maintenance1.enabled) {
 			return VK_ERROR_OUT_OF_POOL_MEMORY;		// Failure is an acceptable test...don't log as error.
@@ -888,6 +889,7 @@
 }
 
 VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets) {
+//	MVKLogDebug("Descriptor pool %p freeing %d descriptor sets from total %d.", this, count, _allocatedSetCount);
 	for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) {
 		MVKDescriptorSet* mvkDS = (MVKDescriptorSet*)pDescriptorSets[dsIdx];
 		if (mvkDS) {
@@ -900,6 +902,7 @@
 }
 
 VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) {
+//	MVKLogDebug("Descriptor pool %p resetting with %d descriptor sets.", this,  _allocatedSetCount);
 	mvkDestroyContainerContents(_allocatedSets);
 	_allocatedSetCount = 0;
 	return VK_SUCCESS;
@@ -909,12 +912,13 @@
 									 const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKBaseDeviceObject(device) {
 	_maxSets = pCreateInfo->maxSets;
 	_allocatedSetCount = 0;
+//	MVKLogDebug("Descriptor pool %p created with max %d sets.", this, _maxSets);
 }
 
 // TODO: Destroying a descriptor pool implicitly destroys all descriptor sets created from it.
 
 MVKDescriptorPool::~MVKDescriptorPool() {
-//	MVKLogDebug("Pool %p destroyed with %d descriptor sets.", this, _allocatedSetCount);
+//	MVKLogDebug("Descriptor pool %p destroyed with %d descriptor sets.", this, _allocatedSetCount);
 	reset(0);
 }
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
index f15aeac..6827bf1 100644
--- a/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
+++ b/MoltenVK/MoltenVK/Utility/MVKBaseObject.h
@@ -95,7 +95,7 @@
      * This is the compliment of the getVkHandle() method.
      */
     static inline MVKDispatchableObject* getDispatchableObject(void* vkHandle) {
-        return ((MVKDispatchableObjectICDRef*)vkHandle)->mvkObject;
+		return vkHandle ? ((MVKDispatchableObjectICDRef*)vkHandle)->mvkObject : nullptr;
     }
 
 protected:
diff --git a/MoltenVK/MoltenVK/Utility/MVKObjectPool.h b/MoltenVK/MoltenVK/Utility/MVKObjectPool.h
index 4e243e4..ba0d09e 100644
--- a/MoltenVK/MoltenVK/Utility/MVKObjectPool.h
+++ b/MoltenVK/MoltenVK/Utility/MVKObjectPool.h
@@ -76,6 +76,8 @@
      * aquireObject() and returnObject() must be made from the same thread.
 	 */
 	void returnObject(T* obj) {
+		if ( !obj ) { return; }
+
 		if (_isPooling) {
 			if (_tail) { _tail->_next = obj; }
 			obj->_next = VK_NULL_HANDLE;