Merge pull request #680 from cdavis5e/device-group-creation

Add a minimal implementation of VK_KHR_device_group_creation.
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
index 1a1282e..e087ea1 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.h
@@ -663,7 +663,7 @@
 	MVKResource* addResource(MVKResource* rez);
 	MVKResource* removeResource(MVKResource* rez);
     void initPerformanceTracking();
-	void initPhysicalDevice(MVKPhysicalDevice* physicalDevice);
+	void initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo);
 	void initQueues(const VkDeviceCreateInfo* pCreateInfo);
 	void initMTLCompileOptions();
 	void enableFeatures(const VkDeviceCreateInfo* pCreateInfo);
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
index e22abc0..c1b6a68 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKDevice.mm
@@ -2232,7 +2232,7 @@
 {
 
 	initPerformanceTracking();
-	initPhysicalDevice(physicalDevice);
+	initPhysicalDevice(physicalDevice, pCreateInfo);
 	enableFeatures(pCreateInfo);
 	enableExtensions(pCreateInfo);
 
@@ -2273,9 +2273,27 @@
 	_performanceStatistics.queue.mtlCommandBufferCompletion = initPerf;
 }
 
-void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice) {
+void MVKDevice::initPhysicalDevice(MVKPhysicalDevice* physicalDevice, const VkDeviceCreateInfo* pCreateInfo) {
 
-	_physicalDevice = physicalDevice;
+	const VkDeviceGroupDeviceCreateInfo* pGroupCreateInfo = nullptr;
+	for (const auto* next = (const VkBaseInStructure*)pCreateInfo->pNext; next; next = next->pNext) {
+		switch (next->sType) {
+		case VK_STRUCTURE_TYPE_DEVICE_GROUP_DEVICE_CREATE_INFO:
+			pGroupCreateInfo = (const VkDeviceGroupDeviceCreateInfo*)next;
+			break;
+		default:
+			break;
+		}
+	}
+
+	// If I was given physical devices for a grouped device, use them.
+	// At this time, we only support device groups consisting of a single member,
+	// so this is sufficient for now.
+	if (pGroupCreateInfo && pGroupCreateInfo->physicalDeviceCount)
+		_physicalDevice = MVKPhysicalDevice::getMVKPhysicalDevice(pGroupCreateInfo->pPhysicalDevices[0]);
+	else
+		_physicalDevice = physicalDevice;
+
 	_pMVKConfig = _physicalDevice->_mvkInstance->getMoltenVKConfiguration();
 	_pMetalFeatures = _physicalDevice->getMetalFeatures();
 	_pProperties = &_physicalDevice->_properties;
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
index 6784567..5771fe6 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.h
@@ -85,6 +85,20 @@
 	 */
 	VkResult getPhysicalDevices(uint32_t* pCount, VkPhysicalDevice* pPhysicalDevices);
 
+	/**
+	 * If pPhysicalDeviceGroups is null, the value of pCount is updated with the number of 
+	 * physical device groups supported by this instance.
+	 *
+	 * If pPhysicalDeviceGroups is not null, then pCount physical device groups are copied into the array.
+	 * If the number of available physical device groups is less than pCount, the value of pCount is 
+	 * updated to indicate the number of physical device groups actually returned in the array.
+	 *
+	 * Returns VK_SUCCESS if successful. Returns VK_INCOMPLETE if the number of physical
+	 * device groups available in this instance is larger than the specified pCount. Returns other 
+	 * values if an error occurs.
+	 */
+	VkResult getPhysicalDeviceGroups(uint32_t* pCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProps);
+
 	/** Returns the driver layer. */
 	MVKLayer* getDriverLayer() { return MVKLayerManager::globalManager()->getDriverLayer(); }
 
diff --git a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
index 88c41af..ce9fa40 100644
--- a/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
+++ b/MoltenVK/MoltenVK/GPUObjects/MVKInstance.mm
@@ -70,6 +70,36 @@
 	return result;
 }
 
+VkResult MVKInstance::getPhysicalDeviceGroups(uint32_t* pCount, VkPhysicalDeviceGroupProperties* pPhysicalDeviceGroupProps) {
+
+	// According to the Vulkan spec:
+	//  "Every physical device *must* be in exactly one device group."
+	// Since we don't really support this yet, we must return one group for every
+	// device.
+
+	// Get the number of physical devices
+	uint32_t pdCnt = (uint32_t)_physicalDevices.size();
+
+	// If properties aren't actually being requested yet, simply update the returned count
+	if ( !pPhysicalDeviceGroupProps ) {
+		*pCount = pdCnt;
+		return VK_SUCCESS;
+	}
+
+	// Othewise, determine how many physical device groups we'll return, and return that count
+	VkResult result = (*pCount >= pdCnt) ? VK_SUCCESS : VK_INCOMPLETE;
+	*pCount = min(pdCnt, *pCount);
+
+	// Now populate the device groups
+	for (uint32_t pdIdx = 0; pdIdx < *pCount; pdIdx++) {
+		pPhysicalDeviceGroupProps[pdIdx].physicalDeviceCount = 1;
+		pPhysicalDeviceGroupProps[pdIdx].physicalDevices[0] = _physicalDevices[pdIdx]->getVkPhysicalDevice();
+		pPhysicalDeviceGroupProps[pdIdx].subsetAllocation = VK_FALSE;
+	}
+
+	return result;
+}
+
 MVKSurface* MVKInstance::createSurface(const VkMetalSurfaceCreateInfoEXT* pCreateInfo,
 									   const VkAllocationCallbacks* pAllocator) {
 	return new MVKSurface(this, pCreateInfo, pAllocator);
@@ -523,6 +553,7 @@
 	ADD_DVC_ENTRY_POINT(vkCmdExecuteCommands);
 
 	// Instance extension functions:
+	ADD_INST_EXT_ENTRY_POINT(vkEnumeratePhysicalDeviceGroupsKHR, KHR_DEVICE_GROUP_CREATION);
 	ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceFeatures2KHR, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2);
 	ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceProperties2KHR, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2);
 	ADD_INST_EXT_ENTRY_POINT(vkGetPhysicalDeviceFormatProperties2KHR, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2);
