vulkan/instance: add vulkan API version selection and checking
authorMatthew Waters <matthew@centricular.com>
Sat, 13 Jun 2020 07:31:07 +0000 (17:31 +1000)
committerGStreamer Merge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Sun, 21 Jun 2020 09:30:29 +0000 (09:30 +0000)
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1341>

docs/plugins/gst_plugins_cache.json
gst-libs/gst/vulkan/gstvkinstance.c
gst-libs/gst/vulkan/gstvkinstance.h
tests/check/libs/vkinstance.c [new file with mode: 0644]
tests/check/meson.build

index e4f1cd60081b6bc0185f074dbfaa9be029699ea8..6580f9b172504157c909e46ddc8b7336b5c4aad6 100644 (file)
                     "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": [],
index e4d01e379c695d57121c12d2f0573162dd812f62..ddd619b6a095bd2e85c2a29d5bf113e79c511c27 100644 (file)
 /**
  * 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);
+}
index 4330d579063588c160042e508217b430144a0cca..81c3806c7b4619f38612643b8e90ded1e4348df2 100644 (file)
@@ -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 (file)
index 0000000..2d989dc
--- /dev/null
@@ -0,0 +1,137 @@
+/* GStreamer
+ *
+ * Copyright (C) 2020 Matthew Waters <matthew@centricular.com>
+ *
+ * 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 <gst/gst.h>
+#include <gst/check/gstcheck.h>
+#include <gst/vulkan/vulkan.h>
+
+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);
index be60e84d65b580738db94c3515ef648a5f6d5647..12eefe3ab946cfc3b7d28492b91598ecb05da9d2 100644 (file)
@@ -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