Merge branch 'master' of https://github.com/KhronosGroup/MoltenVK
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index d763dc3..3aabc51 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -19,7 +19,17 @@
 Released TBD
 
 - Add support for extensions:
+	- `VK_KHR_swapchain_mutable_format`
+	- `VK_KHR_uniform_buffer_standard_layout`
 	- `VK_EXT_metal_surface`
+	- `VK_EXT_post_depth_coverage`
+	- `VK_EXT_scalar_block_layout`
+	- `VK_EXT_shader_stencil_export`
+	- `VK_EXT_swapchain_colorspace`
+	- `VK_EXT_texel_buffer_alignment`
+	- `VK_AMD_shader_image_load_store_lod`
+	- `VK_AMD_shader_trinary_minmax`
+	- `VK_INTEL_shader_integer_functions2`
 - For shaders created directly from MSL, set function name from 
   `VkPipelineShaderStageCreateInfo::pName`.
 - On iOS GPU family 2 and earlier, support immutable depth-compare samplers 
@@ -32,6 +42,9 @@
 - Fix race condition between swapchain image destruction and presentation completion callback.
 - Set Metal texture usage to allow texture copy via view.
 - Fix memory leak in debug marker and debug utils labelling.
+- Fix issue with push constants used across multiple draw calls not being applied.
+- Fix crash when binding descriptor set to layout that has been destroyed and recreated.
+- Return error when `MVKImage` created as 1D attachment.
 - Reduce use of autoreleased Obj-C objects, and ensure those remaining are 
   covered by deliberate autorelease pools. 
 - `vkCmdCopyImage()` support copying between compressed and uncompressed formats
@@ -75,7 +88,7 @@
 	- Fix tessellated indirect draws using wrong kernels to map parameters.
 	- Work around potential Metal bug with stage-in indirect buffers.
 	- Fix zero local threadgroup size in indirect tessellated rendering.
-	- Fix [[attribute]] assignment for tessellation evaluation shaders.
+	- Fix `[[attribute]]` assignment for tessellation evaluation shaders.
 - `VkSemaphore` optionally uses `MTLEvent`, if available and 
   `MVK_ALLOW_METAL_EVENTS` environment variable is enabled.
 - Add `vkSetWorkgroupSizeMVK()` to set compute kernel workgroup size 
@@ -100,7 +113,7 @@
 - Fix unused attachments terminating loop early.
 - Fix offset of buffer view relative to buffer offset within device memory.
 - Guard against missing Metal pipeline states when pipeline compilation fails.
-- MVKBuffer: Force managed storage for linear textures on shared buffers.
+- `MVKBuffer`: Force managed storage for linear textures on shared buffers.
 - Use device address space when decompressing DXT image data.
 - Added missing `texelBufferTextureWidth` setting in `MVKComputePipeline::getMTLFunction()`.
 - Fixes and consolidation of external library header references.
@@ -128,7 +141,7 @@
 	- MSL: Support Invariant qualifier on position.
 	- MSL: Support stencil export.
 	- Deal with case where a block is somehow emitted in a duplicated fashion.
-	- Fix infinite loop when OpAtomic* temporaries are used in other blocks.
+	- Fix infinite loop when `OpAtomic*` temporaries are used in other blocks.
 	- Fix tests for device->constant address space change in MSL tessellation control shader generation.
 	- Accept SPIR-V 1.4 version.
 
@@ -272,7 +285,7 @@
 - Allow default GPU Capture scope to be assigned to any queue in any queue family.
 - VkPhysicalDevice: Correct some features and limits.
 - Stop advertising atomic image support.
-- vkSetMTLTextureMVK() function retains texture object.
+- `vkSetMTLTextureMVK()` function retains texture object.
 - Log to stderr instead of stdout.
 - `fetchDependencies`: build `spirv-tools` when attached via symlink.
 - Enhancements to `MVKVector`, and set appropriate inline sizing usages.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index b8e106b..18fe127 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -197,7 +197,7 @@
 	_offset = offset;
 
 	_pushConstants.resize(size);
-  std::copy_n((char*)pValues, size, _pushConstants.begin());
+	std::copy_n((char*)pValues, size, _pushConstants.begin());
 }
 
 void MVKCmdPushConstants::encode(MVKCommandEncoder* cmdEncoder) {
diff --git a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
index 35d6a02..641832e 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCommandEncoderState.mm
@@ -170,9 +170,14 @@
     }
 }
 