diff --git a/MoltenVK/MoltenVK/Layers/MVKExtensions.def b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
index 0a518a3..43e244b 100644
--- a/MoltenVK/MoltenVK/Layers/MVKExtensions.def
+++ b/MoltenVK/MoltenVK/Layers/MVKExtensions.def
@@ -35,6 +35,7 @@
 MVK_EXTENSION(KHR_bind_memory2, KHR_BIND_MEMORY_2)
 MVK_EXTENSION(KHR_dedicated_allocation, KHR_DEDICATED_ALLOCATION)
 MVK_EXTENSION(KHR_descriptor_update_template, KHR_DESCRIPTOR_UPDATE_TEMPLATE)
+MVK_EXTENSION(KHR_device_group_creation, KHR_DEVICE_GROUP_CREATION)
 MVK_EXTENSION(KHR_get_memory_requirements2, KHR_GET_MEMORY_REQUIREMENTS_2)
 MVK_EXTENSION(KHR_get_physical_device_properties2, KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2)
 MVK_EXTENSION(KHR_get_surface_capabilities2, KHR_GET_SURFACE_CAPABILITIES_2)
diff --git a/MoltenVK/MoltenVK/Vulkan/vulkan.mm b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
index 8a287a5..3522f87 100644
--- a/MoltenVK/MoltenVK/Vulkan/vulkan.mm
+++ b/MoltenVK/MoltenVK/Vulkan/vulkan.mm
@@ -1964,6 +1964,21 @@
 
 
 #pragma mark -
+#pragma mark VK_KHR_device_group_creation extension
+
+MVK_PUBLIC_SYMBOL VkResult vkEnumeratePhysicalDeviceGroupsKHR(
+    VkInstance                                  instance,
+    uint32_t*                                   pPhysicalDeviceGroupCount,
+    VkPhysicalDeviceGroupPropertiesKHR*         pPhysicalDeviceGroupProperties) {
+    MVKTraceVulkanCallStart();
+    MVKInstance* mvkInst = MVKInstance::getMVKInstance(instance);
+    VkResult rslt = mvkInst->getPhysicalDeviceGroups(pPhysicalDeviceGroupCount, pPhysicalDeviceGroupProperties);
+    MVKTraceVulkanCallEnd();
+    return rslt;
+}
+
+
+#pragma mark -
 #pragma mark VK_KHR_get_memory_requirements2 extension
 
 MVK_PUBLIC_SYMBOL void vkGetBufferMemoryRequirements2KHR(
@@ -2377,22 +2392,6 @@
 
 
 #pragma mark -
-#pragma mark VK_EXT_host_query_reset extension
-
-MVK_PUBLIC_SYMBOL void vkResetQueryPoolEXT(
-    VkDevice                                    device,
-    VkQueryPool                                 queryPool,
-    uint32_t                                    firstQuery,
-    uint32_t                                    queryCount) {
-
-	MVKTraceVulkanCallStart();
-    auto* mvkQueryPool = (MVKQueryPool*)queryPool;
-    mvkQueryPool->resetResults(firstQuery, queryCount, nullptr);
-	MVKTraceVulkanCallEnd();
-}
-
-
-#pragma mark -
 #pragma mark VK_EXT_debug_report extension
 
 MVK_PUBLIC_SYMBOL VkResult vkCreateDebugReportCallbackEXT(
@@ -2608,6 +2607,23 @@
 	MVKTraceVulkanCallEnd();
 }
 
+
+#pragma mark -
+#pragma mark VK_EXT_host_query_reset extension
+
+MVK_PUBLIC_SYMBOL void vkResetQueryPoolEXT(
+    VkDevice                                    device,
+    VkQueryPool                                 queryPool,
+    uint32_t                                    firstQuery,
+    uint32_t                                    queryCount) {
+
+	MVKTraceVulkanCallStart();
+    auto* mvkQueryPool = (MVKQueryPool*)queryPool;
+    mvkQueryPool->resetResults(firstQuery, queryCount, nullptr);
+	MVKTraceVulkanCallEnd();
+}
+
+
 #pragma mark -
 #pragma mark VK_EXT_metal_surface extension