vulkan: Add common physical device management
authorKonstantin Seurer <konstantin.seurer@gmail.com>
Tue, 26 Jul 2022 14:46:49 +0000 (16:46 +0200)
committerMarge Bot <emma+marge@anholt.net>
Tue, 30 Aug 2022 19:34:47 +0000 (19:34 +0000)
Add common entrypoints for enumerating physical devices, based on the RADV implementation.

Signed-off-by: Konstantin Seurer <konstantin.seurer@gmail.com>
Reviewed-by: Jason Ekstrand <jason.ekstrand@collabora.com>
Reviewed-by: Samuel Pitoiset <samuel.pitoiset@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/17750>

src/vulkan/runtime/vk_instance.c
src/vulkan/runtime/vk_instance.h
src/vulkan/runtime/vk_physical_device.h

index 66b896f..21a0067 100644 (file)
 
 #include "vk_instance.h"
 
+#ifdef HAVE_LIBDRM
+#include <xf86drm.h>
+#endif
+
 #include "vk_alloc.h"
 #include "vk_common_entrypoints.h"
 #include "vk_dispatch_trampolines.h"
 #include "vk_log.h"
 #include "vk_util.h"
 #include "vk_debug_utils.h"
+#include "vk_physical_device.h"
 
 #include "compiler/glsl_types.h"
 
@@ -176,14 +181,34 @@ vk_instance_init(struct vk_instance *instance,
 
    list_inithead(&instance->debug_utils.callbacks);
 
+   list_inithead(&instance->physical_devices.list);
+
+   if (mtx_init(&instance->physical_devices.mutex, mtx_plain) != 0) {
+      mtx_destroy(&instance->debug_report.callbacks_mutex);
+      mtx_destroy(&instance->debug_utils.callbacks_mutex);
+      return vk_error(instance, VK_ERROR_INITIALIZATION_FAILED);
+   }
+
    glsl_type_singleton_init_or_ref();
 
    return VK_SUCCESS;
 }
 
+static void
+destroy_physical_devices(struct vk_instance *instance)
+{
+   list_for_each_entry_safe(struct vk_physical_device, pdevice,
+                            &instance->physical_devices.list, link) {
+      list_del(&pdevice->link);
+      instance->physical_devices.destroy(pdevice);
+   }
+}
+
 void
 vk_instance_finish(struct vk_instance *instance)
 {
+   destroy_physical_devices(instance);
+
    glsl_type_singleton_decref();
    if (unlikely(!list_is_empty(&instance->debug_utils.callbacks))) {
       list_for_each_entry_safe(struct vk_debug_utils_messenger, messenger,
@@ -204,6 +229,7 @@ vk_instance_finish(struct vk_instance *instance)
    }
    mtx_destroy(&instance->debug_report.callbacks_mutex);
    mtx_destroy(&instance->debug_utils.callbacks_mutex);
+   mtx_destroy(&instance->physical_devices.mutex);
    vk_free(&instance->alloc, (char *)instance->app_info.app_name);
    vk_free(&instance->alloc, (char *)instance->app_info.engine_name);
    vk_object_base_finish(&instance->base);
@@ -329,3 +355,119 @@ vk_instance_get_physical_device_proc_addr(const struct vk_instance *instance,
                                                              instance->app_info.api_version,
                                                              &instance->enabled_extensions);
 }
+
+static VkResult
+enumerate_drm_physical_devices_locked(struct vk_instance *instance)
+{
+#ifdef HAVE_LIBDRM
+   /* TODO: Check for more devices ? */
+   drmDevicePtr devices[8];
+   int max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
+
+   if (max_devices < 1)
+      return VK_SUCCESS;
+
+   VkResult result;
+   for (uint32_t i = 0; i < (uint32_t)max_devices; i++) {
+      struct vk_physical_device *pdevice;
+      result = instance->physical_devices.try_create_for_drm(instance, devices[i], &pdevice);
+
+      /* Incompatible DRM device, skip. */
+      if (result == VK_ERROR_INCOMPATIBLE_DRIVER) {
+         result = VK_SUCCESS;
+         continue;
+      }
+
+      /* Error creating the physical device, report the error. */
+      if (result != VK_SUCCESS)
+         break;
+
+      list_addtail(&pdevice->link, &instance->physical_devices.list);
+   }
+
+   drmFreeDevices(devices, max_devices);
+   return result;
+#endif
+   return VK_SUCCESS;
+}
+
+static VkResult
+enumerate_physical_devices_locked(struct vk_instance *instance)
+{
+   if (instance->physical_devices.enumerate)
+      return instance->physical_devices.enumerate(instance);
+
+   VkResult result = VK_SUCCESS;
+
+   if (instance->physical_devices.try_create_for_drm) {
+      result = enumerate_drm_physical_devices_locked(instance);
+      if (result != VK_SUCCESS) {
+         destroy_physical_devices(instance);
+         return result;
+      }
+   }
+
+   return result;
+}
+
+static VkResult
+enumerate_physical_devices(struct vk_instance *instance)
+{
+   VkResult result = VK_SUCCESS;
+
+   mtx_lock(&instance->physical_devices.mutex);
+   if (!instance->physical_devices.enumerated) {
+      result = enumerate_physical_devices_locked(instance);
+      if (result == VK_SUCCESS)
+         instance->physical_devices.enumerated = true;
+   }
+   mtx_unlock(&instance->physical_devices.mutex);
+
+   return result;
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+vk_common_EnumeratePhysicalDevices(VkInstance _instance, uint32_t *pPhysicalDeviceCount,
+                                   VkPhysicalDevice *pPhysicalDevices)
+{
+   VK_FROM_HANDLE(vk_instance, instance, _instance);
+   VK_OUTARRAY_MAKE_TYPED(VkPhysicalDevice, out, pPhysicalDevices, pPhysicalDeviceCount);
+
+   VkResult result = enumerate_physical_devices(instance);
+   if (result != VK_SUCCESS)
+      return result;
+
+   list_for_each_entry(struct vk_physical_device, pdevice,
+                       &instance->physical_devices.list, link) {
+      vk_outarray_append_typed(VkPhysicalDevice, &out, element) {
+         *element = vk_physical_device_to_handle(pdevice);
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
+
+VKAPI_ATTR VkResult VKAPI_CALL
+vk_common_EnumeratePhysicalDeviceGroups(VkInstance _instance, uint32_t *pGroupCount,
+                                        VkPhysicalDeviceGroupProperties *pGroupProperties)
+{
+   VK_FROM_HANDLE(vk_instance, instance, _instance);
+   VK_OUTARRAY_MAKE_TYPED(VkPhysicalDeviceGroupProperties, out, pGroupProperties,
+                          pGroupCount);
+
+   VkResult result = enumerate_physical_devices(instance);
+   if (result != VK_SUCCESS)
+      return result;
+
+   list_for_each_entry(struct vk_physical_device, pdevice,
+                       &instance->physical_devices.list, link) {
+      vk_outarray_append_typed(VkPhysicalDeviceGroupProperties, &out, p) {
+         p->physicalDeviceCount = 1;
+         memset(p->physicalDevices, 0, sizeof(p->physicalDevices));
+         p->physicalDevices[0] = vk_physical_device_to_handle(pdevice);
+         p->subsetAllocation = false;
+      }
+   }
+
+   return vk_outarray_status(&out);
+}
index 3609afc..af5b54b 100644 (file)
@@ -55,6 +55,9 @@ struct vk_app_info {
    uint32_t           api_version;
 };
 
+struct _drmDevice;
+struct vk_physical_device;
+
 /** Base struct for all `VkInstance` implementations
  *
  * This contains data structures necessary for detecting enabled extensions,
@@ -101,6 +104,46 @@ struct vk_instance {
       /* Persistent callbacks */
       struct list_head callbacks;
    } debug_utils;
+
+   /** List of all physical devices and callbacks
+   *
+   * This is used for automatic physical device creation,
+   * deletion and enumeration.
+   */
+   struct {
+      struct list_head list;
+      bool enumerated;
+
+      /** Enumerate physical devices for this instance
+       *
+       * The driver can implement this callback for custom physical device
+       * enumeration. The callback should always return VK_SUCCESS unless
+       * an allocation failed.
+       * 
+       * If this callback is not set, try_create_for_drm will be used for
+       * enumeration.
+       */
+      VkResult (*enumerate)(struct vk_instance *instance);
+
+      /** Try to create a physical device for a drm device
+       * 
+       * For incompatible devices this callback should return
+       * VK_ERROR_INCOMPATIBLE_DRIVER.
+       */
+      VkResult (*try_create_for_drm)(struct vk_instance *instance,
+                                     struct _drmDevice *device,
+                                     struct vk_physical_device **out);
+
+      /** Handle the destruction of a physical device
+       * 
+       * This callback has to be implemented when using common physical device
+       * management. The device pointer and any resource allocated for the
+       * device should be freed here.
+       */
+      void (*destroy)(struct vk_physical_device *pdevice);
+
+      mtx_t mutex;
+   } physical_devices;
 };
 
 VK_DEFINE_HANDLE_CASTS(vk_instance, base, VkInstance,
index eac023d..b047df1 100644 (file)
@@ -27,6 +27,8 @@
 #include "vk_extensions.h"
 #include "vk_object.h"
 
+#include "util/list.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -41,6 +43,9 @@ struct vk_pipeline_cache_object_ops;
 struct vk_physical_device {
    struct vk_object_base base;
 
+   /* See vk_instance::pdevices::list */
+   struct list_head link;
+
    /** Instance which is the parent of this physical device */
    struct vk_instance *instance;