+// 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.
 void MVKPushConstantsCommandEncoderState::encodeImpl(uint32_t stage) {
     if (_pushConstants.empty() ) { return; }
 
+	_isDirty = true;	// Stay dirty until I actually decide to make a change to the encoder
+
     switch (_shaderStage) {
         case VK_SHADER_STAGE_VERTEX_BIT:
             if (stage == (isTessellating() ? kMVKGraphicsStageVertex : kMVKGraphicsStageRasterization)) {
@@ -180,6 +185,7 @@
                                             _pushConstants.data(),
                                             _pushConstants.size(),
                                             _mtlBufferIndex);
+				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
         case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
@@ -188,6 +194,7 @@
                                              _pushConstants.data(),
                                              _pushConstants.size(),
                                              _mtlBufferIndex);
+				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
         case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
@@ -196,6 +203,7 @@
                                             _pushConstants.data(),
                                             _pushConstants.size(),
                                             _mtlBufferIndex);
+				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
         case VK_SHADER_STAGE_FRAGMENT_BIT:
@@ -204,6 +212,7 @@
                                               _pushConstants.data(),
                                               _pushConstants.size(),
                                               _mtlBufferIndex);
+				_isDirty = false;	// Okay, I changed the encoder
             }
             break;
         case VK_SHADER_STAGE_COMPUTE_BIT:
@@ -211,6 +220,7 @@
                                          _pushConstants.data(),
                                          _pushConstants.size(),
                                          _mtlBufferIndex);
+			_isDirty = false;	// Okay, I changed the encoder
             break;
         default:
             MVKAssert(false, "Unsupported shader stage: %d", _shaderStage);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
index 90cf292..6bd5ae8 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.h
@@ -167,20 +167,25 @@
 	/** Returns true if this layout is for push descriptors only. */
 	bool isPushDescriptorLayout() const { return _isPushDescriptorLayout; }
 
-	/** Constructs an instance for the specified device. */
 	MVKDescriptorSetLayout(MVKDevice* device, const VkDescriptorSetLayoutCreateInfo* pCreateInfo);
 
+	~MVKDescriptorSetLayout();
+
 protected:
 
 	friend class MVKDescriptorSetLayoutBinding;
 	friend class MVKPipelineLayout;
 	friend class MVKDescriptorSet;
+	friend class MVKDescriptorPool;
 
 	void propogateDebugName() override {}
-	
+	void addDescriptorPool(MVKDescriptorPool* mvkDescPool) { _descriptorPools.insert(mvkDescPool); }
+	void removeDescriptorPool(MVKDescriptorPool* mvkDescPool) { _descriptorPools.erase(mvkDescPool); }
+
 	MVKVectorInline<MVKDescriptorSetLayoutBinding, 8> _bindings;
 	std::unordered_map<uint32_t, uint32_t> _bindingToIndex;
 	MVKShaderResourceBinding _mtlResourceCounts;
+	std::unordered_set<MVKDescriptorPool*> _descriptorPools;
 	bool _isPushDescriptorLayout : 1;
 };
 
@@ -357,6 +362,9 @@
 	/** Destoys all currently allocated descriptor sets. */
 	VkResult reset(VkDescriptorPoolResetFlags flags);
 
+	/** Removes the pool associated with a descriptor set layout. */
+	void removeDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout);
+
 	/** Constructs an instance for the specified device. */
 	MVKDescriptorPool(MVKDevice* device, const VkDescriptorPoolCreateInfo* pCreateInfo);
 
@@ -366,6 +374,7 @@
 protected:
 	void propogateDebugName() override {}
 	MVKDescriptorSetPool* getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout);
+	void returnDescriptorSet(MVKDescriptorSet* mvkDescSet);
 
 	uint32_t _maxSets;
 	std::unordered_set<MVKDescriptorSet*> _allocatedSets;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index 1396bac..1f97ac0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -652,6 +652,10 @@
     }
 }
 
+MVKDescriptorSetLayout::~MVKDescriptorSetLayout() {
+	for (auto& dsPool : _descriptorPools) { dsPool->removeDescriptorSetPool(this); }
+}
+
 
 #pragma mark -
 #pragma mark MVKDescriptorBinding
