3 * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
25 #include "gstvkinstance.h"
31 * @title: GstVulkanInstance
32 * @short_description: memory subclass for Vulkan image memory
33 * @see_also: #GstMemory, #GstAllocator
35 * GstVulkanImageMemory is a #GstMemory subclass providing support for the
36 * mapping of Vulkan device memory.
39 #define APP_SHORT_NAME "GStreamer"
41 #define GST_CAT_DEFAULT gst_vulkan_instance_debug
42 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
43 GST_DEBUG_CATEGORY (GST_VULKAN_DEBUG_CAT);
44 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
53 static guint gst_vulkan_instance_signals[LAST_SIGNAL] = { 0 };
55 static void gst_vulkan_instance_finalize (GObject * object);
57 struct _GstVulkanInstancePrivate
65 static volatile gsize _init = 0;
67 if (g_once_init_enter (&_init)) {
68 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkaninstance", 0,
70 GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT, "vulkandebug", 0,
72 GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
73 g_once_init_leave (&_init, 1);
77 #define gst_vulkan_instance_parent_class parent_class
78 G_DEFINE_TYPE_WITH_CODE (GstVulkanInstance, gst_vulkan_instance,
79 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanInstance)
83 gst_vulkan_instance_new (void)
85 GstVulkanInstance *instance;
87 instance = g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL);
88 gst_object_ref_sink (instance);
94 gst_vulkan_instance_init (GstVulkanInstance * instance)
96 instance->priv = gst_vulkan_instance_get_instance_private (instance);
100 gst_vulkan_instance_class_init (GstVulkanInstanceClass * klass)
102 gst_vulkan_memory_init_once ();
103 gst_vulkan_image_memory_init_once ();
104 gst_vulkan_buffer_memory_init_once ();
107 * GstVulkanInstance::create-device:
108 * @object: the #GstVulkanDisplay
110 * Overrides the #GstVulkanDevice creation mechanism.
111 * It can be called from any thread.
113 * Returns: (transfer full): the newly created #GstVulkanDevice.
117 gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE] =
118 g_signal_new ("create-device", G_TYPE_FROM_CLASS (klass),
119 G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
120 GST_TYPE_VULKAN_DEVICE, 0);
122 G_OBJECT_CLASS (klass)->finalize = gst_vulkan_instance_finalize;
126 gst_vulkan_instance_finalize (GObject * object)
128 GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object);
130 if (instance->priv->opened) {
131 if (instance->dbgDestroyDebugReportCallback)
132 instance->dbgDestroyDebugReportCallback (instance->instance,
133 instance->msg_callback, NULL);
135 g_free (instance->physical_devices);
137 instance->priv->opened = FALSE;
139 if (instance->instance)
140 vkDestroyInstance (instance->instance, NULL);
141 instance->instance = NULL;
143 G_OBJECT_CLASS (parent_class)->finalize (object);
147 _gst_vk_debug_callback (VkDebugReportFlagsEXT msgFlags,
148 VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location,
149 int32_t msgCode, const char *pLayerPrefix, const char *pMsg,
152 if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
153 GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
155 } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
156 GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
158 } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
159 GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
161 } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
162 GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
164 } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
165 GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
172 * false indicates that layer should not bail-out of an
173 * API call that had validation failures. This may mean that the
174 * app dies inside the driver due to invalid parameter(s).
175 * That's what would happen without validation layers, so we'll
176 * keep that behavior here.
182 * gst_vulkan_instance_open:
183 * @instance: a #GstVulkanInstance
186 * Returns: whether the instance could be created
191 gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error)
193 VkExtensionProperties *instance_extensions;
194 char *extension_names[64]; /* FIXME: make dynamic */
195 VkLayerProperties *instance_layers;
196 uint32_t instance_extension_count = 0;
197 uint32_t enabled_extension_count = 0;
198 uint32_t instance_layer_count = 0;
199 gboolean have_debug_extension = FALSE;
202 g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), FALSE);
204 GST_OBJECT_LOCK (instance);
205 if (instance->priv->opened) {
206 GST_OBJECT_UNLOCK (instance);
210 /* Look for validation layers */
211 err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL);
212 if (gst_vulkan_error_to_g_error (err, error,
213 "vKEnumerateInstanceLayerProperties") < 0)
216 instance_layers = g_new0 (VkLayerProperties, instance_layer_count);
218 vkEnumerateInstanceLayerProperties (&instance_layer_count,
220 if (gst_vulkan_error_to_g_error (err, error,
221 "vKEnumerateInstanceLayerProperties") < 0) {
222 g_free (instance_layers);
226 g_free (instance_layers);
229 vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
231 if (gst_vulkan_error_to_g_error (err, error,
232 "vkEnumerateInstanceExtensionProperties") < 0) {
235 GST_DEBUG_OBJECT (instance, "Found %u extensions", instance_extension_count);
237 memset (extension_names, 0, sizeof (extension_names));
238 instance_extensions =
239 g_new0 (VkExtensionProperties, instance_extension_count);
241 vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
242 instance_extensions);
243 if (gst_vulkan_error_to_g_error (err, error,
244 "vkEnumerateInstanceExtensionProperties") < 0) {
245 g_free (instance_extensions);
250 GstVulkanDisplayType display_type;
251 gboolean swapchain_ext_found = FALSE;
252 gboolean winsys_ext_found = FALSE;
253 const gchar *winsys_ext_name;
256 display_type = gst_vulkan_display_choose_type (instance);
259 gst_vulkan_display_type_to_extension_string (display_type);
260 if (!winsys_ext_name) {
261 GST_WARNING_OBJECT (instance, "No window system extension enabled");
262 winsys_ext_found = TRUE; /* Don't error out completely */
265 /* TODO: allow outside selection */
266 for (i = 0; i < instance_extension_count; i++) {
267 GST_TRACE_OBJECT (instance, "checking instance extension %s",
268 instance_extensions[i].extensionName);
270 if (!g_strcmp0 (VK_KHR_SURFACE_EXTENSION_NAME,
271 instance_extensions[i].extensionName)) {
272 swapchain_ext_found = TRUE;
273 extension_names[enabled_extension_count++] =
274 (gchar *) VK_KHR_SURFACE_EXTENSION_NAME;
276 if (!g_strcmp0 (VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
277 instance_extensions[i].extensionName)) {
278 extension_names[enabled_extension_count++] =
279 (gchar *) VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
280 have_debug_extension = TRUE;
282 if (!g_strcmp0 (winsys_ext_name, instance_extensions[i].extensionName)) {
283 winsys_ext_found = TRUE;
284 extension_names[enabled_extension_count++] = (gchar *) winsys_ext_name;
286 g_assert (enabled_extension_count < 64);
288 if (!swapchain_ext_found) {
289 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
290 "vkEnumerateInstanceExtensionProperties failed to find the required "
291 "\"" VK_KHR_SURFACE_EXTENSION_NAME "\" extension");
292 g_free (instance_extensions);
295 if (!winsys_ext_found) {
296 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
297 "vkEnumerateInstanceExtensionProperties failed to find the required "
298 "\"%s\" window system extension", winsys_ext_name);
299 g_free (instance_extensions);
305 VkApplicationInfo app = { 0, };
306 VkInstanceCreateInfo inst_info = { 0, };
309 app = (VkApplicationInfo) {
310 .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
312 .pApplicationName = APP_SHORT_NAME,
313 .applicationVersion = 0,
314 .pEngineName = APP_SHORT_NAME,
316 .apiVersion = VK_API_VERSION_1_0
319 inst_info = (VkInstanceCreateInfo) {
320 .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
322 .pApplicationInfo = &app,
323 .enabledLayerCount = 0,
324 .ppEnabledLayerNames = NULL,
325 .enabledExtensionCount = enabled_extension_count,
326 .ppEnabledExtensionNames = (const char *const *) extension_names
330 err = vkCreateInstance (&inst_info, NULL, &instance->instance);
331 if (gst_vulkan_error_to_g_error (err, error, "vkCreateInstance") < 0) {
332 g_free (instance_extensions);
337 g_free (instance_extensions);
340 vkEnumeratePhysicalDevices (instance->instance,
341 &instance->n_physical_devices, NULL);
342 if (gst_vulkan_error_to_g_error (err, error,
343 "vkEnumeratePhysicalDevices") < 0)
345 g_assert (instance->n_physical_devices > 0);
346 instance->physical_devices =
347 g_new0 (VkPhysicalDevice, instance->n_physical_devices);
349 vkEnumeratePhysicalDevices (instance->instance,
350 &instance->n_physical_devices, instance->physical_devices);
351 if (gst_vulkan_error_to_g_error (err, error,
352 "vkEnumeratePhysicalDevices") < 0)
355 if (have_debug_extension) {
356 VkDebugReportCallbackCreateInfoEXT info = { 0, };
358 instance->dbgCreateDebugReportCallback =
359 (PFN_vkCreateDebugReportCallbackEXT)
360 gst_vulkan_instance_get_proc_address (instance,
361 "vkCreateDebugReportCallbackEXT");
362 if (!instance->dbgCreateDebugReportCallback) {
363 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
364 "Failed to retrieve vkCreateDebugReportCallback");
367 instance->dbgDestroyDebugReportCallback =
368 (PFN_vkDestroyDebugReportCallbackEXT)
369 gst_vulkan_instance_get_proc_address (instance,
370 "vkDestroyDebugReportCallbackEXT");
371 if (!instance->dbgDestroyDebugReportCallback) {
372 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
373 "Failed to retrieve vkDestroyDebugReportCallback");
376 instance->dbgReportMessage = (PFN_vkDebugReportMessageEXT)
377 gst_vulkan_instance_get_proc_address (instance,
378 "vkDebugReportMessageEXT");
379 if (!instance->dbgReportMessage) {
380 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
381 "Failed to retrieve vkDebugReportMessage");
385 info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
388 VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
389 VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT |
390 VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
391 info.pfnCallback = (PFN_vkDebugReportCallbackEXT) _gst_vk_debug_callback;
392 info.pUserData = NULL;
395 instance->dbgCreateDebugReportCallback (instance->instance, &info, NULL,
396 &instance->msg_callback);
397 if (gst_vulkan_error_to_g_error (err, error,
398 "vkCreateDebugReportCallback") < 0)
402 instance->priv->opened = TRUE;
403 GST_OBJECT_UNLOCK (instance);
409 GST_OBJECT_UNLOCK (instance);
415 * gst_vulkan_instance_get_proc_address:
416 * @instance: a #GstVulkanInstance
417 * @name: name of the function to retrieve
419 * Performs vkGetInstanceProcAddr() with @instance and @name
421 * Returns: the function pointer for @name or %NULL
426 gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
429 g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
430 g_return_val_if_fail (instance->instance != NULL, NULL);
431 g_return_val_if_fail (name != NULL, NULL);
433 GST_TRACE_OBJECT (instance, "%s", name);
435 return vkGetInstanceProcAddr (instance->instance, name);
439 * gst_vulkan_instance_create_device:
440 * @instance: a #GstVulkanIncstance
442 * Returns: (transfer full): a new #GstVulkanDevice
447 gst_vulkan_instance_create_device (GstVulkanInstance * instance,
450 GstVulkanDevice *device;
452 g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
454 g_signal_emit (instance, gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE], 0,
458 device = gst_vulkan_device_new_with_index (instance, 0);
461 if (!gst_vulkan_device_open (device, error)) {
462 gst_object_unref (device);
470 * gst_context_set_vulkan_instance:
471 * @context: a #GstContext
472 * @instance: a #GstVulkanInstance
474 * Sets @instance on @context
479 gst_context_set_vulkan_instance (GstContext * context,
480 GstVulkanInstance * instance)
484 g_return_if_fail (context != NULL);
485 g_return_if_fail (gst_context_is_writable (context));
488 GST_CAT_LOG (GST_CAT_CONTEXT,
489 "setting GstVulkanInstance(%" GST_PTR_FORMAT ") on context(%"
490 GST_PTR_FORMAT ")", instance, context);
492 s = gst_context_writable_structure (context);
493 gst_structure_set (s, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR,
494 GST_TYPE_VULKAN_INSTANCE, instance, NULL);
498 * gst_context_get_vulkan_instance:
499 * @context: a #GstContext
500 * @instance: resulting #GstVulkanInstance
502 * Returns: Whether @instance was in @context
507 gst_context_get_vulkan_instance (GstContext * context,
508 GstVulkanInstance ** instance)
510 const GstStructure *s;
513 g_return_val_if_fail (instance != NULL, FALSE);
514 g_return_val_if_fail (context != NULL, FALSE);
516 s = gst_context_get_structure (context);
517 ret = gst_structure_get (s, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR,
518 GST_TYPE_VULKAN_INSTANCE, instance, NULL);
520 GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVulkanInstance(%" GST_PTR_FORMAT
521 ") from context(%" GST_PTR_FORMAT ")", *instance, context);
527 * gst_vulkan_instance_handle_context_query:
528 * @element: a #GstElement
529 * @query: a #GstQuery of type #GST_QUERY_CONTEXT
530 * @instance: (nullable): the #GstVulkanInstance
532 * If a #GstVulkanInstance is requested in @query, sets @instance as the reply.
534 * Intended for use with element query handlers to respond to #GST_QUERY_CONTEXT
535 * for a #GstVulkanInstance.
537 * Returns: whether @query was responded to with @instance
542 gst_vulkan_instance_handle_context_query (GstElement * element,
543 GstQuery * query, GstVulkanInstance * instance)
545 gboolean res = FALSE;
546 const gchar *context_type;
547 GstContext *context, *old_context;
549 g_return_val_if_fail (element != NULL, FALSE);
550 g_return_val_if_fail (query != NULL, FALSE);
551 g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT, FALSE);
556 gst_query_parse_context_type (query, &context_type);
558 if (g_strcmp0 (context_type, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
559 gst_query_parse_context (query, &old_context);
562 context = gst_context_copy (old_context);
564 context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
566 gst_context_set_vulkan_instance (context, instance);
567 gst_query_set_context (query, context);
568 gst_context_unref (context);
570 res = instance != NULL;
577 * gst_vulkan_instance_run_context_query:
578 * @element: a #GstElement
579 * @instance: (inout): a #GstVulkanInstance
581 * Attempt to retrieve a #GstVulkanInstance using #GST_QUERY_CONTEXT from the
582 * surrounding elements of @element.
584 * Returns: whether @instance contains a valid #GstVulkanInstance
589 gst_vulkan_instance_run_context_query (GstElement * element,
590 GstVulkanInstance ** instance)
592 g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
593 g_return_val_if_fail (instance != NULL, FALSE);
597 if (*instance && GST_IS_VULKAN_INSTANCE (*instance))
600 gst_vulkan_global_context_query (element,
601 GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
603 GST_DEBUG_OBJECT (element, "found instance %p", *instance);