Merge pull request #872 from billhollings/master

Add support for VK_KHR_external_memory extension.
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index ca421d0..2b7845c 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -19,6 +19,12 @@
 
 Released TBD
 
+- Add support for extensions:
+	- `VK_KHR_external_memory` (non-functional groundwork for future 
+	  Metal-resource Vulkan extension).
+	- `VK_KHR_external_memory_capabilities` (non-functional groundwork 
+	   for future Metal-resource Vulkan extension).
+- Memory consumption improvements when handling Vulkan commands.
 - Reinstate `VulkanSamples API-Samples` demo apps and add 
   `input_attachment` and `push_descriptors` demos.
 - `vkQueuePresentKHR()` returns a `VkResult` for each swapchain.
diff --git a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
index 01b8daf..af83890 100644
--- a/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
+++ b/MoltenVK/MoltenVK/Commands/MVKCmdPipeline.mm
@@ -261,18 +261,17 @@
 		}
         if (mvkDvc->_enabledExtensions.vk_EXT_inline_uniform_block.enabled) {
             const VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock = nullptr;
-            for (auto* next = (VkWriteDescriptorSetInlineUniformBlockEXT*)descWrite.pNext; next; next = (VkWriteDescriptorSetInlineUniformBlockEXT*)next->pNext)
-            {
+			for (const auto* next = (VkBaseInStructure*)descWrite.pNext; next; next = next->pNext) {
                 switch (next->sType) {
                 case VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT: {
-                    pInlineUniformBlock = next;
+                    pInlineUniformBlock = (VkWriteDescriptorSetInlineUniformBlockEXT*)next;
                     break;
                 }
                 default:
                     break;
                 }
             }
-            if (pInlineUniformBlock != nullptr) {
+            if (pInlineUniformBlock) {
                 auto *pNewInlineUniformBlock = new VkWriteDescriptorSetInlineUniformBlockEXT(*pInlineUniformBlock);
                 pNewInlineUniformBlock->pNext = nullptr; // clear pNext just in case, no other extensions are supported at this time
                 descWrite.pNext = pNewInlineUniformBlock;
@@ -300,11 +299,10 @@
 		if (descWrite.pTexelBufferView) { delete[] descWrite.pTexelBufferView; }
 
 		const VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock = nullptr;
-		for (auto* next = (VkWriteDescriptorSetInlineUniformBlockEXT*)descWrite.pNext; next; next = (VkWriteDescriptorSetInlineUniformBlockEXT*)next->pNext)
-		{
+		for (const auto* next = (VkBaseInStructure*)descWrite.pNext; next; next = next->pNext) {
 			switch (next->sType) {
 				case VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT: {
-					pInlineUniformBlock = next;
+					pInlineUniformBlock = (VkWriteDescriptorSetInlineUniformBlockEXT*)next;
 					break;
 				}
 				default:
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
index d3cbb05..3f88415 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.h
@@ -48,6 +48,9 @@
 	/** Binds this resource to the specified offset within the specified memory allocation. */
 	VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override;
 
+	/** Binds this resource to the specified offset within the specified memory allocation. */
+	VkResult bindDeviceMemory2(const VkBindBufferMemoryInfo* pBindInfo);
+
 	/** Applies the specified global memory barrier. */
     void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
                             VkPipelineStageFlags dstStageMask,
@@ -92,6 +95,7 @@
 	bool shouldFlushHostMemory();
 	VkResult flushToDevice(VkDeviceSize offset, VkDeviceSize size);
 	VkResult pullFromDevice(VkDeviceSize offset, VkDeviceSize size);
+	void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
 
 	VkBufferUsageFlags _usage;
 	bool _isHostCoherentTexelBuffer = false;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
index ed74605..8c55b3b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKBuffer.mm
@@ -66,8 +66,8 @@
 		switch (next->sType) {
 		case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
 			auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
-			dedicatedReqs->prefersDedicatedAllocation = VK_FALSE;
-			dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
+			dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation;
+			dedicatedReqs->prefersDedicatedAllocation = dedicatedReqs->requiresDedicatedAllocation;
 			break;
 		}
 		default:
@@ -93,6 +93,10 @@
 	return _deviceMemory ? _deviceMemory->addBuffer(this) : VK_SUCCESS;
 }
 
+VkResult MVKBuffer::bindDeviceMemory2(const VkBindBufferMemoryInfo* pBindInfo) {
+	return bindDeviceMemory((MVKDeviceMemory*)pBindInfo->memory, pBindInfo->memoryOffset);
+}
+
 void MVKBuffer::applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
 								   VkPipelineStageFlags dstStageMask,
 								   VkMemoryBarrier* pMemoryBarrier,
@@ -192,6 +196,28 @@
 MVKBuffer::MVKBuffer(MVKDevice* device, const VkBufferCreateInfo* pCreateInfo) : MVKResource(device), _usage(pCreateInfo->usage) {
     _byteAlignment = _device->_pMetalFeatures->mtlBufferAlignment;
     _byteCount = pCreateInfo->size;
+
+	for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+			case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO: {
+				auto* pExtMemInfo = (const VkExternalMemoryBufferCreateInfo*)next;
+				initExternalMemory(pExtMemInfo->handleTypes);
+				break;
+			}
+			default:
+				break;
+		}
+	}
+}
+
+void MVKBuffer::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
+	if (mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR)) {
+		_externalMemoryHandleTypes = handleTypes;
+		auto& xmProps = _device->getPhysicalDevice()->getExternalBufferProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR);
+		_requiresDedicatedMemoryAllocation = _requiresDedicatedMemoryAllocation || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
+	} else {
+		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateBuffer(): Only external memory handle type VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR is supported."));
+	}
 }
 
 MVKBuffer::~MVKBuffer() {
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
index a433703..45d5ba5 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDescriptorSet.mm
@@ -109,11 +109,10 @@
         const VkBufferView* pTexelBufferView = descWrite.pTexelBufferView;
         const VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock = nullptr;
         if (_device->_enabledExtensions.vk_EXT_inline_uniform_block.enabled) {
-            for (auto* next = (VkWriteDescriptorSetInlineUniformBlockEXT*)descWrite.pNext; next; next = (VkWriteDescriptorSetInlineUniformBlockEXT*)next->pNext)
-            {
+			for (const auto* next = (VkBaseInStructure*)descWrite.pNext; next; next = next->pNext) {
                 switch (next->sType) {
                 case VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT: {
-                    pInlineUniformBlock = next;
+					pInlineUniformBlock = (VkWriteDescriptorSetInlineUniformBlockEXT*)next;
                     break;
                 }
                 default:
@@ -689,11 +688,10 @@
 
 		const VkWriteDescriptorSetInlineUniformBlockEXT* pInlineUniformBlock = nullptr;
 		if (dstSet->getDevice()->_enabledExtensions.vk_EXT_inline_uniform_block.enabled) {
-			for (auto* next = (VkWriteDescriptorSetInlineUniformBlockEXT*)pDescWrite->pNext; next; next = (VkWriteDescriptorSetInlineUniformBlockEXT*)next->pNext)
-			{
+			for (const auto* next = (VkBaseInStructure*)pDescWrite->pNext; next; next = next->pNext) {
 				switch (next->sType) {
 				case VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_INLINE_UNIFORM_BLOCK_EXT: {
-					pInlineUniformBlock = next;
+					pInlineUniformBlock = (VkWriteDescriptorSetInlineUniformBlockEXT*)next;
 					break;
 				}
 				default:
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index c9527f0..f5e1f4d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -117,12 +117,9 @@
 	void getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties);
 
 	/** Populates the specified structure with the format properties of this device. */
-	void getFormatProperties(VkFormat format, VkFormatProperties2KHR* pFormatProperties);
+	void getFormatProperties(VkFormat format, VkFormatProperties2* pFormatProperties);
 
-    /** 
-     * Populates the specified structure with the image format properties
-     * supported for the specified image characteristics on this device.
-     */
+	/** Populates the image format properties supported on this device. */
     VkResult getImageFormatProperties(VkFormat format,
                                       VkImageType type,
                                       VkImageTiling tiling,
@@ -130,12 +127,13 @@
                                       VkImageCreateFlags flags,
                                       VkImageFormatProperties* pImageFormatProperties);
 
-    /** 
-     * Populates the specified structure with the image format properties
-     * supported for the specified image characteristics on this device.
-     */
-    VkResult getImageFormatProperties(const VkPhysicalDeviceImageFormatInfo2KHR* pImageFormatInfo,
-                                      VkImageFormatProperties2KHR* pImageFormatProperties);
+    /** Populates the image format properties supported on this device. */
+    VkResult getImageFormatProperties(const VkPhysicalDeviceImageFormatInfo2* pImageFormatInfo,
+                                      VkImageFormatProperties2* pImageFormatProperties);
+
+	/** Populates the external buffer properties supported on this device. */
+	void getExternalBufferProperties(const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+									 VkExternalBufferProperties* pExternalBufferProperties);
 
 #pragma mark Surfaces
 
@@ -244,13 +242,13 @@
 #pragma mark Memory models
 
 	/** Returns a pointer to the memory characteristics of this device. */
-    inline const VkPhysicalDeviceMemoryProperties* getPhysicalDeviceMemoryProperties() { return &_memoryProperties; }
+    inline const VkPhysicalDeviceMemoryProperties* getMemoryProperties() { return &_memoryProperties; }
 
 	/** Populates the specified memory properties with the memory characteristics of this device. */
-	VkResult getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties);
+	VkResult getMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties);
 
 	/** Populates the specified memory properties with the memory characteristics of this device. */
-	VkResult getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
+	VkResult getMemoryProperties(VkPhysicalDeviceMemoryProperties2* pMemoryProperties);
 
 	/**
 	 * Returns a bit mask of all memory type indices. 
@@ -285,6 +283,12 @@
 	/** Returns whether this is a unified memory device. */
 	bool getHasUnifiedMemory();
 
+	/** Returns the external memory properties supported for buffers for the handle type. */
+	VkExternalMemoryProperties& getExternalBufferProperties(VkExternalMemoryHandleTypeFlagBits handleType);
+
+	/** Returns the external memory properties supported for images for the handle type. */
+	VkExternalMemoryProperties& getExternalImageProperties(VkExternalMemoryHandleTypeFlagBits handleType);
+
 	
 #pragma mark Metal
 
@@ -339,12 +343,14 @@
 	uint64_t getVRAMSize();
 	uint64_t getRecommendedMaxWorkingSetSize();
 	uint64_t getCurrentAllocatedSize();
+	void initExternalMemoryProperties();
 	void initExtensions();
 	MVKVector<MVKQueueFamily*>& getQueueFamilies();
 	void initPipelineCacheUUID();
 	uint32_t getHighestMTLFeatureSet();
 	uint64_t getSpirvCrossRevision();
-	bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo);
+	bool getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo);
+	void populate(VkPhysicalDeviceIDProperties* pDevIdProps);
 	void logGPUInfo();
 
 	id<MTLDevice> _mtlDevice;
@@ -362,6 +368,8 @@
 	uint32_t _hostCoherentMemoryTypes;
 	uint32_t _privateMemoryTypes;
 	uint32_t _lazilyAllocatedMemoryTypes;
+	VkExternalMemoryProperties _mtlBufferExternalMemoryProperties;
+	VkExternalMemoryProperties _mtlTextureExternalMemoryProperties;
 };
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index 442f23c..00b8b42 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -62,166 +62,216 @@
 }
 
 void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures* features) {
-    if (features) { *features = _features; }
+    *features = _features;
 }
 
 void MVKPhysicalDevice::getFeatures(VkPhysicalDeviceFeatures2* features) {
-    if (features) {
-        features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
-        features->features = _features;
-        for (auto* next = (VkBaseOutStructure*)features->pNext; next; next = next->pNext) {
-            switch ((uint32_t)next->sType) {
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: {
-                    auto* storageFeatures = (VkPhysicalDevice16BitStorageFeatures*)next;
-                    storageFeatures->storageBuffer16BitAccess = true;
-                    storageFeatures->uniformAndStorageBuffer16BitAccess = true;
-                    storageFeatures->storagePushConstant16 = true;
-                    storageFeatures->storageInputOutput16 = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR: {
-                    auto* storageFeatures = (VkPhysicalDevice8BitStorageFeaturesKHR*)next;
-                    storageFeatures->storageBuffer8BitAccess = true;
-                    storageFeatures->uniformAndStorageBuffer8BitAccess = true;
-                    storageFeatures->storagePushConstant8 = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR: {
-                    auto* f16Features = (VkPhysicalDeviceFloat16Int8FeaturesKHR*)next;
-                    f16Features->shaderFloat16 = true;
-                    f16Features->shaderInt8 = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
-                    auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
-                    uboLayoutFeatures->uniformBufferStandardLayout = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
-                    auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
-                    varPtrFeatures->variablePointersStorageBuffer = true;
-                    varPtrFeatures->variablePointers = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: {
-                    auto* interlockFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next;
-                    interlockFeatures->fragmentShaderSampleInterlock = _metalFeatures.rasterOrderGroups;
-                    interlockFeatures->fragmentShaderPixelInterlock = _metalFeatures.rasterOrderGroups;
-                    interlockFeatures->fragmentShaderShadingRateInterlock = false;    // Requires variable rate shading; not supported yet in Metal
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT: {
-                    auto* hostQueryResetFeatures = (VkPhysicalDeviceHostQueryResetFeaturesEXT*)next;
-                    hostQueryResetFeatures->hostQueryReset = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: {
-                    auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next;
-                    scalarLayoutFeatures->scalarBlockLayout = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
-                    auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
-                    texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)];
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: {
-                    auto* divisorFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next;
-                    divisorFeatures->vertexAttributeInstanceRateDivisor = true;
-                    divisorFeatures->vertexAttributeInstanceRateZeroDivisor = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_EXTX: {
-                    auto* portabilityFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesEXTX*)next;
-                    portabilityFeatures->triangleFans = false;
-                    portabilityFeatures->separateStencilMaskRef = true;
-					portabilityFeatures->events = true;
-                    portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle || _metalFeatures.nativeTextureSwizzle;
-                    portabilityFeatures->samplerMipLodBias = false;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL: {
-                    auto* shaderIntFuncsFeatures = (VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL*)next;
-                    shaderIntFuncsFeatures->shaderIntegerFunctions2 = true;
-                    break;
-                }
-                case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT: {
-                    auto* inlineUniformBlockFeatures = (VkPhysicalDeviceInlineUniformBlockFeaturesEXT*)next;
-                    inlineUniformBlockFeatures->inlineUniformBlock = true;
-                    inlineUniformBlockFeatures->descriptorBindingInlineUniformBlockUpdateAfterBind = true;
-                    break;
-                }
-                default:
-                    break;
-            }
-        }
-    }
+	features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
+	features->features = _features;
+	for (auto* next = (VkBaseOutStructure*)features->pNext; next; next = next->pNext) {
+		switch ((uint32_t)next->sType) {
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES: {
+				auto* storageFeatures = (VkPhysicalDevice16BitStorageFeatures*)next;
+				storageFeatures->storageBuffer16BitAccess = true;
+				storageFeatures->uniformAndStorageBuffer16BitAccess = true;
+				storageFeatures->storagePushConstant16 = true;
+				storageFeatures->storageInputOutput16 = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR: {
+				auto* storageFeatures = (VkPhysicalDevice8BitStorageFeaturesKHR*)next;
+				storageFeatures->storageBuffer8BitAccess = true;
+				storageFeatures->uniformAndStorageBuffer8BitAccess = true;
+				storageFeatures->storagePushConstant8 = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR: {
+				auto* f16Features = (VkPhysicalDeviceFloat16Int8FeaturesKHR*)next;
+				f16Features->shaderFloat16 = true;
+				f16Features->shaderInt8 = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_UNIFORM_BUFFER_STANDARD_LAYOUT_FEATURES_KHR: {
+				auto* uboLayoutFeatures = (VkPhysicalDeviceUniformBufferStandardLayoutFeaturesKHR*)next;
+				uboLayoutFeatures->uniformBufferStandardLayout = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES: {
+				auto* varPtrFeatures = (VkPhysicalDeviceVariablePointerFeatures*)next;
+				varPtrFeatures->variablePointersStorageBuffer = true;
+				varPtrFeatures->variablePointers = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADER_INTERLOCK_FEATURES_EXT: {
+				auto* interlockFeatures = (VkPhysicalDeviceFragmentShaderInterlockFeaturesEXT*)next;
+				interlockFeatures->fragmentShaderSampleInterlock = _metalFeatures.rasterOrderGroups;
+				interlockFeatures->fragmentShaderPixelInterlock = _metalFeatures.rasterOrderGroups;
+				interlockFeatures->fragmentShaderShadingRateInterlock = false;    // Requires variable rate shading; not supported yet in Metal
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_HOST_QUERY_RESET_FEATURES_EXT: {
+				auto* hostQueryResetFeatures = (VkPhysicalDeviceHostQueryResetFeaturesEXT*)next;
+				hostQueryResetFeatures->hostQueryReset = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SCALAR_BLOCK_LAYOUT_FEATURES_EXT: {
+				auto* scalarLayoutFeatures = (VkPhysicalDeviceScalarBlockLayoutFeaturesEXT*)next;
+				scalarLayoutFeatures->scalarBlockLayout = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT: {
+				auto* texelBuffAlignFeatures = (VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT*)next;
+				texelBuffAlignFeatures->texelBufferAlignment = _metalFeatures.texelBuffers && [_mtlDevice respondsToSelector: @selector(minimumLinearTextureAlignmentForPixelFormat:)];
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT: {
+				auto* divisorFeatures = (VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT*)next;
+				divisorFeatures->vertexAttributeInstanceRateDivisor = true;
+				divisorFeatures->vertexAttributeInstanceRateZeroDivisor = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_EXTX: {
+				auto* portabilityFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesEXTX*)next;
+				portabilityFeatures->triangleFans = false;
+				portabilityFeatures->separateStencilMaskRef = true;
+				portabilityFeatures->events = true;
+				portabilityFeatures->standardImageViews = _mvkInstance->getMoltenVKConfiguration()->fullImageViewSwizzle || _metalFeatures.nativeTextureSwizzle;
+				portabilityFeatures->samplerMipLodBias = false;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_INTEGER_FUNCTIONS_2_FEATURES_INTEL: {
+				auto* shaderIntFuncsFeatures = (VkPhysicalDeviceShaderIntegerFunctions2FeaturesINTEL*)next;
+				shaderIntFuncsFeatures->shaderIntegerFunctions2 = true;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT: {
+				auto* inlineUniformBlockFeatures = (VkPhysicalDeviceInlineUniformBlockFeaturesEXT*)next;
+				inlineUniformBlockFeatures->inlineUniformBlock = true;
+				inlineUniformBlockFeatures->descriptorBindingInlineUniformBlockUpdateAfterBind = true;
+				break;
+			}
+			default:
+				break;
+		}
+	}
 }
 
 void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties* properties) {
-    if (properties) { *properties = _properties; }
+	*properties = _properties;
 }
 
 void MVKPhysicalDevice::getProperties(VkPhysicalDeviceProperties2* properties) {
-    if (properties) {
-        properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
-        properties->properties = _properties;
-		for (auto* next = (VkBaseOutStructure*)properties->pNext; next; next = next->pNext) {
-			switch ((uint32_t)next->sType) {
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES: {
-                auto* pointClipProps = (VkPhysicalDevicePointClippingProperties*)next;
-                pointClipProps->pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES;
-                break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: {
-                auto* maint3Props = (VkPhysicalDeviceMaintenance3Properties*)next;
-                maint3Props->maxPerSetDescriptors = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount + _metalFeatures.maxPerStageSamplerCount) * 4;
-                maint3Props->maxMemoryAllocationSize = _metalFeatures.maxMTLBufferSize;
-                break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: {
-                auto* pushDescProps = (VkPhysicalDevicePushDescriptorPropertiesKHR*)next;
-                pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources;
-                break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT: {
-                auto* texelBuffAlignProps = (VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT*)next;
-                // Save the 'next' pointer; we'll unintentionally overwrite it
-                // on the next line. Put it back when we're done.
-                void* savedNext = texelBuffAlignProps->pNext;
-                *texelBuffAlignProps = _texelBuffAlignProperties;
-                texelBuffAlignProps->pNext = savedNext;
-                break;
-            }
-            case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
-                auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
-                divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
-                break;
-            }
+	properties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
+	properties->properties = _properties;
+	for (auto* next = (VkBaseOutStructure*)properties->pNext; next; next = next->pNext) {
+		switch ((uint32_t)next->sType) {
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_POINT_CLIPPING_PROPERTIES: {
+				auto* pointClipProps = (VkPhysicalDevicePointClippingProperties*)next;
+				pointClipProps->pointClippingBehavior = VK_POINT_CLIPPING_BEHAVIOR_ALL_CLIP_PLANES;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MAINTENANCE_3_PROPERTIES: {
+				auto* maint3Props = (VkPhysicalDeviceMaintenance3Properties*)next;
+				maint3Props->maxPerSetDescriptors = (_metalFeatures.maxPerStageBufferCount + _metalFeatures.maxPerStageTextureCount + _metalFeatures.maxPerStageSamplerCount) * 4;
+				maint3Props->maxMemoryAllocationSize = _metalFeatures.maxMTLBufferSize;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PUSH_DESCRIPTOR_PROPERTIES_KHR: {
+				auto* pushDescProps = (VkPhysicalDevicePushDescriptorPropertiesKHR*)next;
+				pushDescProps->maxPushDescriptors = _properties.limits.maxPerStageResources;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_PROPERTIES_EXT: {
+				auto* texelBuffAlignProps = (VkPhysicalDeviceTexelBufferAlignmentPropertiesEXT*)next;
+				// Save the 'next' pointer; we'll unintentionally overwrite it
+				// on the next line. Put it back when we're done.
+				void* savedNext = texelBuffAlignProps->pNext;
+				*texelBuffAlignProps = _texelBuffAlignProperties;
+				texelBuffAlignProps->pNext = savedNext;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_PROPERTIES_EXT: {
+				auto* divisorProps = (VkPhysicalDeviceVertexAttributeDivisorPropertiesEXT*)next;
+				divisorProps->maxVertexAttribDivisor = kMVKUndefinedLargeUInt32;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES: {
+				populate((VkPhysicalDeviceIDProperties*)next);
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_PROPERTIES_EXTX: {
 				auto* portabilityProps = (VkPhysicalDevicePortabilitySubsetPropertiesEXTX*)next;
 				portabilityProps->minVertexInputBindingStrideAlignment = 4;
 				break;
 			}
-            default:
-                break;
-            }
-        }
-    }
+			default:
+				break;
+		}
+	}
+}
+
+// Populates the device ID properties structure
+void MVKPhysicalDevice::populate(VkPhysicalDeviceIDProperties* pDevIdProps) {
+
+	uint8_t* uuid;
+	size_t uuidComponentOffset;
+
+	//  ---- Device ID ----------------------------------------------
+	uuid = pDevIdProps->deviceUUID;
+	uuidComponentOffset = 0;
+	mvkClear(uuid, VK_UUID_SIZE);
+
+	// First 4 bytes contains GPU vendor ID
+	uint32_t vendorID = _properties.vendorID;
+	*(uint32_t*)&uuid[uuidComponentOffset] = NSSwapHostIntToBig(vendorID);
+	uuidComponentOffset += sizeof(vendorID);
+
+	// Next 4 bytes contains GPU device ID
+	uint32_t deviceID = _properties.deviceID;
+	*(uint32_t*)&uuid[uuidComponentOffset] = NSSwapHostIntToBig(deviceID);
+	uuidComponentOffset += sizeof(deviceID);
+
+	// Last 8 bytes contain the GPU registry ID
+	uint64_t regID = mvkGetRegistryID(_mtlDevice);
+	*(uint64_t*)&uuid[uuidComponentOffset] = NSSwapHostLongLongToBig(regID);
+	uuidComponentOffset += sizeof(regID);
+
+
+	// ---- Driver ID ----------------------------------------------
+	uuid = pDevIdProps->driverUUID;
+	uuidComponentOffset = 0;
+	mvkClear(uuid, VK_UUID_SIZE);
+
+	// First 4 bytes contains MoltenVK prefix
+	const char* mvkPfx = "MVK";
+	size_t mvkPfxLen = strlen(mvkPfx);
+	mvkCopy(&uuid[uuidComponentOffset], (uint8_t*)mvkPfx, mvkPfxLen);
+	uuidComponentOffset += mvkPfxLen + 1;
+
+	// Next 4 bytes contains MoltenVK version
+	uint32_t mvkVersion = MVK_VERSION;
+	*(uint32_t*)&uuid[uuidComponentOffset] = NSSwapHostIntToBig(mvkVersion);
+	uuidComponentOffset += sizeof(mvkVersion);
+
+	// Next 4 bytes contains highest Metal feature set supported by this device
+	uint32_t mtlFeatSet = getHighestMTLFeatureSet();
+	*(uint32_t*)&uuid[uuidComponentOffset] = NSSwapHostIntToBig(mtlFeatSet);
+	uuidComponentOffset += sizeof(mtlFeatSet);
+
+
+	// ---- LUID ignored for Metal devices ------------------------
+	mvkClear(pDevIdProps->deviceLUID, VK_LUID_SIZE);
+	pDevIdProps->deviceNodeMask = 0;
+	pDevIdProps->deviceLUIDValid = VK_FALSE;
 }
 
 void MVKPhysicalDevice::getFormatProperties(VkFormat format, VkFormatProperties* pFormatProperties) {
-    if (pFormatProperties) {
-		*pFormatProperties = _pixelFormats.getVkFormatProperties(format);
-	}
+	*pFormatProperties = _pixelFormats.getVkFormatProperties(format);
 }
 
-void MVKPhysicalDevice::getFormatProperties(VkFormat format,
-                                            VkFormatProperties2KHR* pFormatProperties) {
-	if (pFormatProperties) {
-		pFormatProperties->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR;
-		pFormatProperties->formatProperties = _pixelFormats.getVkFormatProperties(format);
-	}
+void MVKPhysicalDevice::getFormatProperties(VkFormat format, VkFormatProperties2KHR* pFormatProperties) {
+	pFormatProperties->sType = VK_STRUCTURE_TYPE_FORMAT_PROPERTIES_2_KHR;
+	pFormatProperties->formatProperties = _pixelFormats.getVkFormatProperties(format);
 }
 
 VkResult MVKPhysicalDevice::getImageFormatProperties(VkFormat format,
@@ -357,32 +407,41 @@
 	return VK_SUCCESS;
 }
 
-VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo,
-                                                     VkImageFormatProperties2KHR* pImageFormatProperties) {
+VkResult MVKPhysicalDevice::getImageFormatProperties(const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo,
+													 VkImageFormatProperties2* pImageFormatProperties) {
 
-    if ( !pImageFormatInfo || pImageFormatInfo->sType != VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2_KHR ) {
-        return VK_ERROR_FORMAT_NOT_SUPPORTED;
-    }
+	for (const auto* nextInfo = (VkBaseInStructure*)pImageFormatInfo->pNext; nextInfo; nextInfo = nextInfo->pNext) {
+		switch (nextInfo->sType) {
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_EXTERNAL_IMAGE_FORMAT_INFO: {
+				// Return information about external memory support for MTLTexture.
+				// Search VkImageFormatProperties2 for the corresponding VkExternalImageFormatProperties and populate it.
+				auto* pExtImgFmtInfo = (VkPhysicalDeviceExternalImageFormatInfo*)nextInfo;
+				for (auto* nextProps = (VkBaseOutStructure*)pImageFormatProperties->pNext; nextProps; nextProps = nextProps->pNext) {
+					if (nextProps->sType == VK_STRUCTURE_TYPE_EXTERNAL_IMAGE_FORMAT_PROPERTIES) {
+						auto* pExtImgFmtProps = (VkExternalImageFormatProperties*)nextProps;
+						pExtImgFmtProps->externalMemoryProperties = getExternalImageProperties(pExtImgFmtInfo->handleType);
+					}
+				}
+				break;
+			}
+			default:
+				break;
+		}
+	}
 
-    if ( !_pixelFormats.isSupported(pImageFormatInfo->format) ) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
+	if ( !_pixelFormats.isSupported(pImageFormatInfo->format) ) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
 
 	if ( !getImageViewIsSupported(pImageFormatInfo) ) { return VK_ERROR_FORMAT_NOT_SUPPORTED; }
 
-	if (pImageFormatProperties) {
-		pImageFormatProperties->sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2_KHR;
-		return getImageFormatProperties(pImageFormatInfo->format, pImageFormatInfo->type,
-										pImageFormatInfo->tiling, pImageFormatInfo->usage,
-										pImageFormatInfo->flags,
-										&pImageFormatProperties->imageFormatProperties);
-	}
-
-	return VK_SUCCESS;
+	return getImageFormatProperties(pImageFormatInfo->format, pImageFormatInfo->type,
+									pImageFormatInfo->tiling, pImageFormatInfo->usage,
+									pImageFormatInfo->flags,
+									&pImageFormatProperties->imageFormatProperties);
 }
 
 // If the image format info links portability image view info, test if an image view of that configuration is supported
-bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2KHR *pImageFormatInfo) {
-	auto* next = (MVKVkAPIStructHeader*)pImageFormatInfo->pNext;
-	while (next) {
+bool MVKPhysicalDevice::getImageViewIsSupported(const VkPhysicalDeviceImageFormatInfo2 *pImageFormatInfo) {
+	for (const auto* next = (VkBaseInStructure*)pImageFormatInfo->pNext; next; next = next->pNext) {
 		switch ((uint32_t)next->sType) {
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_VIEW_SUPPORT_EXTX: {
 				auto* portImgViewInfo = (VkPhysicalDeviceImageViewSupportEXTX*)next;
@@ -390,7 +449,7 @@
 				// Create an image view and test whether it could be configured
 				VkImageViewCreateInfo viewInfo = {
 					.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
-					.pNext = (VkStructureType*)portImgViewInfo->pNext,
+					.pNext = portImgViewInfo->pNext,
 					.flags = portImgViewInfo->flags,
 					.image = nullptr,
 					.viewType = portImgViewInfo->viewType,
@@ -411,7 +470,6 @@
 																	 mtlPixFmt, useSwizzle) == VK_SUCCESS);
 			}
 			default:
-				next = (MVKVkAPIStructHeader*)next->pNext;
 				break;
 		}
 	}
@@ -419,6 +477,27 @@
 	return true;
 }
 
+void MVKPhysicalDevice::getExternalBufferProperties(const VkPhysicalDeviceExternalBufferInfo* pExternalBufferInfo,
+													VkExternalBufferProperties* pExternalBufferProperties) {
+	pExternalBufferProperties->externalMemoryProperties = getExternalBufferProperties(pExternalBufferInfo->handleType);
+}
+
+static VkExternalMemoryProperties _emptyExtMemProps = {};
+
+VkExternalMemoryProperties& MVKPhysicalDevice::getExternalBufferProperties(VkExternalMemoryHandleTypeFlagBits handleType) {
+	switch (handleType) {
+		case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR:		return _mtlBufferExternalMemoryProperties;
+		default: 													return _emptyExtMemProps;
+	}
+}
+
+VkExternalMemoryProperties& MVKPhysicalDevice::getExternalImageProperties(VkExternalMemoryHandleTypeFlagBits handleType) {
+	switch (handleType) {
+		case VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR:		return _mtlTextureExternalMemoryProperties;
+		default: 													return _emptyExtMemProps;
+	}
+}
+
 
 #pragma mark Surfaces
 
@@ -722,17 +801,16 @@
 #pragma mark Memory models
 
 /** Populates the specified memory properties with the memory characteristics of this device. */
-VkResult MVKPhysicalDevice::getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
+VkResult MVKPhysicalDevice::getMemoryProperties(VkPhysicalDeviceMemoryProperties* pMemoryProperties) {
 	*pMemoryProperties = _memoryProperties;
 	return VK_SUCCESS;
 }
 
-VkResult MVKPhysicalDevice::getPhysicalDeviceMemoryProperties(VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
-	if (pMemoryProperties) {
-		pMemoryProperties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
-		pMemoryProperties->memoryProperties = _memoryProperties;
-		for (auto* next = (VkBaseOutStructure*)pMemoryProperties->pNext; next; next = next->pNext) {
-			switch (next->sType) {
+VkResult MVKPhysicalDevice::getMemoryProperties(VkPhysicalDeviceMemoryProperties2* pMemoryProperties) {
+	pMemoryProperties->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2;
+	pMemoryProperties->memoryProperties = _memoryProperties;
+	for (auto* next = (VkBaseOutStructure*)pMemoryProperties->pNext; next; next = next->pNext) {
+		switch (next->sType) {
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT: {
 				auto* budgetProps = (VkPhysicalDeviceMemoryBudgetPropertiesEXT*)next;
 				mvkClear(budgetProps->heapBudget, VK_MAX_MEMORY_HEAPS);
@@ -747,7 +825,6 @@
 			}
 			default:
 				break;
-			}
 		}
 	}
 	return VK_SUCCESS;
@@ -765,8 +842,9 @@
 	initMetalFeatures();        		// Call first.
 	initFeatures();             		// Call second.
 	initProperties();           		// Call third.
-	initMemoryProperties();
 	initExtensions();
+	initMemoryProperties();
+	initExternalMemoryProperties();
 	logGPUInfo();
 }
 
@@ -1470,8 +1548,7 @@
 	if (regID) {
 		entry = IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(regID));
 		if (entry) {
-			// That returned the IOGraphicsAccelerator nub. Its parent, then, is the actual
-			// PCI device.
+			// That returned the IOGraphicsAccelerator nub. Its parent, then, is the actual PCI device.
 			io_registry_entry_t parent;
 			if (IORegistryEntryGetParentEntry(entry, kIOServicePlane, &parent) == kIOReturnSuccess) {
 				isFound = true;
@@ -1665,7 +1742,7 @@
 	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mvkVersion);
 	uuidComponentOffset += sizeof(mvkVersion);
 
-	// Next 4 bytes contains hightest Metal feature set supported by this device
+	// Next 4 bytes contains highest Metal feature set supported by this device
 	uint32_t mtlFeatSet = getHighestMTLFeatureSet();
 	*(uint32_t*)&_properties.pipelineCacheUUID[uuidComponentOffset] = NSSwapHostIntToBig(mtlFeatSet);
 	uuidComponentOffset += sizeof(mtlFeatSet);
@@ -1892,6 +1969,23 @@
 #endif
 }
 
+void MVKPhysicalDevice::initExternalMemoryProperties() {
+
+	// Buffers
+	_mtlBufferExternalMemoryProperties.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT |
+																 VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
+																 VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
+	_mtlBufferExternalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
+	_mtlBufferExternalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR;
+
+	// Images
+	_mtlTextureExternalMemoryProperties.externalMemoryFeatures = (VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT |
+																  VK_EXTERNAL_MEMORY_FEATURE_EXPORTABLE_BIT |
+																  VK_EXTERNAL_MEMORY_FEATURE_IMPORTABLE_BIT);
+	_mtlTextureExternalMemoryProperties.exportFromImportedHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+	_mtlTextureExternalMemoryProperties.compatibleHandleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR;
+}
+
 void MVKPhysicalDevice::initExtensions() {
 	MVKExtensionList* pWritableExtns = (MVKExtensionList*)&_supportedExtensions;
 	pWritableExtns->disableAllButEnabledDeviceExtensions();
@@ -2815,8 +2909,7 @@
 					   &pdFeats2.features.robustBufferAccess, 55);
 	}
 
-	auto* next = (MVKVkAPIStructHeader*)pCreateInfo->pNext;
-	while (next) {
+	for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
 		switch ((uint32_t)next->sType) {
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2: {
 				auto* requestedFeatures = (VkPhysicalDeviceFeatures2*)next;
@@ -2905,7 +2998,6 @@
 			default:
 				break;
 		}
-		next = (MVKVkAPIStructHeader*)next->pNext;
 	}
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
index 5fc2cb2..0a51525 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.h
@@ -27,6 +27,10 @@
 class MVKBuffer;
 class MVKImage;
 
+// TODO: These are inoperable placeholders until VK_KHR_external_memory_metal defines them properly
+static const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
+static const VkExternalMemoryHandleTypeFlagBits VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR = VK_EXTERNAL_MEMORY_HANDLE_TYPE_FLAG_BITS_MAX_ENUM;
+
 
 #pragma mark MVKDeviceMemory
 
@@ -141,6 +145,7 @@
 	bool ensureHostMemory();
 	void freeHostMemory();
 	MVKResource* getDedicatedResource();
+	void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
 
 	MVKVectorInline<MVKBuffer*, 4> _buffers;
 	MVKVectorInline<MVKImage*, 4> _images;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
index 714a8f9..efcce8d 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDeviceMemory.mm
@@ -266,10 +266,7 @@
 
 MVKResource* MVKDeviceMemory::getDedicatedResource() {
 	MVKAssert(_isDedicated, "This method should only be called on dedicated allocations!");
-	if (_buffers.empty())
-		return _images[0];
-	else
-		return _buffers[0];
+	return _buffers.empty() ? (MVKResource*)_images[0] : (MVKResource*)_buffers[0];
 }
 
 MVKDeviceMemory::MVKDeviceMemory(MVKDevice* device,
@@ -284,22 +281,28 @@
 
 	VkImage dedicatedImage = VK_NULL_HANDLE;
 	VkBuffer dedicatedBuffer = VK_NULL_HANDLE;
-	auto* next = (VkStructureType*)pAllocateInfo->pNext;
-	while (next) {
-		switch (*next) {
-		case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO: {
-			auto* pDedicatedInfo = (VkMemoryDedicatedAllocateInfo*)next;
-			dedicatedImage = pDedicatedInfo->image;
-			dedicatedBuffer = pDedicatedInfo->buffer;
-			next = (VkStructureType*)pDedicatedInfo->pNext;
-			break;
-		}
-		default:
-			next = (VkStructureType*)((VkMemoryAllocateInfo*)next)->pNext;
-			break;
+	VkExternalMemoryHandleTypeFlags handleTypes = 0;
+	for (const auto* next = (const VkBaseInStructure*)pAllocateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+			case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO: {
+				auto* pDedicatedInfo = (VkMemoryDedicatedAllocateInfo*)next;
+				dedicatedImage = pDedicatedInfo->image;
+				dedicatedBuffer = pDedicatedInfo->buffer;
+				_isDedicated = dedicatedImage || dedicatedBuffer;
+				break;
+			}
+			case VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO: {
+				auto* pExpMemInfo = (VkExportMemoryAllocateInfo*)next;
+				handleTypes = pExpMemInfo->handleTypes;
+				break;
+			}
+			default:
+				break;
 		}
 	}
 
+	initExternalMemory(handleTypes);	// After setting _isDedicated
+
 	// "Dedicated" means this memory can only be used for this image or buffer.
 	if (dedicatedImage) {
 #if MVK_MACOS
@@ -316,14 +319,16 @@
 			}
 		}
 #endif
-		_isDedicated = true;
 		_images.push_back((MVKImage*)dedicatedImage);
 		return;
 	}
 
-	// If we can, create a MTLHeap. This should happen before creating the buffer
-	// allowing us to map its contents.
-	if (!dedicatedImage && !dedicatedBuffer) {
+	if (dedicatedBuffer) {
+		_buffers.push_back((MVKBuffer*)dedicatedBuffer);
+	}
+
+	// If we can, create a MTLHeap. This should happen before creating the buffer, allowing us to map its contents.
+	if ( !_isDedicated ) {
 		if (!ensureMTLHeap()) {
 			setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate VkDeviceMemory of size %llu bytes.", _allocationSize));
 			return;
@@ -334,10 +339,26 @@
 	if (isMemoryHostCoherent() && !ensureMTLBuffer() ) {
 		setConfigurationResult(reportError(VK_ERROR_OUT_OF_DEVICE_MEMORY, "Could not allocate a host-coherent VkDeviceMemory of size %llu bytes. The maximum memory-aligned size of a host-coherent VkDeviceMemory is %llu bytes.", _allocationSize, _device->_pMetalFeatures->maxMTLBufferSize));
 	}
+}
 
-	if (dedicatedBuffer) {
-		_isDedicated = true;
-		_buffers.push_back((MVKBuffer*)dedicatedBuffer);
+void MVKDeviceMemory::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
+	if ( !handleTypes ) { return; }
+	
+	if ( !mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR | VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR) ) {
+		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "vkAllocateMemory(): Only external memory handle types VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR or VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR are supported."));
+	}
+
+	bool requiresDedicated = false;
+	if (mvkIsAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR)) {
+		auto& xmProps = _device->getPhysicalDevice()->getExternalBufferProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLBUFFER_BIT_KHR);
+		requiresDedicated = requiresDedicated || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
+	}
+	if (mvkIsAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR)) {
+		auto& xmProps = _device->getPhysicalDevice()->getExternalImageProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR);
+		requiresDedicated = requiresDedicated || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
+	}
+	if (requiresDedicated && !_isDedicated) {
+		setConfigurationResult(reportError(VK_ERROR_INITIALIZATION_FAILED, "vkAllocateMemory(): External memory requires a dedicated VkBuffer or VkImage."));
 	}
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
index 89e5af0..8bbfa05 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.h
@@ -130,6 +130,9 @@
 	/** Binds this resource to the specified offset within the specified memory allocation. */
 	VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) override;
 
+	/** Binds this resource to the specified offset within the specified memory allocation. */
+	virtual VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo);
+
 	/** Applies the specified global memory barrier. */
     void applyMemoryBarrier(VkPipelineStageFlags srcStageMask,
                             VkPipelineStageFlags dstStageMask,
@@ -235,6 +238,7 @@
 	bool validateUseTexelBuffer();
 	void initSubresources(const VkImageCreateInfo* pCreateInfo);
 	void initSubresourceLayout(MVKImageSubresource& imgSubRez);
+	void initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes);
 	id<MTLTexture> newMTLTexture();
 	void releaseMTLTexture();
     void releaseIOSurface();
@@ -380,7 +384,7 @@
 public:
 
 	/** Binds this resource according to the specified bind information. */
-	VkResult bindDeviceMemory2(const void* pBindInfo) override;
+	VkResult bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) override;
 
 
 #pragma mark Construction
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
index 89fb6dd..a90913b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKImage.mm
@@ -182,8 +182,9 @@
 		case VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS: {
 			auto* dedicatedReqs = (VkMemoryDedicatedRequirements*)next;
 			bool writable = mvkIsAnyFlagEnabled(_usage, VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT);
-			dedicatedReqs->prefersDedicatedAllocation = !_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps);
-			dedicatedReqs->requiresDedicatedAllocation = VK_FALSE;
+			dedicatedReqs->requiresDedicatedAllocation = _requiresDedicatedMemoryAllocation;
+			dedicatedReqs->prefersDedicatedAllocation = (dedicatedReqs->requiresDedicatedAllocation ||
+														 (!_usesTexelBuffer && (writable || !_device->_pMetalFeatures->placementHeaps)));
 			break;
 		}
 		default:
@@ -206,6 +207,10 @@
 	return _deviceMemory ? _deviceMemory->addImage(this) : VK_SUCCESS;
 }
 
+VkResult MVKImage::bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) {
+	return bindDeviceMemory((MVKDeviceMemory*)pBindInfo->memory, pBindInfo->memoryOffset);
+}
+
 bool MVKImage::validateUseTexelBuffer() {
 	VkExtent2D blockExt = getPixelFormats()->getBlockTexelSize(_mtlPixelFormat);
 	bool isUncompressed = blockExt.width == 1 && blockExt.height == 1;
@@ -615,6 +620,18 @@
 	}
 
     initSubresources(pCreateInfo);
+
+	for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+			case VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO: {
+				auto* pExtMemInfo = (const VkExternalMemoryImageCreateInfo*)next;
+				initExternalMemory(pExtMemInfo->handleTypes);
+				break;
+			}
+			default:
+				break;
+		}
+	}
 }
 
 VkSampleCountFlagBits MVKImage::validateSamples(const VkImageCreateInfo* pCreateInfo, bool isAttachment) {
@@ -784,6 +801,16 @@
 	layout.depthPitch = bytesPerLayerCurrLevel;
 }
 
+void MVKImage::initExternalMemory(VkExternalMemoryHandleTypeFlags handleTypes) {
+	if (mvkIsOnlyAnyFlagEnabled(handleTypes, VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR)) {
+		_externalMemoryHandleTypes = handleTypes;
+		auto& xmProps = _device->getPhysicalDevice()->getExternalImageProperties(VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR);
+		_requiresDedicatedMemoryAllocation = _requiresDedicatedMemoryAllocation || mvkIsAnyFlagEnabled(xmProps.externalMemoryFeatures, VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
+	} else {
+		setConfigurationResult(reportError(VK_ERROR_FEATURE_NOT_PRESENT, "vkCreateImage(): Only external memory handle type VK_EXTERNAL_MEMORY_HANDLE_TYPE_MTLTEXTURE_BIT_KHR is supported."));
+	}
+}
+
 MVKImage::~MVKImage() {
 	if (_deviceMemory) { _deviceMemory->removeImage(this); }
 	releaseMTLTexture();
@@ -991,10 +1018,9 @@
 #pragma mark -
 #pragma mark MVKPeerSwapchainImage
 
-VkResult MVKPeerSwapchainImage::bindDeviceMemory2(const void* pBindInfo) {
-	const auto* imageInfo = (const VkBindImageMemoryInfo*)pBindInfo;
+VkResult MVKPeerSwapchainImage::bindDeviceMemory2(const VkBindImageMemoryInfo* pBindInfo) {
 	const VkBindImageMemorySwapchainInfoKHR* swapchainInfo = nullptr;
-	for (const auto* next = (const VkBaseInStructure*)imageInfo->pNext; next; next = next->pNext) {
+	for (const auto* next = (const VkBaseInStructure*)pBindInfo->pNext; next; next = next->pNext) {
 		switch (next->sType) {
 			case VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_SWAPCHAIN_INFO_KHR:
 				swapchainInfo = (const VkBindImageMemorySwapchainInfoKHR*)next;
@@ -1002,11 +1028,9 @@
 			default:
 				break;
 		}
-		if (swapchainInfo) { break; }
 	}
-	if (!swapchainInfo) {
-		return VK_ERROR_OUT_OF_DEVICE_MEMORY;
-	}
+	if (!swapchainInfo) { return VK_ERROR_OUT_OF_DEVICE_MEMORY; }
+
 	_swapchainIndex = swapchainInfo->imageIndex;
 	return VK_SUCCESS;
 }
@@ -1114,17 +1138,14 @@
 	_image = (MVKImage*)pCreateInfo->image;
 	_usage = _image->_usage;
 
-	auto* next = (MVKVkAPIStructHeader*)pCreateInfo->pNext;
-	while (next) {
-		switch ((uint32_t)next->sType) {
+	for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
 			case VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO: {
 				auto* pViewUsageInfo = (VkImageViewUsageCreateInfo*)next;
 				if (!(pViewUsageInfo->usage & ~_usage)) { _usage = pViewUsageInfo->usage; }
-				next = (MVKVkAPIStructHeader*)next->pNext;
 				break;
 			}
 			default:
-				next = (MVKVkAPIStructHeader*)next->pNext;
 				break;
 		}
 	}
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index aa08efa..66d7a6b 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -383,8 +383,7 @@
 	_hasDebugUtilsMessengers = false;
 	_debugReportCallbackLayerPrefix = getDriverLayer()->getName();
 
-	MVKVkAPIStructHeader* next = (MVKVkAPIStructHeader*)pCreateInfo->pNext;
-	while (next) {
+	for (const auto* next = (VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
 		switch (next->sType) {
 			case VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT:
 				createDebugReportCallback((VkDebugReportCallbackCreateInfoEXT*)next, nullptr);
@@ -395,7 +394,6 @@
 			default:
 				break;
 		}
-		next = (MVKVkAPIStructHeader*)next->pNext;
 	}
 }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
index 727f02b..c67ab8c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKPipeline.mm
@@ -944,15 +944,12 @@
 												   const SPIRVToMSLConversionConfiguration& shaderContext) {
     // Collect extension structures
     VkPipelineVertexInputDivisorStateCreateInfoEXT* pVertexInputDivisorState = nullptr;
-    auto* next = (MVKVkAPIStructHeader*)pVI->pNext;
-    while (next) {
+	for (const auto* next = (VkBaseInStructure*)pVI->pNext; next; next = next->pNext) {
         switch (next->sType) {
         case VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT:
             pVertexInputDivisorState = (VkPipelineVertexInputDivisorStateCreateInfoEXT*)next;
-            next = (MVKVkAPIStructHeader*)pVertexInputDivisorState->pNext;
             break;
         default:
-            next = (MVKVkAPIStructHeader*)next->pNext;
             break;
         }
     }
@@ -1032,15 +1029,12 @@
 
 	VkPipelineTessellationDomainOriginStateCreateInfo* pTessDomainOriginState = nullptr;
 	if (reflectData.patchKind == spv::ExecutionModeTriangles) {
-		auto* next = (MVKVkAPIStructHeader*)pTS->pNext;
-		while (next) {
+		for (const auto* next = (VkBaseInStructure*)pTS->pNext; next; next = next->pNext) {
 			switch (next->sType) {
 			case VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO:
 				pTessDomainOriginState = (VkPipelineTessellationDomainOriginStateCreateInfo*)next;
-				next = (MVKVkAPIStructHeader*)pTessDomainOriginState->pNext;
 				break;
 			default:
-				next = (MVKVkAPIStructHeader*)next->pNext;
 				break;
 			}
 		}
@@ -1131,15 +1125,12 @@
 
     VkPipelineTessellationDomainOriginStateCreateInfo* pTessDomainOriginState = nullptr;
     if (pCreateInfo->pTessellationState) {
-        auto* next = (MVKVkAPIStructHeader*)pCreateInfo->pTessellationState->pNext;
-        while (next) {
+		for (const auto* next = (VkBaseInStructure*)pCreateInfo->pTessellationState->pNext; next; next = next->pNext) {
             switch (next->sType) {
             case VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO:
                 pTessDomainOriginState = (VkPipelineTessellationDomainOriginStateCreateInfo*)next;
-                next = (MVKVkAPIStructHeader*)pTessDomainOriginState->pNext;
                 break;
             default:
-                next = (MVKVkAPIStructHeader*)next->pNext;
                 break;
             }
         }
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
index 3f5c235..fb6f7ee 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.h
@@ -47,9 +47,6 @@
 	/** Binds this resource to the specified offset within the specified memory allocation. */
 	virtual VkResult bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset);
 
-	/** Binds this resource according to the specified bind information. */
-	virtual VkResult bindDeviceMemory2(const void* pBindInfo);
-
 	/** Returns the device memory underlying this resource. */
 	inline MVKDeviceMemory* getDeviceMemory() { return _deviceMemory; }
 
@@ -88,4 +85,6 @@
 	VkDeviceSize _deviceMemoryOffset = 0;
     VkDeviceSize _byteCount = 0;
     VkDeviceSize _byteAlignment = 0;
+	VkExternalMemoryHandleTypeFlags _externalMemoryHandleTypes = 0;
+	bool _requiresDedicatedMemoryAllocation = false;
 };
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm
index ab50512..bf106f1 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKResource.mm
@@ -21,18 +21,6 @@
 #include "MVKEnvironment.h"
 
 
-struct MVKBindDeviceMemoryInfo {
-	VkStructureType sType;
-	void* pNext;
-	union {
-		VkBuffer buffer;
-		VkImage image;
-	};
-	VkDeviceMemory memory;
-	VkDeviceSize memoryOffset;
-};
-
-
 #pragma mark MVKResource
 
 VkResult MVKResource::bindDeviceMemory(MVKDeviceMemory* mvkMem, VkDeviceSize memOffset) {
@@ -46,11 +34,6 @@
 	return VK_SUCCESS;
 }
 
-VkResult MVKResource::bindDeviceMemory2(const void* pBindInfo) {
-	auto* mvkBindInfo = (const MVKBindDeviceMemoryInfo*)pBindInfo;
-	return bindDeviceMemory((MVKDeviceMemory*)mvkBindInfo->memory, mvkBindInfo->memoryOffset);
-}
-
 // Returns whether the specified global memory barrier requires a sync between this
 // texture and host memory for the purpose of the host reading texture memory.
 bool MVKResource::needsHostReadSync(VkPipelineStageFlags srcStageMask,
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 196860b..f1b4580 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -39,58 +39,60 @@
 #define MVK_EXTENSION_LAST(var, EXT, type) MVK_EXTENSION(var, EXT, type)
 #endif
 
-MVK_EXTENSION(KHR_16bit_storage, KHR_16BIT_STORAGE, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_8bit_storage, KHR_8BIT_STORAGE, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_bind_memory2, KHR_BIND_MEMORY_2, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLATE, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_device_group, KHR_DEVICE_GROUP, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(KHR_get_memory_requirements2, KHR_GET_MEMORY_REQUIREMENTS_2, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(KHR_get_surface_capabilities2, KHR_GET_SURFACE_CAPABILITIES_2, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(KHR_image_format_list, KHR_IMAGE_FORMAT_LIST, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_maintenance1, KHR_MAINTENANCE1, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_maintenance2, KHR_MAINTENANCE2, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_maintenance3, KHR_MAINTENANCE3, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_push_descriptor, KHR_PUSH_DESCRIPTOR, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_relaxed_block_layout, KHR_RELAXED_BLOCK_LAYOUT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_sampler_mirror_clamp_to_edge, KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_shader_draw_parameters, KHR_SHADER_DRAW_PARAMETERS, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_shader_float16_int8, KHR_SHADER_FLOAT16_INT8, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_storage_buffer_storage_class, KHR_STORAGE_BUFFER_STORAGE_CLASS, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_surface, KHR_SURFACE, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(KHR_swapchain, KHR_SWAPCHAIN, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_swapchain_mutable_format, KHR_SWAPCHAIN_MUTABLE_FORMAT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_hdr_metadata, EXT_HDR_METADATA, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_inline_uniform_block, EXT_INLINE_UNIFORM_BLOCK, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(EXT_post_depth_coverage, EXT_POST_DEPTH_COVERAGE, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_scalar_block_layout, EXT_SCALAR_BLOCK_LAYOUT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_swapchain_colorspace, EXT_SWAPCHAIN_COLOR_SPACE, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(EXT_texel_buffer_alignment, EXT_TEXEL_BUFFER_ALIGNMENT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXT_vertex_attribute_divisor, EXT_VERTEX_ATTRIBUTE_DIVISOR, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(EXTX_portability_subset, EXTX_PORTABILITY_SUBSET, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(MVK_ios_surface, MVK_IOS_SURFACE, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(MVK_macos_surface, MVK_MACOS_SURFACE, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(MVK_moltenvk, MVK_MOLTENVK, MVK_EXTENSION_INSTANCE)
-MVK_EXTENSION(AMD_gpu_shader_half_float, AMD_GPU_SHADER_HALF_FLOAT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(AMD_negative_viewport_height, AMD_NEGATIVE_VIEWPORT_HEIGHT, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(AMD_shader_image_load_store_lod, AMD_SHADER_IMAGE_LOAD_STORE_LOD, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(AMD_shader_trinary_minmax, AMD_SHADER_TRINARY_MINMAX, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(IMG_format_pvrtc, IMG_FORMAT_PVRTC, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION(INTEL_shader_integer_functions2, INTEL_SHADER_INTEGER_FUNCTIONS_2, MVK_EXTENSION_DEVICE)
-MVK_EXTENSION_LAST(NV_glsl_shader, NV_GLSL_SHADER, MVK_EXTENSION_DEVICE)
+MVK_EXTENSION(KHR_16bit_storage, KHR_16BIT_STORAGE, DEVICE)
+MVK_EXTENSION(KHR_8bit_storage, KHR_8BIT_STORAGE, DEVICE)
+MVK_EXTENSION(KHR_bind_memory2, KHR_BIND_MEMORY_2, DEVICE)
+MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION, DEVICE)
+MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLATE, DEVICE)
+MVK_EXTENSION(KHR_device_group, KHR_DEVICE_GROUP, DEVICE)
+MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION, INSTANCE)
+MVK_EXTENSION(KHR_external_memory, KHR_EXTERNAL_MEMORY, DEVICE)
+MVK_EXTENSION(KHR_external_memory_capabilities, KHR_EXTERNAL_MEMORY_CAPABILITIES, INSTANCE)
+MVK_EXTENSION(KHR_get_memory_requirements2, KHR_GET_MEMORY_REQUIREMENTS_2, DEVICE)
+MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2, INSTANCE)
+MVK_EXTENSION(KHR_get_surface_capabilities2, KHR_GET_SURFACE_CAPABILITIES_2, INSTANCE)
+MVK_EXTENSION(KHR_image_format_list, KHR_IMAGE_FORMAT_LIST, DEVICE)
+MVK_EXTENSION(KHR_maintenance1, KHR_MAINTENANCE1, DEVICE)
+MVK_EXTENSION(KHR_maintenance2, KHR_MAINTENANCE2, DEVICE)
+MVK_EXTENSION(KHR_maintenance3, KHR_MAINTENANCE3, DEVICE)
+MVK_EXTENSION(KHR_push_descriptor, KHR_PUSH_DESCRIPTOR, DEVICE)
+MVK_EXTENSION(KHR_relaxed_block_layout, KHR_RELAXED_BLOCK_LAYOUT, DEVICE)
+MVK_EXTENSION(KHR_sampler_mirror_clamp_to_edge, KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE, DEVICE)
+MVK_EXTENSION(KHR_shader_draw_parameters, KHR_SHADER_DRAW_PARAMETERS, DEVICE)
+MVK_EXTENSION(KHR_shader_float16_int8, KHR_SHADER_FLOAT16_INT8, DEVICE)
+MVK_EXTENSION(KHR_storage_buffer_storage_class, KHR_STORAGE_BUFFER_STORAGE_CLASS, DEVICE)
+MVK_EXTENSION(KHR_surface, KHR_SURFACE, INSTANCE)
+MVK_EXTENSION(KHR_swapchain, KHR_SWAPCHAIN, DEVICE)
+MVK_EXTENSION(KHR_swapchain_mutable_format, KHR_SWAPCHAIN_MUTABLE_FORMAT, DEVICE)
+MVK_EXTENSION(KHR_uniform_buffer_standard_layout, KHR_UNIFORM_BUFFER_STANDARD_LAYOUT, DEVICE)
+MVK_EXTENSION(KHR_variable_pointers, KHR_VARIABLE_POINTERS, DEVICE)
+MVK_EXTENSION(EXT_debug_marker, EXT_DEBUG_MARKER, DEVICE)
+MVK_EXTENSION(EXT_debug_report, EXT_DEBUG_REPORT, INSTANCE)
+MVK_EXTENSION(EXT_debug_utils, EXT_DEBUG_UTILS, INSTANCE)
+MVK_EXTENSION(EXT_fragment_shader_interlock, EXT_FRAGMENT_SHADER_INTERLOCK, DEVICE)
+MVK_EXTENSION(EXT_hdr_metadata, EXT_HDR_METADATA, DEVICE)
+MVK_EXTENSION(EXT_host_query_reset, EXT_HOST_QUERY_RESET, DEVICE)
+MVK_EXTENSION(EXT_inline_uniform_block, EXT_INLINE_UNIFORM_BLOCK, DEVICE)
+MVK_EXTENSION(EXT_memory_budget, EXT_MEMORY_BUDGET, DEVICE)
+MVK_EXTENSION(EXT_metal_surface, EXT_METAL_SURFACE, INSTANCE)
+MVK_EXTENSION(EXT_post_depth_coverage, EXT_POST_DEPTH_COVERAGE, DEVICE)
+MVK_EXTENSION(EXT_scalar_block_layout, EXT_SCALAR_BLOCK_LAYOUT, DEVICE)
+MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, DEVICE)
+MVK_EXTENSION(EXT_shader_viewport_index_layer, EXT_SHADER_VIEWPORT_INDEX_LAYER, DEVICE)
+MVK_EXTENSION(EXT_swapchain_colorspace, EXT_SWAPCHAIN_COLOR_SPACE, INSTANCE)
+MVK_EXTENSION(EXT_texel_buffer_alignment, EXT_TEXEL_BUFFER_ALIGNMENT, DEVICE)
+MVK_EXTENSION(EXT_vertex_attribute_divisor, EXT_VERTEX_ATTRIBUTE_DIVISOR, DEVICE)
+MVK_EXTENSION(EXTX_portability_subset, EXTX_PORTABILITY_SUBSET, DEVICE)
+MVK_EXTENSION(MVK_ios_surface, MVK_IOS_SURFACE, INSTANCE)
+MVK_EXTENSION(MVK_macos_surface, MVK_MACOS_SURFACE, INSTANCE)
+MVK_EXTENSION(MVK_moltenvk, MVK_MOLTENVK, INSTANCE)
+MVK_EXTENSION(AMD_gpu_shader_half_float, AMD_GPU_SHADER_HALF_FLOAT, DEVICE)
+MVK_EXTENSION(AMD_negative_viewport_height, AMD_NEGATIVE_VIEWPORT_HEIGHT, DEVICE)
+MVK_EXTENSION(AMD_shader_image_load_store_lod, AMD_SHADER_IMAGE_LOAD_STORE_LOD, DEVICE)
+MVK_EXTENSION(AMD_shader_trinary_minmax, AMD_SHADER_TRINARY_MINMAX, DEVICE)
+MVK_EXTENSION(IMG_format_pvrtc, IMG_FORMAT_PVRTC, DEVICE)
+MVK_EXTENSION(INTEL_shader_integer_functions2, INTEL_SHADER_INTEGER_FUNCTIONS_2, DEVICE)
+MVK_EXTENSION_LAST(NV_glsl_shader, NV_GLSL_SHADER, DEVICE)
 
 #undef MVK_EXTENSION
 #undef MVK_EXTENSION_LAST
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
index 4d15962..80c221a 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.mm
@@ -47,54 +47,30 @@
 // Returns whether the specified properties are valid for this platform
 static bool mvkIsSupportedOnPlatform(VkExtensionProperties* pProperties) {
 #if MVK_MACOS
-	if (pProperties == &kVkExtProps_EXT_HDR_METADATA) {
-		return mvkOSVersionIsAtLeast(10.15);
-	}
-	if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) {
-		return mvkOSVersionIsAtLeast(10.13);
-	}
-	if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) {
-		return mvkOSVersionIsAtLeast(10.13);
-	}
-	if (pProperties == &kVkExtProps_EXT_POST_DEPTH_COVERAGE) { return false; }
-	if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) {
-		return mvkOSVersionIsAtLeast(10.14);
-	}
-	if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) {
-		return mvkOSVersionIsAtLeast(10.13);
-	}
 	if (pProperties == &kVkExtProps_MVK_IOS_SURFACE) { return false; }
-	if (pProperties == &kVkExtProps_AMD_SHADER_IMAGE_LOAD_STORE_LOD) { return false; }
-	if (pProperties == &kVkExtProps_AMD_SHADER_TRINARY_MINMAX) {
-		return mvkOSVersionIsAtLeast(10.14);
-	}
 	if (pProperties == &kVkExtProps_IMG_FORMAT_PVRTC) { return false; }
+	if (pProperties == &kVkExtProps_EXT_POST_DEPTH_COVERAGE) { return false; }
+	if (pProperties == &kVkExtProps_AMD_SHADER_IMAGE_LOAD_STORE_LOD) { return false; }
+
+	if (pProperties == &kVkExtProps_EXT_HDR_METADATA) { return mvkOSVersionIsAtLeast(10.15); }
+	if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) { return mvkOSVersionIsAtLeast(10.13); }
+	if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) { return mvkOSVersionIsAtLeast(10.13); }
+	if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) { return mvkOSVersionIsAtLeast(10.14); }
+	if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) { return mvkOSVersionIsAtLeast(10.13); }
+	if (pProperties == &kVkExtProps_AMD_SHADER_TRINARY_MINMAX) { return mvkOSVersionIsAtLeast(10.14); }
 #endif
 #if MVK_IOS
+	if (pProperties == &kVkExtProps_MVK_MACOS_SURFACE) { return false; }
 	if (pProperties == &kVkExtProps_KHR_SAMPLER_MIRROR_CLAMP_TO_EDGE) { return false; }
 	if (pProperties == &kVkExtProps_EXT_HDR_METADATA) { return false; }
-	if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) {
-		return mvkOSVersionIsAtLeast(11.0);
-	}
-	if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) {
-		return mvkOSVersionIsAtLeast(11.0);
-	}
-	if (pProperties == &kVkExtProps_EXT_POST_DEPTH_COVERAGE) {
-		return mvkOSVersionIsAtLeast(11.0);
-	}
-	if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) {
-		return mvkOSVersionIsAtLeast(12.0);
-	}
-	if (pProperties == &kVkExtProps_EXT_SWAPCHAIN_COLOR_SPACE) {
-		return mvkOSVersionIsAtLeast(9.0);
-	}
-	if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) {
-		return mvkOSVersionIsAtLeast(11.0);
-	}
-	if (pProperties == &kVkExtProps_MVK_MACOS_SURFACE) { return false; }
-	if (pProperties == &kVkExtProps_AMD_SHADER_TRINARY_MINMAX) {
-		return mvkOSVersionIsAtLeast(12.0);
-	}
+
+	if (pProperties == &kVkExtProps_EXT_FRAGMENT_SHADER_INTERLOCK) { return mvkOSVersionIsAtLeast(11.0); }
+	if (pProperties == &kVkExtProps_EXT_MEMORY_BUDGET) { return mvkOSVersionIsAtLeast(11.0); }
+	if (pProperties == &kVkExtProps_EXT_POST_DEPTH_COVERAGE) { return mvkOSVersionIsAtLeast(11.0); }
+	if (pProperties == &kVkExtProps_EXT_SHADER_STENCIL_EXPORT) { return mvkOSVersionIsAtLeast(12.0); }
+	if (pProperties == &kVkExtProps_EXT_SWAPCHAIN_COLOR_SPACE) { return mvkOSVersionIsAtLeast(9.0); }
+	if (pProperties == &kVkExtProps_EXT_TEXEL_BUFFER_ALIGNMENT) { return mvkOSVersionIsAtLeast(11.0); }
+	if (pProperties == &kVkExtProps_AMD_SHADER_TRINARY_MINMAX) { return mvkOSVersionIsAtLeast(12.0); }
 #endif
 
 	return true;
@@ -127,17 +103,19 @@
 #include "MVKExtensions.def"
 }
 
+#define MVK_ENSURE_EXTENSION_TYPE(var, EXT, type) vk_ ##var.enabled = vk_ ##var.enabled && MVK_EXTENSION_ ##type;
+
 void MVKExtensionList::disableAllButEnabledInstanceExtensions() {
-#define MVK_EXTENSION_INSTANCE	true
-#define MVK_EXTENSION_DEVICE	false
-#define MVK_EXTENSION(var, EXT, type) vk_ ##var.enabled = type && vk_ ##var.enabled;
+#define MVK_EXTENSION_INSTANCE         true
+#define MVK_EXTENSION_DEVICE           false
+#define MVK_EXTENSION(var, EXT, type)  MVK_ENSURE_EXTENSION_TYPE(var, EXT, type)
 #include "MVKExtensions.def"
 }
 
 void MVKExtensionList::disableAllButEnabledDeviceExtensions() {
-#define MVK_EXTENSION_INSTANCE	false
-#define MVK_EXTENSION_DEVICE	true
-#define MVK_EXTENSION(var, EXT, type) vk_ ##var.enabled = type && vk_ ##var.enabled;
+#define MVK_EXTENSION_INSTANCE         false
+#define MVK_EXTENSION_DEVICE           true
+#define MVK_EXTENSION(var, EXT, type)  MVK_ENSURE_EXTENSION_TYPE(var, EXT, type)
 #include "MVKExtensions.def"
 }
 
diff --git a/MoltenVK/MoltenVK/Utility/MVKFoundation.h b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
index c256bf0..c2c17f3 100644
--- a/MoltenVK/MoltenVK/Utility/MVKFoundation.h
+++ b/MoltenVK/MoltenVK/Utility/MVKFoundation.h
@@ -55,12 +55,6 @@
 /** A representation of the value of 1.0 as a 16-bit half-float. */
 #define kHalfFloat1	0x3C00
 
-/** Common header for many standard Vulkan API structures. */
-typedef struct {
-	VkStructureType sType;
-	const void* pNext;
-} MVKVkAPIStructHeader;
-
 
 #pragma mark -
 #pragma mark Vertex content structures
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 5b28ae3..51990e8 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -230,7 +230,7 @@
 
 	MVKTraceVulkanCallStart();
 	MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
-	mvkPD->getPhysicalDeviceMemoryProperties(pMemoryProperties);
+	mvkPD->getMemoryProperties(pMemoryProperties);
 	MVKTraceVulkanCallEnd();
 }
 
@@ -2113,7 +2113,7 @@
 
 	MVKTraceVulkanCallStart();
     MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
-    mvkPD->getPhysicalDeviceMemoryProperties(pMemoryProperties);
+    mvkPD->getMemoryProperties(pMemoryProperties);
 	MVKTraceVulkanCallEnd();
 }
 
@@ -2665,6 +2665,21 @@
 
 
 #pragma mark -
+#pragma mark VK_KHR_external_memory_capabilities extension
+
+MVK_PUBLIC_SYMBOL void vkGetPhysicalDeviceExternalBufferPropertiesKHR(
+	VkPhysicalDevice                            physicalDevice,
+	const VkPhysicalDeviceExternalBufferInfo*   pExternalBufferInfo,
+	VkExternalBufferProperties*                 pExternalBufferProperties) {
+
+	MVKTraceVulkanCallStart();
+	MVKPhysicalDevice* mvkPD = MVKPhysicalDevice::getMVKPhysicalDevice(physicalDevice);
+	mvkPD->getExternalBufferProperties(pExternalBufferInfo, pExternalBufferProperties);
+	MVKTraceVulkanCallEnd();
+}
+
+
+#pragma mark -
 #pragma mark VK_EXT_metal_surface extension
 
 MVK_PUBLIC_SYMBOL VkResult vkCreateMetalSurfaceEXT(