vulkan/device: expose extension/layer choices
authorMatthew Waters <matthew@centricular.com>
Sun, 14 Jun 2020 15:26:08 +0000 (01:26 +1000)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 21 Jun 2020 09:30:29 +0000 (09:30 +0000)
Extensions and layers can be enabled before calling
gst_vulkan_device_open().  The available extensions are stored in
GstVulkanPhysicalDevice.

Defaults are still the same.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1341>

gst-libs/gst/vulkan/gstvkdevice.c
gst-libs/gst/vulkan/gstvkdevice.h
gst-libs/gst/vulkan/gstvkphysicaldevice.c
gst-libs/gst/vulkan/gstvkphysicaldevice.h
gst-libs/gst/vulkan/gstvkswapper.c

index dd6c345..76be644 100644 (file)
@@ -54,6 +54,9 @@ static void gst_vulkan_device_finalize (GObject * object);
 
 struct _GstVulkanDevicePrivate
 {
+  GPtrArray *enabled_layers;
+  GPtrArray *enabled_extensions;
+
   gboolean opened;
   guint queue_family_id;
   guint n_queues;
@@ -163,6 +166,10 @@ gst_vulkan_device_get_property (GObject * object, guint prop_id,
 static void
 gst_vulkan_device_init (GstVulkanDevice * device)
 {
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+
+  priv->enabled_layers = g_ptr_array_new_with_free_func (g_free);
+  priv->enabled_extensions = g_ptr_array_new_with_free_func (g_free);
 }
 
 static void
@@ -172,6 +179,10 @@ gst_vulkan_device_constructed (GObject * object)
 
   g_object_get (device->physical_device, "instance", &device->instance, NULL);
 
+  /* by default allow vkswapper to work for rendering to an output window.
+   * Ignore the failure if the extension does not exist. */
+  gst_vulkan_device_enable_extension (device, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+
   G_OBJECT_CLASS (parent_class)->constructed (object);
 }
 
@@ -219,6 +230,7 @@ static void
 gst_vulkan_device_finalize (GObject * object)
 {
   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
 
   if (device->device) {
     vkDeviceWaitIdle (device->device);
@@ -229,6 +241,12 @@ gst_vulkan_device_finalize (GObject * object)
   gst_clear_object (&device->physical_device);
   gst_clear_object (&device->instance);
 
+  g_ptr_array_unref (priv->enabled_layers);
+  priv->enabled_layers = NULL;
+
+  g_ptr_array_unref (priv->enabled_extensions);
+  priv->enabled_extensions = NULL;
+
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
@@ -247,9 +265,6 @@ gboolean
 gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
 {
   GstVulkanDevicePrivate *priv = GET_PRIV (device);
-  const char *extension_names[64];
-  uint32_t enabled_extension_count = 0;
-  gboolean have_swapchain_ext;
   VkPhysicalDevice gpu;
   VkResult err;
   guint i;
@@ -265,29 +280,6 @@ gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
 
   gpu = gst_vulkan_device_get_physical_device (device);
 
-  have_swapchain_ext = 0;
-  enabled_extension_count = 0;
-  memset (extension_names, 0, sizeof (extension_names));
-
-  for (i = 0; i < device->physical_device->n_device_extensions; i++) {
-    GST_TRACE_OBJECT (device, "checking device extension %s",
-        device->physical_device->device_extensions[i].extensionName);
-    if (!strcmp (VK_KHR_SWAPCHAIN_EXTENSION_NAME,
-            device->physical_device->device_extensions[i].extensionName)) {
-      have_swapchain_ext = TRUE;
-      extension_names[enabled_extension_count++] =
-          (gchar *) VK_KHR_SWAPCHAIN_EXTENSION_NAME;
-    }
-    g_assert (enabled_extension_count < 64);
-  }
-  if (!have_swapchain_ext) {
-    g_set_error_literal (error, GST_VULKAN_ERROR,
-        VK_ERROR_EXTENSION_NOT_PRESENT,
-        "Failed to find required extension, \"" VK_KHR_SWAPCHAIN_EXTENSION_NAME
-        "\"");
-    goto error;
-  }
-
   /* FIXME: allow overriding/selecting */
   for (i = 0; i < device->physical_device->n_queue_families; i++) {
     if (device->physical_device->
@@ -317,10 +309,12 @@ gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
     device_info.pNext = NULL;
     device_info.queueCreateInfoCount = 1;
     device_info.pQueueCreateInfos = &queue_info;
-    device_info.enabledLayerCount = 0;
-    device_info.ppEnabledLayerNames = NULL;
-    device_info.enabledExtensionCount = enabled_extension_count;
-    device_info.ppEnabledExtensionNames = (const char *const *) extension_names;
+    device_info.enabledLayerCount = priv->enabled_layers->len;
+    device_info.ppEnabledLayerNames =
+        (const char *const *) priv->enabled_layers->pdata;
+    device_info.enabledExtensionCount = priv->enabled_extensions->len;
+    device_info.ppEnabledExtensionNames =
+        (const char *const *) priv->enabled_extensions->pdata;
     device_info.pEnabledFeatures = NULL;
 
     err = vkCreateDevice (gpu, &device_info, NULL, &device->device);
@@ -629,3 +623,221 @@ gst_vulkan_device_create_fence (GstVulkanDevice * device, GError ** error)
 
   return gst_vulkan_fence_cache_acquire (priv->fence_cache, error);
 }
+
+/* reimplement a specfic case of g_ptr_array_find_with_equal_func as that
+ * requires Glib 2.54 */
+static gboolean
+ptr_array_find_string (GPtrArray * array, const gchar * str, guint * index)
+{
+  guint i;
+
+  for (i = 0; i < array->len; i++) {
+    gchar *val = (gchar *) g_ptr_array_index (array, i);
+    if (g_strcmp0 (val, str) == 0) {
+      if (index)
+        *index = i;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+static gboolean
+gst_vulkan_device_is_extension_enabled_unlocked (GstVulkanDevice * device,
+    const gchar * name, guint * index)
+{
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+
+  return ptr_array_find_string (priv->enabled_extensions, name, index);
+}
+
+/**
+ * gst_vulkan_device_is_extension_enabled:
+ * @device: a # GstVulkanDevice
+ * @name: extension name
+ *
+ * Returns: whether extension @name is enabled
+ */
+gboolean
+gst_vulkan_device_is_extension_enabled (GstVulkanDevice * device,
+    const gchar * name)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret = gst_vulkan_device_is_extension_enabled_unlocked (device, name, NULL);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
+
+static gboolean
+gst_vulkan_device_enable_extension_unlocked (GstVulkanDevice * device,
+    const gchar * name)
+{
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+
+  if (gst_vulkan_device_is_extension_enabled_unlocked (device, name, NULL))
+    /* extension is already enabled */
+    return TRUE;
+
+  if (!gst_vulkan_physical_device_get_extension_info (device->physical_device,
+          name, NULL))
+    return FALSE;
+
+  g_ptr_array_add (priv->enabled_extensions, g_strdup (name));
+
+  return TRUE;
+}
+
+/**
+ * gst_vulkan_device_enable_extension:
+ * @device: a #GstVulkanDevice
+ * @name: extension name to enable
+ *
+ * Enable an Vulkan extension by @name.  Enabling an extension will
+ * only have an effect before the call to gst_vulkan_device_open().
+ *
+ * Returns: whether the Vulkan extension could be enabled.
+ */
+gboolean
+gst_vulkan_device_enable_extension (GstVulkanDevice * device,
+    const gchar * name)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret = gst_vulkan_device_enable_extension_unlocked (device, name);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
+
+static gboolean
+gst_vulkan_device_disable_extension_unlocked (GstVulkanDevice * device,
+    const gchar * name)
+{
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+  guint i;
+
+  if (!gst_vulkan_physical_device_get_extension_info (device->physical_device,
+          name, NULL))
+    return FALSE;
+
+  if (!gst_vulkan_device_is_extension_enabled_unlocked (device, name, &i))
+    /* extension is already disabled */
+    return TRUE;
+
+  g_ptr_array_remove_index_fast (priv->enabled_extensions, i);
+
+  return TRUE;
+}
+
+/**
+ * gst_vulkan_device_disable_extension:
+ * @device: a #GstVulkanDevice
+ * @name: extension name to enable
+ *
+ * Disable an Vulkan extension by @name.  Disabling an extension will only have
+ * an effect before the call to gst_vulkan_device_open().
+ *
+ * Returns: whether the Vulkan extension could be disabled.
+ */
+gboolean
+gst_vulkan_device_disable_extension (GstVulkanDevice * device,
+    const gchar * name)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret = gst_vulkan_device_disable_extension_unlocked (device, name);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
+
+static gboolean
+gst_vulkan_device_is_layer_enabled_unlocked (GstVulkanDevice * device,
+    const gchar * name)
+{
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+
+  return ptr_array_find_string (priv->enabled_layers, name, NULL);
+}
+
+/**
+ * gst_vulkan_device_is_layer_enabled:
+ * @device: a # GstVulkanDevice
+ * @name: layer name
+ *
+ * Returns: whether layer @name is enabled
+ */
+gboolean
+gst_vulkan_device_is_layer_enabled (GstVulkanDevice * device,
+    const gchar * name)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret = gst_vulkan_device_is_layer_enabled_unlocked (device, name);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
+
+static gboolean
+gst_vulkan_device_enable_layer_unlocked (GstVulkanDevice * device,
+    const gchar * name)
+{
+  GstVulkanDevicePrivate *priv = GET_PRIV (device);
+
+  if (gst_vulkan_device_is_layer_enabled_unlocked (device, name))
+    /* layer is already enabled */
+    return TRUE;
+
+  if (!gst_vulkan_physical_device_get_layer_info (device->physical_device,
+          name, NULL, NULL, NULL))
+    return FALSE;
+
+  g_ptr_array_add (priv->enabled_layers, g_strdup (name));
+
+  return TRUE;
+}
+
+/**
+ * gst_vulkan_device_enable_layer:
+ * @device: a #GstVulkanDevice
+ * @name: layer name to enable
+ *
+ * Enable an Vulkan layer by @name.  Enabling a layer will
+ * only have an effect before the call to gst_vulkan_device_open().
+ *
+ * Returns: whether the Vulkan layer could be enabled.
+ */
+gboolean
+gst_vulkan_device_enable_layer (GstVulkanDevice * device, const gchar * name)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret = gst_vulkan_device_enable_layer_unlocked (device, name);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
index 1614f98..5a69fb8 100644 (file)
@@ -64,6 +64,22 @@ gboolean            gst_vulkan_device_open                  (GstVulkanDevice * d
                                                              GError ** error);
 
 GST_VULKAN_API
+gboolean            gst_vulkan_device_enable_extension      (GstVulkanDevice * device,
+                                                             const gchar * name);
+GST_VULKAN_API
+gboolean            gst_vulkan_device_disable_extension     (GstVulkanDevice * device,
+                                                             const gchar * name);
+GST_VULKAN_API
+gboolean            gst_vulkan_device_is_extension_enabled  (GstVulkanDevice * device,
+                                                             const gchar * name);
+GST_VULKAN_API
+gboolean            gst_vulkan_device_enable_layer          (GstVulkanDevice * device,
+                                                             const gchar * name);
+GST_VULKAN_API
+gboolean            gst_vulkan_device_is_layer_enabled      (GstVulkanDevice * device,
+                                                             const gchar * name);
+
+GST_VULKAN_API
 gpointer            gst_vulkan_device_get_proc_address      (GstVulkanDevice * device,
                                                              const gchar * name);
 GST_VULKAN_API
index 2bd434e..f55e0f4 100644 (file)
@@ -52,7 +52,12 @@ static void gst_vulkan_physical_device_finalize (GObject * object);
 
 struct _GstVulkanPhysicalDevicePrivate
 {
-  guint dummy;
+  guint32 n_available_layers;
+  VkLayerProperties *available_layers;
+
+  guint32 n_available_extensions;
+  VkExtensionProperties *available_extensions;
+
 #if defined (VK_API_VERSION_1_2)
   VkPhysicalDeviceFeatures2 features10;
   VkPhysicalDeviceProperties2 properties10;
@@ -162,6 +167,9 @@ gst_vulkan_physical_device_init (GstVulkanPhysicalDevice * device)
 {
   GstVulkanPhysicalDevicePrivate *priv = GET_PRIV (device);
 
+  priv->n_available_layers = 0;
+  priv->n_available_extensions = 0;
+
 #if defined (VK_API_VERSION_1_2)
   priv->properties10.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2;
   priv->properties11.sType =
@@ -231,12 +239,13 @@ static void
 gst_vulkan_physical_device_finalize (GObject * object)
 {
   GstVulkanPhysicalDevice *device = GST_VULKAN_PHYSICAL_DEVICE (object);
+  GstVulkanPhysicalDevicePrivate *priv = GET_PRIV (device);
 
-  g_free (device->device_layers);
-  device->device_layers = NULL;
+  g_free (priv->available_layers);
+  priv->available_layers = NULL;
 
-  g_free (device->device_extensions);
-  device->device_extensions = NULL;
+  g_free (priv->available_extensions);
+  priv->available_extensions = NULL;
 
   g_free (device->queue_family_props);
   device->queue_family_props = NULL;
@@ -803,15 +812,15 @@ gst_vulkan_physical_device_fill_info (GstVulkanPhysicalDevice * device,
 
   err =
       vkEnumerateDeviceLayerProperties (device->device,
-      &device->n_device_layers, NULL);
+      &priv->n_available_layers, NULL);
   if (gst_vulkan_error_to_g_error (err, error,
           "vkEnumerateDeviceLayerProperties") < 0)
     goto error;
 
-  device->device_layers = g_new0 (VkLayerProperties, device->n_device_layers);
+  priv->available_layers = g_new0 (VkLayerProperties, priv->n_available_layers);
   err =
       vkEnumerateDeviceLayerProperties (device->device,
-      &device->n_device_layers, device->device_layers);
+      &priv->n_available_layers, priv->available_layers);
   if (gst_vulkan_error_to_g_error (err, error,
           "vkEnumerateDeviceLayerProperties") < 0) {
     goto error;
@@ -819,18 +828,19 @@ gst_vulkan_physical_device_fill_info (GstVulkanPhysicalDevice * device,
 
   err =
       vkEnumerateDeviceExtensionProperties (device->device, NULL,
-      &device->n_device_extensions, NULL);
+      &priv->n_available_extensions, NULL);
   if (gst_vulkan_error_to_g_error (err, error,
           "vkEnumerateDeviceExtensionProperties") < 0) {
     goto error;
   }
-  GST_DEBUG_OBJECT (device, "Found %u extensions", device->n_device_extensions);
+  GST_DEBUG_OBJECT (device, "Found %u extensions",
+      priv->n_available_extensions);
 
-  device->device_extensions =
-      g_new0 (VkExtensionProperties, device->n_device_extensions);
+  priv->available_extensions =
+      g_new0 (VkExtensionProperties, priv->n_available_extensions);
   err =
       vkEnumerateDeviceExtensionProperties (device->device, NULL,
-      &device->n_device_extensions, device->device_extensions);
+      &priv->n_available_extensions, priv->available_extensions);
   if (gst_vulkan_error_to_g_error (err, error,
           "vkEnumerateDeviceExtensionProperties") < 0) {
     goto error;
@@ -951,3 +961,117 @@ gst_vulkan_physical_device_get_instance (GstVulkanPhysicalDevice * device)
 
   return device->instance ? gst_object_ref (device->instance) : NULL;
 }
+
+static gboolean
+gst_vulkan_physical_device_get_layer_info_unlocked (GstVulkanPhysicalDevice *
+    device, const gchar * name, gchar ** description, guint32 * spec_version,
+    guint32 * implementation_version)
+{
+  GstVulkanPhysicalDevicePrivate *priv;
+  int i;
+
+  priv = GET_PRIV (device);
+
+  for (i = 0; i < priv->n_available_layers; i++) {
+    if (g_strcmp0 (name, priv->available_layers[i].layerName) == 0) {
+      if (description)
+        *description = g_strdup (priv->available_layers[i].description);
+      if (spec_version)
+        *spec_version = priv->available_layers[i].specVersion;
+      if (implementation_version)
+        *spec_version = priv->available_layers[i].implementationVersion;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+ * gst_vulkan_physical_device_get_layer_info:
+ * @device: a #GstVulkanPhysicalDevice
+ * @name: the layer name to look for
+ * @description: (out) (nullable): return value for the layer description or %NULL
+ * @spec_version: (out) (nullable): return value for the layer specification version
+ * @implementation_version: (out) (nullable): return value for the layer implementation version
+ *
+ * Retrieves information about a layer.
+ *
+ * Will not find any layers before gst_vulkan_instance_fill_info() has been
+ * called.
+ *
+ * Returns: whether layer @name is available
+ *
+ * Since: 1.18
+ */
+gboolean
+gst_vulkan_physical_device_get_layer_info (GstVulkanPhysicalDevice * device,
+    const gchar * name, gchar ** description, guint32 * spec_version,
+    guint32 * implementation_version)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_PHYSICAL_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret =
+      gst_vulkan_physical_device_get_layer_info_unlocked (device, name,
+      description, spec_version, implementation_version);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
+
+static gboolean
+gst_vulkan_physical_device_get_extension_info_unlocked (GstVulkanPhysicalDevice
+    * device, const gchar * name, guint32 * spec_version)
+{
+  GstVulkanPhysicalDevicePrivate *priv;
+  int i;
+
+  priv = GET_PRIV (device);
+
+  for (i = 0; i < priv->n_available_extensions; i++) {
+    if (g_strcmp0 (name, priv->available_extensions[i].extensionName) == 0) {
+      if (spec_version)
+        *spec_version = priv->available_extensions[i].specVersion;
+      return TRUE;
+    }
+  }
+
+  return FALSE;
+}
+
+/**
+ * gst_vulkan_physical_device_get_extension_info:
+ * @device: a #GstVulkanPhysicalDevice
+ * @name: the extension name to look for
+ * @spec_version: (out) (nullable): return value for the exteion specification version
+ *
+ * Retrieves information about a device extension.
+ *
+ * Will not find any extensions before gst_vulkan_instance_fill_info() has been
+ * called.
+ *
+ * Returns: whether extension @name is available
+ *
+ * Since: 1.18
+ */
+gboolean
+gst_vulkan_physical_device_get_extension_info (GstVulkanPhysicalDevice * device,
+    const gchar * name, guint32 * spec_version)
+{
+  gboolean ret;
+
+  g_return_val_if_fail (GST_IS_VULKAN_PHYSICAL_DEVICE (device), FALSE);
+  g_return_val_if_fail (name != NULL, FALSE);
+
+  GST_OBJECT_LOCK (device);
+  ret =
+      gst_vulkan_physical_device_get_extension_info_unlocked (device, name,
+      spec_version);
+  GST_OBJECT_UNLOCK (device);
+
+  return ret;
+}
index 99f4fd6..b915f4c 100644 (file)
@@ -44,12 +44,6 @@ struct _GstVulkanPhysicalDevice
   guint device_index;
   VkPhysicalDevice device; /* hides a pointer */
 
-  VkLayerProperties *device_layers;
-  guint32 n_device_layers;
-
-  VkExtensionProperties *device_extensions;
-  guint32 n_device_extensions;
-
   VkPhysicalDeviceProperties properties;
   VkPhysicalDeviceFeatures features;
   VkPhysicalDeviceMemoryProperties memory_properties;
@@ -64,12 +58,25 @@ struct _GstVulkanPhysicalDeviceClass
 };
 
 GST_VULKAN_API
-GstVulkanPhysicalDevice *   gst_vulkan_physical_device_new              (GstVulkanInstance * instance, guint device_index);
+GstVulkanPhysicalDevice *   gst_vulkan_physical_device_new                  (GstVulkanInstance * instance,
+                                                                             guint device_index);
+GST_VULKAN_API
+GstVulkanInstance *         gst_vulkan_physical_device_get_instance         (GstVulkanPhysicalDevice * device);
+
 GST_VULKAN_API
-GstVulkanInstance *         gst_vulkan_physical_device_get_instance     (GstVulkanPhysicalDevice * device);
+VkPhysicalDevice            gst_vulkan_physical_device_get_handle           (GstVulkanPhysicalDevice * device);
 
 GST_VULKAN_API
-VkPhysicalDevice            gst_vulkan_physical_device_get_handle       (GstVulkanPhysicalDevice * device);
+gboolean                    gst_vulkan_physical_device_get_extension_info   (GstVulkanPhysicalDevice * device,
+                                                                             const gchar * name,
+                                                                             guint32 * spec_version);
+GST_VULKAN_API
+gboolean                    gst_vulkan_physical_device_get_layer_info       (GstVulkanPhysicalDevice * device,
+                                                                             const gchar * name,
+                                                                             gchar ** description,
+                                                                             guint32 * spec_version,
+                                                                             guint32 * implementation_version);
+
 
 G_END_DECLS
 
index a052c9d..1738f80 100644 (file)
@@ -132,6 +132,13 @@ _get_function_table (GstVulkanSwapper * swapper)
     GST_ERROR_OBJECT (swapper, "Failed to get instance from the device");
     return FALSE;
   }
+
+  if (!gst_vulkan_device_is_extension_enabled (device,
+          VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+    GST_ERROR_OBJECT (swapper, "Required extension \'%s\' is not enabled on "
+        "device %" GST_PTR_FORMAT, VK_KHR_SWAPCHAIN_EXTENSION_NAME, device);
+    return FALSE;
+  }
 #define GET_PROC_ADDRESS_REQUIRED(type, name) \
   G_STMT_START { \
     priv->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \