Add support for VK_EXT_private_data extension.

Moved to a new model for creation: create and potentially destroy the object
within MVKDevice::create..., to hide it from vulkan.mm where all other object
creation errors are handled. We could move to this slowly over time.

Passes all 49 private data CTS tests.
diff --git a/Docs/MoltenVK_Runtime_UserGuide.md b/Docs/MoltenVK_Runtime_UserGuide.md
index f1fc211..7c83f6b 100644
--- a/Docs/MoltenVK_Runtime_UserGuide.md
+++ b/Docs/MoltenVK_Runtime_UserGuide.md
@@ -310,6 +310,7 @@
 - `VK_EXT_memory_budget` *(requires Metal 2.0)*
 - `VK_EXT_metal_surface`
 - `VK_EXT_post_depth_coverage` *(iOS, requires GPU family 4)*
+- `VK_EXT_private_data `
 - `VK_EXT_robustness2`
 - `VK_EXT_scalar_block_layout`
 - `VK_EXT_shader_stencil_export` *(requires Mac GPU family 2 or iOS GPU family 5)*
diff --git a/Docs/Whats_New.md b/Docs/Whats_New.md
index 36fe387..e404d3a 100644
--- a/Docs/Whats_New.md
+++ b/Docs/Whats_New.md
@@ -18,6 +18,8 @@
 
 Released TBD
 
+- Add support for extensions:
+	- `VK_EXT_private_data`
 - Use `VK_KHR_image_format_list` to disable `MTLTextureUsagePixelFormatView` 
   if only swizzles or `sRGB` conversion will be used for image views, improving 
   performance on *iOS* by allowing Metal to use lossless texture compression.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 3fd9fbd..1af11db 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -66,6 +66,7 @@
 class MVKCommandPool;
 class MVKCommandEncoder;
 class MVKCommandResourceFactory;
+class MVKPrivateDataSlot;
 
 
 /** The buffer index to use for vertex content. */
@@ -578,6 +579,13 @@
 	void freeMemory(MVKDeviceMemory* mvkDevMem,
 					const VkAllocationCallbacks* pAllocator);
 
+	VkResult createPrivateDataSlot(const VkPrivateDataSlotCreateInfoEXT* pCreateInfo,
+								   const VkAllocationCallbacks* pAllocator,
+								   VkPrivateDataSlotEXT* pPrivateDataSlot);
+
+	void destroyPrivateDataSlot(VkPrivateDataSlotEXT privateDataSlot,
+								const VkAllocationCallbacks* pAllocator);
+
 
 #pragma mark Operations
 
@@ -678,6 +686,7 @@
 	const VkPhysicalDeviceScalarBlockLayoutFeaturesEXT _enabledScalarLayoutFeatures;
 	const VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT _enabledTexelBuffAlignFeatures;
 	const VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT _enabledVtxAttrDivFeatures;
+	const VkPhysicalDevicePrivateDataFeaturesEXT _enabledPrivateDataFeatures;
 	const VkPhysicalDevicePortabilitySubsetFeaturesKHR _enabledPortabilityFeatures;
 
 	/** The list of Vulkan extensions, indicating whether each has been enabled by the app for this device. */
@@ -724,6 +733,7 @@
     void initPerformanceTracking();
 	void initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo);
 	void initQueues(const VkDeviceCreateInfo* pCreateInfo);
+	void reservePrivateData(const VkDeviceCreateInfo* pCreateInfo);
 	void initMTLCompileOptions();
 	void enableFeatures(const VkDeviceCreateInfo* pCreateInfo);
 	void enableFeatures(const VkBool32* pEnable, const VkBool32* pRequested, const VkBool32* pAvailable, uint32_t count);
@@ -737,6 +747,8 @@
 	MTLCompileOptions* _mtlCompileOptions;
 	MVKSmallVector<MVKSmallVector<MVKQueue*, kMVKQueueCountPerQueueFamily>, kMVKQueueFamilyCount> _queuesByQueueFamilyIndex;
 	MVKSmallVector<MVKResource*, 256> _resources;
+	MVKSmallVector<MVKPrivateDataSlot*> _privateDataSlots;
+	MVKSmallVector<bool> _privateDataSlotsAvailability;
 	std::mutex _rezLock;
     std::mutex _perfLock;
     id<MTLBuffer> _globalVisibilityResultMTLBuffer;
@@ -820,6 +832,35 @@
 
 
 #pragma mark -
+#pragma mark MVKPrivateDataSlot
+
+/** Private data slot. */
+class MVKPrivateDataSlot : public MVKVulkanAPIDeviceObject {
+
+public:
+
+	/** Returns the Vulkan type of this object. */
+	VkObjectType getVkObjectType() override { return VK_OBJECT_TYPE_PRIVATE_DATA_SLOT_EXT; }
+
+	/** Returns the debug report object type of this object. */
+	VkDebugReportObjectTypeEXT getVkDebugReportObjectType() override { return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT; }
+
+	void setData(VkObjectType objectType, uint64_t objectHandle, uint64_t data) { _privateData[objectHandle] = data; }
+
+	uint64_t getData(VkObjectType objectType, uint64_t objectHandle) { return _privateData[objectHandle]; }
+
+	void clearData() { _privateData.clear(); }
+
+	MVKPrivateDataSlot(MVKDevice* device) : MVKVulkanAPIDeviceObject(device) {}
+
+protected:
+	void propagateDebugName() override {}
+
+	std::unordered_map<uint64_t, uint64_t> _privateData;
+};
+
+
+#pragma mark -
 #pragma mark MVKDeviceObjectPool
 
 /** Manages a pool of instances of a particular object type that requires an MVKDevice during construction. */
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index ba3d9cb..8f9bf48 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -169,6 +169,11 @@
 				divisorFeatures->vertexAttributeInstanceRateZeroDivisor = true;
 				break;
 			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT: {
+				auto* privateDataFeatures = (VkPhysicalDevicePrivateDataFeaturesEXT*)next;
+				privateDataFeatures->privateData = true;
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR: {
 				auto* portabilityFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesKHR*)next;
 				portabilityFeatures->constantAlphaColorBlendFactors = true;
@@ -2886,6 +2891,47 @@
 	if (mvkDevMem) { mvkDevMem->destroy(); }
 }
 
+// Look for an available pre-reserved private data slot and return it's address if found.
+// Otherwise create a new instance and return it.
+VkResult MVKDevice::createPrivateDataSlot(const VkPrivateDataSlotCreateInfoEXT* pCreateInfo,
+										  const VkAllocationCallbacks* pAllocator,
+										  VkPrivateDataSlotEXT* pPrivateDataSlot) {
+	MVKPrivateDataSlot* mvkPDS = nullptr;
+
+	size_t slotCnt = _privateDataSlots.size();
+	for (size_t slotIdx = 0; slotIdx < slotCnt; slotIdx++) {
+		if ( _privateDataSlotsAvailability[slotIdx] ) {
+			_privateDataSlotsAvailability[slotIdx] = false;
+			mvkPDS = _privateDataSlots[slotIdx];
+			break;
+		}
+	}
+
+	if ( !mvkPDS ) { mvkPDS = new MVKPrivateDataSlot(this); }
+
+	*pPrivateDataSlot = (VkPrivateDataSlotEXT)mvkPDS;
+	return VK_SUCCESS;
+}
+
+// If the private data slot is one of the pre-reserved slots, clear it and mark it as available.
+// Otherwise destroy it.
+void MVKDevice::destroyPrivateDataSlot(VkPrivateDataSlotEXT privateDataSlot,
+									   const VkAllocationCallbacks* pAllocator) {
+
+	MVKPrivateDataSlot* mvkPDS = (MVKPrivateDataSlot*)privateDataSlot;
+
+	size_t slotCnt = _privateDataSlots.size();
+	for (size_t slotIdx = 0; slotIdx < slotCnt; slotIdx++) {
+		if (mvkPDS == _privateDataSlots[slotIdx]) {
+			mvkPDS->clearData();
+			_privateDataSlotsAvailability[slotIdx] = true;
+			return;
+		}
+	}
+
+	mvkPDS->destroy();
+}
+
 
 #pragma mark Operations
 
@@ -3077,6 +3123,7 @@
 	_enabledScalarLayoutFeatures(),
 	_enabledTexelBuffAlignFeatures(),
 	_enabledVtxAttrDivFeatures(),
