diff --git a/gst-libs/gst/vulkan/gstvkdevice.c b/gst-libs/gst/vulkan/gstvkdevice.c index dd6c345598..76be6449b0 100644 --- a/gst-libs/gst/vulkan/gstvkdevice.c +++ b/gst-libs/gst/vulkan/gstvkdevice.c @@ -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; +} diff --git a/gst-libs/gst/vulkan/gstvkdevice.h b/gst-libs/gst/vulkan/gstvkdevice.h index 1614f98f39..5a69fb8bd6 100644 --- a/gst-libs/gst/vulkan/gstvkdevice.h +++ b/gst-libs/gst/vulkan/gstvkdevice.h @@ -63,6 +63,22 @@ GST_VULKAN_API gboolean gst_vulkan_device_open (GstVulkanDevice * device, 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); diff --git a/gst-libs/gst/vulkan/gstvkphysicaldevice.c b/gst-libs/gst/vulkan/gstvkphysicaldevice.c index 2bd434eebc..f55e0f4754 100644 --- a/gst-libs/gst/vulkan/gstvkphysicaldevice.c +++ b/gst-libs/gst/vulkan/gstvkphysicaldevice.c @@ -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; +} diff --git a/gst-libs/gst/vulkan/gstvkphysicaldevice.h b/gst-libs/gst/vulkan/gstvkphysicaldevice.h index 99f4fd651d..b915f4c965 100644 --- a/gst-libs/gst/vulkan/gstvkphysicaldevice.h +++ b/gst-libs/gst/vulkan/gstvkphysicaldevice.h @@ -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); +GstVulkanInstance * gst_vulkan_physical_device_get_instance (GstVulkanPhysicalDevice * device); GST_VULKAN_API -VkPhysicalDevice gst_vulkan_physical_device_get_handle (GstVulkanPhysicalDevice * device); +VkPhysicalDevice gst_vulkan_physical_device_get_handle (GstVulkanPhysicalDevice * device); + +GST_VULKAN_API +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 diff --git a/gst-libs/gst/vulkan/gstvkswapper.c b/gst-libs/gst/vulkan/gstvkswapper.c index a052c9dca9..1738f800b3 100644 --- a/gst-libs/gst/vulkan/gstvkswapper.c +++ b/gst-libs/gst/vulkan/gstvkswapper.c @@ -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)); \