@@ -1021,41 +1025,66 @@
 VkResult MVKDescriptorPool::freeDescriptorSets(uint32_t count, const VkDescriptorSet* pDescriptorSets) {
 	for (uint32_t dsIdx = 0; dsIdx < count; dsIdx++) {
 		MVKDescriptorSet* mvkDS = (MVKDescriptorSet*)pDescriptorSets[dsIdx];
-		if (_allocatedSets.erase(mvkDS)) {
-			getDescriptorSetPool(mvkDS->_pLayout)->returnObject(mvkDS);
-		}
+		if (_allocatedSets.erase(mvkDS)) { returnDescriptorSet(mvkDS); }
 	}
 	return VK_SUCCESS;
 }
 
 // Return any allocated descriptor sets to their pools
 VkResult MVKDescriptorPool::reset(VkDescriptorPoolResetFlags flags) {
-	for (auto& mvkDS : _allocatedSets) {
-		getDescriptorSetPool(mvkDS->_pLayout)->returnObject(mvkDS);
-	}
+	for (auto& mvkDS : _allocatedSets) { returnDescriptorSet(mvkDS); }
 	_allocatedSets.clear();
 	return VK_SUCCESS;
 }
 
+// Returns the descriptor set to its pool, or if that pool doesn't exist, the descriptor set is destroyed
+void MVKDescriptorPool::returnDescriptorSet(MVKDescriptorSet* mvkDescSet) {
+	MVKDescriptorSetLayout* dsLayout = mvkDescSet->_pLayout;
+	MVKDescriptorSetPool* dsPool = dsLayout ? _descriptorSetPools[dsLayout] : nullptr;
+	if (dsPool) {
+		dsPool->returnObject(mvkDescSet);
+	} else {
+		mvkDescSet->destroy();
+		_descriptorSetPools.erase(dsLayout);
+	}
+}
+
 // Returns the pool of descriptor sets that use a specific layout, lazily creating it if necessary
 MVKDescriptorSetPool* MVKDescriptorPool::getDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout) {
 	MVKDescriptorSetPool* dsp = _descriptorSetPools[mvkDescSetLayout];
 	if ( !dsp ) {
 		dsp = new MVKDescriptorSetPool(_device);
 		_descriptorSetPools[mvkDescSetLayout] = dsp;
+		mvkDescSetLayout->addDescriptorPool(this);		// tell layout to track me
 	}
 	return dsp;
 }
 
+// Remove the descriptor set pool associated with the descriptor set layout,
+// and make sure any allocated sets don't try to return back to their pools.
+void MVKDescriptorPool::removeDescriptorSetPool(MVKDescriptorSetLayout* mvkDescSetLayout) {
+	MVKDescriptorSetPool* dsp = _descriptorSetPools[mvkDescSetLayout];
+	if (dsp) { dsp->destroy(); }
+	_descriptorSetPools.erase(mvkDescSetLayout);
+
+	for (auto& mvkDS : _allocatedSets) {
+		if (mvkDS->_pLayout == mvkDescSetLayout) { mvkDS->_pLayout = nullptr; }
+	}
+}
+
 MVKDescriptorPool::MVKDescriptorPool(MVKDevice* device,
 									 const VkDescriptorPoolCreateInfo* pCreateInfo) : MVKVulkanAPIDeviceObject(device) {
 	_maxSets = pCreateInfo->maxSets;
 }
 
-// Return any allocated sets to their pools and then destroy all the pools.
+// Return any allocated sets to their pools and then destroy all the pools,
+// and ensure any descriptor set layouts used as keys are notified.
 MVKDescriptorPool::~MVKDescriptorPool() {
 	reset(0);
-	for (auto& pair : _descriptorSetPools) { pair.second->destroy(); }
+	for (auto& pair : _descriptorSetPools) {
+		pair.first->removeDescriptorPool(this);
+		if (pair.second) { pair.second->destroy(); }
+	}
 }
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 2c03080..0cbf0d0 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -237,10 +237,10 @@
 
 	void propogateDebugName() override;
 	MVKImageSubresource* getSubresource(uint32_t mipLevel, uint32_t arrayLayer);
-	void validateConfig(const VkImageCreateInfo* pCreateInfo);
-	VkSampleCountFlagBits validateSamples(const VkImageCreateInfo* pCreateInfo);
-	uint32_t validateMipLevels(const VkImageCreateInfo* pCreateInfo);
-	bool validateLinear(const VkImageCreateInfo* pCreateInfo);
+	void validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttachment);
+	VkSampleCountFlagBits validateSamples(const VkImageCreateInfo* pCreateInfo, bool isAttachment);
+	uint32_t validateMipLevels(const VkImageCreateInfo* pCreateInfo, bool isAttachment);
+	bool validateLinear(const VkImageCreateInfo* pCreateInfo, bool isAttachment);
 	bool validateUseTexelBuffer();
 	void initSubresources(const VkImageCreateInfo* pCreateInfo);
 	void initSubresourceLayout(MVKImageSubresource& imgSubRez);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 9204962..be2240a 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -607,10 +607,14 @@
     _arrayLayers = max(pCreateInfo->arrayLayers, minDim);
 
 	// Perform validation and adjustments before configuring other settings