+	_enabledPrivateDataFeatures(),
 	_enabledPortabilityFeatures(),
 	_enabledExtensions(this)
 {
@@ -3085,6 +3132,8 @@
 	initPhysicalDevice(physicalDevice, pCreateInfo);
 	enableFeatures(pCreateInfo);
 	enableExtensions(pCreateInfo);
+	initQueues(pCreateInfo);
+	reservePrivateData(pCreateInfo);
 
     _globalVisibilityResultMTLBuffer = nil;
     _globalVisibilityQueryCount = 0;
@@ -3093,8 +3142,6 @@
 
 	_commandResourceFactory = new MVKCommandResourceFactory(this);
 
-	initQueues(pCreateInfo);
-
 	if (getInstance()->_autoGPUCaptureScope == MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE) {
 		MTLCaptureManager *captureMgr = [MTLCaptureManager sharedCaptureManager];
 		if (!getInstance()->_autoGPUCaptureOutputFile.empty()) {
@@ -3225,6 +3272,7 @@
 	mvkClear(&_enabledScalarLayoutFeatures);
 	mvkClear(&_enabledTexelBuffAlignFeatures);
 	mvkClear(&_enabledVtxAttrDivFeatures);
+	mvkClear(&_enabledPrivateDataFeatures);
 	mvkClear(&_enabledPortabilityFeatures);
 
 	// Fetch the available physical device features.
@@ -3232,9 +3280,13 @@
 	pdPortabilityFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR;
 	pdPortabilityFeatures.pNext = NULL;
 
+	VkPhysicalDevicePrivateDataFeaturesEXT pdPrivateDataFeatures;
+	pdPrivateDataFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT;
+	pdPrivateDataFeatures.pNext = &pdPortabilityFeatures;
+
 	VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT pdVtxAttrDivFeatures;
 	pdVtxAttrDivFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
-	pdVtxAttrDivFeatures.pNext = &pdPortabilityFeatures;
+	pdVtxAttrDivFeatures.pNext = &pdPrivateDataFeatures;
 
 	VkPhysicalDeviceTexelBufferAlignmentFeaturesEXT pdTexelBuffAlignFeatures;
 	pdTexelBuffAlignFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TEXEL_BUFFER_ALIGNMENT_FEATURES_EXT;
@@ -3375,6 +3427,13 @@
 							   &pdVtxAttrDivFeatures.vertexAttributeInstanceRateDivisor, 2);
 				break;
 			}
+			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PRIVATE_DATA_FEATURES_EXT: {
+				auto* requestedFeatures = (VkPhysicalDevicePrivateDataFeaturesEXT*)next;
+				enableFeatures(&_enabledPrivateDataFeatures.privateData,
+							   &requestedFeatures->privateData,
+							   &pdPrivateDataFeatures.privateData, 1);
+				break;
+			}
 			case VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR: {
 				auto* requestedFeatures = (VkPhysicalDevicePortabilitySubsetFeaturesKHR*)next;
 				enableFeatures(&_enabledPortabilityFeatures.constantAlphaColorBlendFactors,
@@ -3428,6 +3487,28 @@
 	}
 }
 
+void MVKDevice::reservePrivateData(const VkDeviceCreateInfo* pCreateInfo) {
+	size_t slotCnt = 0;
+	for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+			case VK_STRUCTURE_TYPE_DEVICE_PRIVATE_DATA_CREATE_INFO_EXT: {
+				auto* pPDCreateInfo = (const VkDevicePrivateDataCreateInfoEXT*)next;
+				slotCnt += pPDCreateInfo->privateDataSlotRequestCount;
+				break;
+			}
+			default:
+				break;
+		}
+	}
+
+	_privateDataSlots.reserve(slotCnt);
+	_privateDataSlotsAvailability.reserve(slotCnt);
+	for (uint32_t slotIdx = 0; slotIdx < slotCnt; slotIdx++) {
+		_privateDataSlots.push_back(new MVKPrivateDataSlot(this));
+		_privateDataSlotsAvailability.push_back(true);
+	}
+}
+
 void MVKDevice::initMTLCompileOptions() {
 	_mtlCompileOptions = [MTLCompileOptions new];	// retained
 	_mtlCompileOptions.languageVersion = _pMetalFeatures->mslVersionEnum;
@@ -3445,6 +3526,8 @@
 	if (getInstance()->_autoGPUCaptureScope == MVK_CONFIG_AUTO_GPU_CAPTURE_SCOPE_DEVICE) {
 		[[MTLCaptureManager sharedCaptureManager] stopCapture];
 	}
+
+	mvkDestroyContainerContents(_privateDataSlots);
 }
 
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index e8d42d6..9fa432c 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -669,6 +669,10 @@
 	ADD_DVC_EXT_ENTRY_POINT(vkCmdDebugMarkerInsertEXT, EXT_DEBUG_MARKER);
 	ADD_DVC_EXT_ENTRY_POINT(vkGetRefreshCycleDurationGOOGLE, GOOGLE_DISPLAY_TIMING);
 	ADD_DVC_EXT_ENTRY_POINT(vkGetPastPresentationTimingGOOGLE, GOOGLE_DISPLAY_TIMING);
+	ADD_DVC_EXT_ENTRY_POINT(vkCreatePrivateDataSlotEXT, EXT_PRIVATE_DATA);
+	ADD_DVC_EXT_ENTRY_POINT(vkDestroyPrivateDataSlotEXT, EXT_PRIVATE_DATA);
+	ADD_DVC_EXT_ENTRY_POINT(vkGetPrivateDataEXT, EXT_PRIVATE_DATA);
+	ADD_DVC_EXT_ENTRY_POINT(vkSetPrivateDataEXT, EXT_PRIVATE_DATA);
 }
 
 void MVKInstance::logVersions() {
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index c770885..2f81276 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -88,6 +88,7 @@
 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_private_data, EXT_PRIVATE_DATA, DEVICE)
 MVK_EXTENSION(EXT_robustness2, EXT_ROBUSTNESS_2, DEVICE)
 MVK_EXTENSION(EXT_scalar_block_layout, EXT_SCALAR_BLOCK_LAYOUT, DEVICE)
 MVK_EXTENSION(EXT_shader_stencil_export, EXT_SHADER_STENCIL_EXPORT, DEVICE)
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 1fecb88..16b3a11 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -2934,6 +2934,7 @@
 	VkDevice                                    device,
 	VkSwapchainKHR                              swapchain,
 	VkRefreshCycleDurationGOOGLE*               pDisplayTimingProperties) {
+
 	MVKTraceVulkanCallStart();
 	MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain;
 	VkResult rslt = mvkSwapchain->getRefreshCycleDuration(pDisplayTimingProperties);
@@ -2946,6 +2947,7 @@
 	VkSwapchainKHR                              swapchain,
 	uint32_t*                                   pPresentationTimingCount,
 	VkPastPresentationTimingGOOGLE*             pPresentationTimings) {
+
 	MVKTraceVulkanCallStart();
 	MVKSwapchain* mvkSwapchain = (MVKSwapchain*)swapchain;
 	VkResult rslt = mvkSwapchain->getPastPresentationTiming(pPresentationTimingCount, pPresentationTimings);
@@ -2954,6 +2956,60 @@
 }
 
 #pragma mark -
+#pragma mark VK_EXT_private_data extension
+
+MVK_PUBLIC_SYMBOL VkResult vkCreatePrivateDataSlotEXT(
+	VkDevice                                    device,
+	const VkPrivateDataSlotCreateInfoEXT*       pCreateInfo,
+	const VkAllocationCallbacks*                pAllocator,
+	VkPrivateDataSlotEXT*                       pPrivateDataSlot) {
+
+	MVKTraceVulkanCallStart();
+	MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
+	VkResult rslt = mvkDev->createPrivateDataSlot(pCreateInfo, pAllocator, pPrivateDataSlot);
+	MVKTraceVulkanCallEnd();
+	return rslt;
+}
+
+MVK_PUBLIC_SYMBOL void vkDestroyPrivateDataSlotEXT(
+	VkDevice                                    device,
+	VkPrivateDataSlotEXT                        privateDataSlot,
+	const VkAllocationCallbacks*                pAllocator) {
+
+	MVKTraceVulkanCallStart();
+	MVKDevice* mvkDev = MVKDevice::getMVKDevice(device);
+	mvkDev->destroyPrivateDataSlot(privateDataSlot, pAllocator);
+	MVKTraceVulkanCallEnd();
+}
+
+MVK_PUBLIC_SYMBOL VkResult vkSetPrivateDataEXT(
+	VkDevice                                    device,
+	VkObjectType                                objectType,
+	uint64_t                                    objectHandle,
+	VkPrivateDataSlotEXT                        privateDataSlot,
+	uint64_t                                    data) {
+
+	MVKTraceVulkanCallStart();
+	MVKPrivateDataSlot* mvkPDS = (MVKPrivateDataSlot*)privateDataSlot;
+	mvkPDS->setData(objectType, objectHandle, data);
+	MVKTraceVulkanCallEnd();
+	return VK_SUCCESS;
+}
+
+MVK_PUBLIC_SYMBOL void vkGetPrivateDataEXT(
+	VkDevice                                    device,
+	VkObjectType                                objectType,
+	uint64_t                                    objectHandle,
+	VkPrivateDataSlotEXT                        privateDataSlot,
+	uint64_t*                                   pData) {
+
+	MVKTraceVulkanCallStart();
+	MVKPrivateDataSlot* mvkPDS = (MVKPrivateDataSlot*)privateDataSlot;
+	*pData = mvkPDS->getData(objectType, objectHandle);
+	MVKTraceVulkanCallEnd();
+}
+
+#pragma mark -
 #pragma mark iOS & macOS surface extensions
 
 MVK_PUBLIC_SYMBOL VkResult vkCreate_PLATFORM_SurfaceMVK(