From aad7ed31e1a92fab3e0990c371b75c3e03be2458 Mon Sep 17 00:00:00 2001 From: Matthew Waters Date: Sat, 13 Jun 2020 17:31:07 +1000 Subject: [PATCH] vulkan/instance: add vulkan API version selection and checking Part-of: --- docs/plugins/gst_plugins_cache.json | 30 ++++ gst-libs/gst/vulkan/gstvkinstance.c | 250 +++++++++++++++++++++++++++- gst-libs/gst/vulkan/gstvkinstance.h | 10 ++ tests/check/libs/vkinstance.c | 137 +++++++++++++++ tests/check/meson.build | 1 + 5 files changed, 419 insertions(+), 9 deletions(-) create mode 100644 tests/check/libs/vkinstance.c diff --git a/docs/plugins/gst_plugins_cache.json b/docs/plugins/gst_plugins_cache.json index e4f1cd6008..6580f9b172 100644 --- a/docs/plugins/gst_plugins_cache.json +++ b/docs/plugins/gst_plugins_cache.json @@ -232272,6 +232272,36 @@ "GObject" ], "kind": "object", + "properties": { + "requested-api-major": { + "blurb": "Major version of the requested Vulkan API (0 = maximum supported)", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "0", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + }, + "requested-api-minor": { + "blurb": "Minor version of the requested Vulkan API", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "0", + "max": "-1", + "min": "0", + "mutable": "null", + "readable": true, + "type": "guint", + "writable": true + } + }, "signals": { "create-device": { "args": [], diff --git a/gst-libs/gst/vulkan/gstvkinstance.c b/gst-libs/gst/vulkan/gstvkinstance.c index e4d01e379c..ddd619b6a0 100644 --- a/gst-libs/gst/vulkan/gstvkinstance.c +++ b/gst-libs/gst/vulkan/gstvkinstance.c @@ -29,11 +29,17 @@ /** * SECTION:vkinstance * @title: GstVulkanInstance - * @short_description: memory subclass for Vulkan image memory - * @see_also: #GstMemory, #GstAllocator + * @short_description: GStreamer Vulkan instance + * @see_also: #GstVulkanPhysicalDevice, #GstVulkanDevice * - * GstVulkanImageMemory is a #GstMemory subclass providing support for the - * mapping of Vulkan device memory. + * #GstVulkanInstance encapsulates the necessary information for the toplevel + * Vulkan instance object. + * + * If GStreamer is built with debugging support, the default Vulkan API chosen + * can be selected with the environment variable + * `GST_VULKAN_INSTANCE_API_VERSION=1.0`. Any subsequent setting of the + * requested Vulkan API version through the available properties will override + * the environment variable. */ #define APP_SHORT_NAME "GStreamer" @@ -50,6 +56,16 @@ enum LAST_SIGNAL }; +enum +{ + PROP_0, + PROP_REQUESTED_API_MAJOR_VERSION, + PROP_REQUESTED_API_MINOR_VERSION, +}; + +#define DEFAULT_REQUESTED_API_VERSION_MAJOR 0 +#define DEFAULT_REQUESTED_API_VERSION_MINOR 0 + static guint gst_vulkan_instance_signals[LAST_SIGNAL] = { 0 }; static void gst_vulkan_instance_finalize (GObject * object); @@ -57,6 +73,9 @@ static void gst_vulkan_instance_finalize (GObject * object); struct _GstVulkanInstancePrivate { gboolean opened; + guint requested_api_major; + guint requested_api_minor; + uint32_t supported_instance_api; }; static void @@ -92,18 +111,111 @@ gst_vulkan_instance_new (void) return instance; } +static void +gst_vulkan_instance_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object); + GstVulkanInstancePrivate *priv = GET_PRIV (instance); + + GST_OBJECT_LOCK (instance); + switch (prop_id) { + case PROP_REQUESTED_API_MAJOR_VERSION: + if (priv->opened) + g_warning ("Attempt to set the requested API version after the " + "instance has been opened"); + priv->requested_api_major = g_value_get_uint (value); + break; + case PROP_REQUESTED_API_MINOR_VERSION: + if (priv->opened) + g_warning ("Attempt to set the requested API version after the " + "instance has been opened"); + priv->requested_api_minor = g_value_get_uint (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (instance); +} + +static void +gst_vulkan_instance_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) +{ + GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object); + GstVulkanInstancePrivate *priv = GET_PRIV (instance); + + GST_OBJECT_LOCK (instance); + switch (prop_id) { + case PROP_REQUESTED_API_MAJOR_VERSION: + g_value_set_uint (value, priv->requested_api_major); + break; + case PROP_REQUESTED_API_MINOR_VERSION: + g_value_set_uint (value, priv->requested_api_minor); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (instance); +} + static void gst_vulkan_instance_init (GstVulkanInstance * instance) { + GstVulkanInstancePrivate *priv = GET_PRIV (instance); + + priv->requested_api_major = DEFAULT_REQUESTED_API_VERSION_MAJOR; + priv->requested_api_minor = DEFAULT_REQUESTED_API_VERSION_MINOR; + +#if !defined (GST_DISABLE_DEBUG) + { + const gchar *api_override = g_getenv ("GST_VULKAN_INSTANCE_API_VERSION"); + if (api_override) { + gchar *end; + gint64 major, minor; + + major = g_ascii_strtoll (api_override, &end, 10); + if (end && end[0] == '.') { + minor = g_ascii_strtoll (&end[1], NULL, 10); + if (major > 0 && major < G_MAXINT64 && minor >= 0 && minor < G_MAXINT64) { + priv->requested_api_major = major; + priv->requested_api_minor = minor; + } + } + } + } +#endif } static void gst_vulkan_instance_class_init (GstVulkanInstanceClass * klass) { + GObjectClass *gobject_class = (GObjectClass *) klass; + gst_vulkan_memory_init_once (); gst_vulkan_image_memory_init_once (); gst_vulkan_buffer_memory_init_once (); + gobject_class->get_property = gst_vulkan_instance_get_property; + gobject_class->set_property = gst_vulkan_instance_set_property; + gobject_class->finalize = gst_vulkan_instance_finalize; + + g_object_class_install_property (gobject_class, + PROP_REQUESTED_API_MAJOR_VERSION, + g_param_spec_uint ("requested-api-major", "Requested API Major", + "Major version of the requested Vulkan API (0 = maximum supported)", + 0, G_MAXUINT, DEFAULT_REQUESTED_API_VERSION_MAJOR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, + PROP_REQUESTED_API_MINOR_VERSION, + g_param_spec_uint ("requested-api-minor", "Requested API Minor", + "Minor version of the requested Vulkan API", + 0, G_MAXUINT, DEFAULT_REQUESTED_API_VERSION_MINOR, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** * GstVulkanInstance::create-device: * @object: the #GstVulkanDisplay @@ -118,8 +230,6 @@ gst_vulkan_instance_class_init (GstVulkanInstanceClass * klass) gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE] = g_signal_new ("create-device", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, GST_TYPE_VULKAN_DEVICE, 0); - - G_OBJECT_CLASS (klass)->finalize = gst_vulkan_instance_finalize; } static void @@ -181,6 +291,26 @@ _gst_vk_debug_callback (VkDebugReportFlagsEXT msgFlags, return FALSE; } +static void +gst_vulkan_get_supported_api_version_unlocked (GstVulkanInstance * instance) +{ + GstVulkanInstancePrivate *priv = GET_PRIV (instance); + PFN_vkEnumerateInstanceVersion gst_vkEnumerateInstanceVersion; + + if (priv->supported_instance_api) + return; + + gst_vkEnumerateInstanceVersion = + (PFN_vkEnumerateInstanceVersion) vkGetInstanceProcAddr (NULL, + "vkEnumerateInstanceVersion"); + + if (!gst_vkEnumerateInstanceVersion + || VK_SUCCESS != + gst_vkEnumerateInstanceVersion (&priv->supported_instance_api)) { + priv->supported_instance_api = VK_MAKE_VERSION (1, 0, 0); + } +} + /** * gst_vulkan_instance_open: * @instance: a #GstVulkanInstance @@ -200,6 +330,7 @@ gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error) uint32_t instance_extension_count = 0; uint32_t enabled_extension_count = 0; uint32_t instance_layer_count = 0; + uint32_t requested_instance_api; gboolean have_debug_extension = FALSE; VkResult err; @@ -213,6 +344,32 @@ gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error) return TRUE; } + gst_vulkan_get_supported_api_version_unlocked (instance); + if (priv->requested_api_major) { + requested_instance_api = + VK_MAKE_VERSION (priv->requested_api_major, priv->requested_api_minor, + 0); + GST_INFO_OBJECT (instance, "requesting Vulkan API %u.%u, max supported " + "%u.%u", priv->requested_api_major, priv->requested_api_minor, + VK_VERSION_MAJOR (priv->supported_instance_api), + VK_VERSION_MINOR (priv->supported_instance_api)); + } else { + requested_instance_api = priv->supported_instance_api; + GST_INFO_OBJECT (instance, "requesting maximum supported API %u.%u", + VK_VERSION_MAJOR (priv->supported_instance_api), + VK_VERSION_MINOR (priv->supported_instance_api)); + } + + if (requested_instance_api > priv->supported_instance_api) { + g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED, + "Requested API version (%u.%u) is larger than the maximum supported " + "version (%u.%u)", VK_VERSION_MAJOR (requested_instance_api), + VK_VERSION_MINOR (requested_instance_api), + VK_VERSION_MAJOR (priv->supported_instance_api), + VK_VERSION_MINOR (priv->supported_instance_api)); + goto error; + } + /* Look for validation layers */ err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL); if (gst_vulkan_error_to_g_error (err, error, @@ -319,7 +476,7 @@ gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error) .applicationVersion = 0, .pEngineName = APP_SHORT_NAME, .engineVersion = 0, - .apiVersion = VK_API_VERSION_1_0 + .apiVersion = requested_instance_api, }; inst_info = (VkInstanceCreateInfo) { @@ -432,13 +589,17 @@ gpointer gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance, const gchar * name) { + gpointer ret; + g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL); g_return_val_if_fail (instance->instance != NULL, NULL); g_return_val_if_fail (name != NULL, NULL); - GST_TRACE_OBJECT (instance, "%s", name); + ret = vkGetInstanceProcAddr (instance->instance, name); - return vkGetInstanceProcAddr (instance->instance, name); + GST_TRACE_OBJECT (instance, "%s = %p", name, ret); + + return ret; } /** @@ -613,3 +774,74 @@ gst_vulkan_instance_run_context_query (GstElement * element, return FALSE; } + +/** + * gst_vulkan_instance_check_version: + * @instance: a #GstVulkanInstance + * @major: major version + * @minor: minor version + * @patch: patch version + * + * Check if the configured vulkan instance supports the specified version. + * Will not work prior to opening the instance with gst_vulkan_instance_open(). + * If a specific version is requested, the @patch level is ignored. + * + * Returns: whether @instance is at least the requested version. + * + * Since: 1.18 + */ +gboolean +gst_vulkan_instance_check_version (GstVulkanInstance * instance, + guint major, guint minor, guint patch) +{ + GstVulkanInstancePrivate *priv; + + g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), FALSE); + + priv = GET_PRIV (instance); + + return (priv->requested_api_major == 0 + && VK_MAKE_VERSION (major, minor, patch) <= priv->supported_instance_api) + || (priv->requested_api_major >= 0 && (major < priv->requested_api_major + || (major == priv->requested_api_major + && minor <= priv->requested_api_minor))); +} + +/** + * gst_vulkan_instance_get_version: + * @instance: a #GstVulkanInstance + * @major: major version + * @minor: minor version + * @patch: patch version + * + * Retrieve the vulkan instance configured version. Only returns the supported + * API version by the instance without taking into account the requested API + * version. This means gst_vulkan_instance_check_version() will return + * different values if a specific version has been requested (which is the + * default) than a version check that is performed manually by retrieving the + * version with this function. + * + * Since: 1.18 + */ +void +gst_vulkan_instance_get_version (GstVulkanInstance * instance, + guint * major, guint * minor, guint * patch) +{ + GstVulkanInstancePrivate *priv; + + g_return_if_fail (GST_IS_VULKAN_INSTANCE (instance)); + + priv = GET_PRIV (instance); + + GST_OBJECT_LOCK (instance); + if (!priv->supported_instance_api) + gst_vulkan_get_supported_api_version_unlocked (instance); + + if (major) + *major = VK_VERSION_MAJOR (priv->supported_instance_api); + if (minor) + *minor = VK_VERSION_MINOR (priv->supported_instance_api); + if (patch) + *patch = VK_VERSION_PATCH (priv->supported_instance_api); + GST_OBJECT_UNLOCK (instance); +} diff --git a/gst-libs/gst/vulkan/gstvkinstance.h b/gst-libs/gst/vulkan/gstvkinstance.h index 4330d57906..81c3806c7b 100644 --- a/gst-libs/gst/vulkan/gstvkinstance.h +++ b/gst-libs/gst/vulkan/gstvkinstance.h @@ -82,6 +82,16 @@ gboolean gst_vulkan_instance_handle_context_query (GstElement * el GST_VULKAN_API gboolean gst_vulkan_instance_run_context_query (GstElement * element, GstVulkanInstance ** instance); +GST_VULKAN_API +gboolean gst_vulkan_instance_check_version (GstVulkanInstance * instance, + guint major, + guint minor, + guint patch); +GST_VULKAN_API +void gst_vulkan_instance_get_version (GstVulkanInstance * instance, + guint * major, + guint * minor, + guint * patch); G_END_DECLS diff --git a/tests/check/libs/vkinstance.c b/tests/check/libs/vkinstance.c new file mode 100644 index 0000000000..2d989dc650 --- /dev/null +++ b/tests/check/libs/vkinstance.c @@ -0,0 +1,137 @@ +/* GStreamer + * + * Copyright (C) 2020 Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +GST_START_TEST (test_instance_new) +{ + GstVulkanInstance *instance; + + instance = gst_vulkan_instance_new (); + fail_unless (instance != NULL); + gst_object_unref (instance); +} + +GST_END_TEST; + +GST_START_TEST (test_instance_open) +{ + GstVulkanInstance *instance; + + instance = gst_vulkan_instance_new (); + fail_unless (instance != NULL); + fail_unless (gst_vulkan_instance_open (instance, NULL)); + gst_object_unref (instance); +} + +GST_END_TEST; + +GST_START_TEST (test_instance_version_before_open) +{ + GstVulkanInstance *instance; + guint major, minor, patch; + + instance = gst_vulkan_instance_new (); + fail_unless (instance != NULL); + gst_vulkan_instance_get_version (instance, &major, &minor, &patch); + gst_object_unref (instance); +} + +GST_END_TEST; + +GST_START_TEST (test_instance_default_max_version) +{ + GstVulkanInstance *instance; + guint major, minor, patch; + + instance = gst_vulkan_instance_new (); + fail_unless (instance != NULL); + gst_vulkan_instance_get_version (instance, &major, &minor, &patch); + fail_unless (gst_vulkan_instance_open (instance, NULL)); + fail_unless (gst_vulkan_instance_check_version (instance, 1, 0, 0)); + fail_unless (gst_vulkan_instance_check_version (instance, major, minor, + patch)); + fail_unless (!gst_vulkan_instance_check_version (instance, major, minor, + patch + 1)); + fail_unless (!gst_vulkan_instance_check_version (instance, major, minor + 1, + patch)); + gst_object_unref (instance); +} + +GST_END_TEST; + +GST_START_TEST (test_instance_request_version) +{ + GstVulkanInstance *instance; + guint major, minor; + + instance = gst_vulkan_instance_new (); + fail_unless (instance != NULL); + gst_vulkan_instance_get_version (instance, &major, &minor, NULL); + + if (major > 1 || minor > 0) { + g_object_set (instance, "requested-api-major", 1, "requested_api_minor", 0, + NULL); + fail_unless (gst_vulkan_instance_open (instance, NULL)); + fail_unless (gst_vulkan_instance_check_version (instance, 1, 0, 0)); + fail_unless (!gst_vulkan_instance_check_version (instance, major, minor, + 0)); + fail_unless (!gst_vulkan_instance_check_version (instance, major, minor + 1, + 0)); + } + gst_object_unref (instance); +} + +GST_END_TEST; + +static Suite * +vkinstance_suite (void) +{ + Suite *s = suite_create ("vkinstance"); + TCase *tc_basic = tcase_create ("general"); + GstVulkanInstance *instance; + gboolean have_instance; + + suite_add_tcase (s, tc_basic); + + tcase_add_test (tc_basic, test_instance_new); + tcase_add_test (tc_basic, test_instance_version_before_open); + + /* FIXME: CI doesn't have a software vulkan renderer (and none exists currently) */ + instance = gst_vulkan_instance_new (); + have_instance = gst_vulkan_instance_open (instance, NULL); + gst_object_unref (instance); + if (have_instance) { + tcase_add_test (tc_basic, test_instance_open); + tcase_add_test (tc_basic, test_instance_default_max_version); + tcase_add_test (tc_basic, test_instance_request_version); + } + + return s; +} + + +GST_CHECK_MAIN (vkinstance); diff --git a/tests/check/meson.build b/tests/check/meson.build index be60e84d65..12eefe3ab9 100644 --- a/tests/check/meson.build +++ b/tests/check/meson.build @@ -76,6 +76,7 @@ base_tests = [ [['elements/vkdeviceprovider.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], [['libs/vkcommandpool.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], [['libs/vkimage.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], + [['libs/vkinstance.c'], not gstvulkan_dep.found(), [gstvulkan_dep]], ] # FIXME: unistd dependency, unstable or not tested yet on windows