-	validateConfig(pCreateInfo);
-	_samples = validateSamples(pCreateInfo);
-	_mipLevels = validateMipLevels(pCreateInfo);
-	_isLinear = validateLinear(pCreateInfo);
+	bool isAttachment = mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+																 VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT |
+																 VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT |
+																 VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT));
+	validateConfig(pCreateInfo, isAttachment);
+	_samples = validateSamples(pCreateInfo, isAttachment);
+	_mipLevels = validateMipLevels(pCreateInfo, isAttachment);
+	_isLinear = validateLinear(pCreateInfo, isAttachment);
 
 	_mtlPixelFormat = getMTLPixelFormatFromVkFormat(pCreateInfo->format);
 	_mtlTextureType = mvkMTLTextureTypeFromVkImageType(pCreateInfo->imageType, _arrayLayers, _samples > VK_SAMPLE_COUNT_1_BIT);
@@ -631,7 +635,7 @@
     initSubresources(pCreateInfo);
 }
 
-void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo) {
+void MVKImage::validateConfig(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
 
 	bool is2D = pCreateInfo->imageType == VK_IMAGE_TYPE_2D;
 	bool isCompressed = mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatCompressed;
@@ -650,18 +654,18 @@
 	if ((mvkFormatTypeFromVkFormat(pCreateInfo->format) == kMVKFormatDepthStencil) && !is2D ) {
 		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Under Metal, depth/stencil formats may only be used with 2D images."));
 	}
-
-	bool isAttachment = mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT));
 	if (isAttachment && (pCreateInfo->arrayLayers > 1) && !_device->_pMetalFeatures->layeredRendering) {
 		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support rendering to array (layered) attachments."));
 	}
-
+	if (isAttachment && (pCreateInfo->imageType == VK_IMAGE_TYPE_1D)) {
+		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support rendering to 1D attachments."));
+	}
 	if (mvkIsAnyFlagEnabled(pCreateInfo->flags, VK_IMAGE_CREATE_BLOCK_TEXEL_VIEW_COMPATIBLE_BIT)) {
 		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : Metal does not allow uncompressed views of compressed images."));
 	}
 }
 
-VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreateInfo) {
+VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
 
 	VkSampleCountFlagBits validSamples = pCreateInfo->samples;
 
@@ -682,8 +686,6 @@
 			setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support multisampled array textures. Setting sample count to 1."));
 			validSamples = VK_SAMPLE_COUNT_1_BIT;
 		}
-
-		bool isAttachment = mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT));
 		if (isAttachment && !_device->_pMetalFeatures->multisampleLayeredRendering) {
 			setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support rendering to multisampled array (layered) attachments. Setting sample count to 1."));
 			validSamples = VK_SAMPLE_COUNT_1_BIT;
@@ -693,7 +695,7 @@
 	return validSamples;
 }
 
-uint32_t MVKImage::validateMipLevels(const VkImageCreateInfo* pCreateInfo) {
+uint32_t MVKImage::validateMipLevels(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
 	uint32_t minDim = 1;
 	uint32_t validMipLevels = max(pCreateInfo->mipLevels, minDim);
 
@@ -707,7 +709,7 @@
 	return validMipLevels;
 }
 
-bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo) {
+bool MVKImage::validateLinear(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
 
 	if (pCreateInfo->tiling != VK_IMAGE_TILING_LINEAR ) { return false; }
 
@@ -739,8 +741,8 @@
 	}
 
 #if MVK_MACOS
-	if ( mvkIsAnyFlagEnabled(pCreateInfo->usage, (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT)) ) {
-		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : If tiling is VK_IMAGE_TILING_LINEAR, usage must not include VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, or VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT."));
+	if (isAttachment) {
+		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage() : This device does not support rendering to linear (VK_IMAGE_TILING_LINEAR) images."));
 		isLin = false;
 	}
 #endif
@@ -929,10 +931,10 @@
 		if (pCreateInfo->subresourceRange.layerCount != image->_extent.depth) {
 			reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): Metal does not fully support views on a subset of a 3D texture.");
 		}
-		if (!mvkAreAllFlagsEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
-			setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images are only supported for color attachments."));
-		} else if (mvkIsAnyFlagEnabled(_usage, ~VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
-			reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images are only supported for color attachments.");
+		if ( !mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT) ) {
+			setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments."));
+		} else if (mvkIsOnlyAnyFlagEnabled(_usage, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)) {
+			reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImageView(): 2D views on 3D images can only be used as color attachments.");
 		}
 	}
 }