documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkinstance.c
1 /*
2  * GStreamer
3  * Copyright (C) 2015 Matthew Waters <matthew@centricular.com>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "gstvkinstance.h"
26
27 #include <string.h>
28
29 /**
30  * SECTION:vkinstance
31  * @title: GstVulkanInstance
32  * @short_description: memory subclass for Vulkan image memory
33  * @see_also: #GstMemory, #GstAllocator
34  *
35  * GstVulkanImageMemory is a #GstMemory subclass providing support for the
36  * mapping of Vulkan device memory.
37  */
38
39 #define APP_SHORT_NAME "GStreamer"
40
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);
45
46 enum
47 {
48   SIGNAL_0,
49   SIGNAL_CREATE_DEVICE,
50   LAST_SIGNAL
51 };
52
53 static guint gst_vulkan_instance_signals[LAST_SIGNAL] = { 0 };
54
55 static void gst_vulkan_instance_finalize (GObject * object);
56
57 struct _GstVulkanInstancePrivate
58 {
59   gboolean opened;
60 };
61
62 static void
63 _init_debug (void)
64 {
65   static volatile gsize _init = 0;
66
67   if (g_once_init_enter (&_init)) {
68     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkaninstance", 0,
69         "Vulkan Instance");
70     GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT, "vulkandebug", 0,
71         "Vulkan Debug");
72     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
73     g_once_init_leave (&_init, 1);
74   }
75 }
76
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)
80     _init_debug ());
81
82 GstVulkanInstance *
83 gst_vulkan_instance_new (void)
84 {
85   GstVulkanInstance *instance;
86
87   instance = g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL);
88   gst_object_ref_sink (instance);
89
90   return instance;
91 }
92
93 static void
94 gst_vulkan_instance_init (GstVulkanInstance * instance)
95 {
96   instance->priv = gst_vulkan_instance_get_instance_private (instance);
97 }
98
99 static void
100 gst_vulkan_instance_class_init (GstVulkanInstanceClass * klass)
101 {
102   gst_vulkan_memory_init_once ();
103   gst_vulkan_image_memory_init_once ();
104   gst_vulkan_buffer_memory_init_once ();
105
106   /**
107    * GstVulkanInstance::create-device:
108    * @object: the #GstVulkanDisplay
109    *
110    * Overrides the #GstVulkanDevice creation mechanism.
111    * It can be called from any thread.
112    *
113    * Returns: (transfer full): the newly created #GstVulkanDevice.
114    *
115    * Since: 1.18
116    */
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);
121
122   G_OBJECT_CLASS (klass)->finalize = gst_vulkan_instance_finalize;
123 }
124
125 static void
126 gst_vulkan_instance_finalize (GObject * object)
127 {
128   GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object);
129
130   if (instance->priv->opened) {
131     if (instance->dbgDestroyDebugReportCallback)
132       instance->dbgDestroyDebugReportCallback (instance->instance,
133           instance->msg_callback, NULL);
134
135     g_free (instance->physical_devices);
136   }
137   instance->priv->opened = FALSE;
138
139   if (instance->instance)
140     vkDestroyInstance (instance->instance, NULL);
141   instance->instance = NULL;
142
143   G_OBJECT_CLASS (parent_class)->finalize (object);
144 }
145
146 static VkBool32
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,
150     void *pUserData)
151 {
152   if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
153     GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
154         msgCode, pMsg);
155   } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
156     GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
157         msgCode, pMsg);
158   } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
159     GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
160         msgCode, pMsg);
161   } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
162     GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
163         msgCode, pMsg);
164   } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
165     GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
166         msgCode, pMsg);
167   } else {
168     return FALSE;
169   }
170
171   /*
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.
177    */
178   return FALSE;
179 }
180
181 /**
182  * gst_vulkan_instance_open:
183  * @instance: a #GstVulkanInstance
184  * @error: #GError
185  *
186  * Returns: whether the instance could be created
187  *
188  * Since: 1.18
189  */
190 gboolean
191 gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error)
192 {
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;
200   VkResult err;
201
202   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), FALSE);
203
204   GST_OBJECT_LOCK (instance);
205   if (instance->priv->opened) {
206     GST_OBJECT_UNLOCK (instance);
207     return TRUE;
208   }
209
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)
214     goto error;
215
216   instance_layers = g_new0 (VkLayerProperties, instance_layer_count);
217   err =
218       vkEnumerateInstanceLayerProperties (&instance_layer_count,
219       instance_layers);
220   if (gst_vulkan_error_to_g_error (err, error,
221           "vKEnumerateInstanceLayerProperties") < 0) {
222     g_free (instance_layers);
223     goto error;
224   }
225
226   g_free (instance_layers);
227
228   err =
229       vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
230       NULL);
231   if (gst_vulkan_error_to_g_error (err, error,
232           "vkEnumerateInstanceExtensionProperties") < 0) {
233     goto error;
234   }
235   GST_DEBUG_OBJECT (instance, "Found %u extensions", instance_extension_count);
236
237   memset (extension_names, 0, sizeof (extension_names));
238   instance_extensions =
239       g_new0 (VkExtensionProperties, instance_extension_count);
240   err =
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);
246     goto error;
247   }
248
249   {
250     GstVulkanDisplayType display_type;
251     gboolean swapchain_ext_found = FALSE;
252     gboolean winsys_ext_found = FALSE;
253     const gchar *winsys_ext_name;
254     uint32_t i;
255
256     display_type = gst_vulkan_display_choose_type (instance);
257
258     winsys_ext_name =
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 */
263     }
264
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);
269
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;
275       }
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;
281       }
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;
285       }
286       g_assert (enabled_extension_count < 64);
287     }
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);
293       goto error;
294     }
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);
300       goto error;
301     }
302   }
303
304   {
305     VkApplicationInfo app = { 0, };
306     VkInstanceCreateInfo inst_info = { 0, };
307
308     /* *INDENT-OFF* */
309     app = (VkApplicationInfo) {
310         .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
311         .pNext = NULL,
312         .pApplicationName = APP_SHORT_NAME,
313         .applicationVersion = 0,
314         .pEngineName = APP_SHORT_NAME,
315         .engineVersion = 0,
316         .apiVersion = VK_API_VERSION_1_0
317     };
318
319     inst_info = (VkInstanceCreateInfo) {
320         .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
321         .pNext = NULL,
322         .pApplicationInfo = &app,
323         .enabledLayerCount = 0,
324         .ppEnabledLayerNames = NULL,
325         .enabledExtensionCount = enabled_extension_count,
326         .ppEnabledExtensionNames = (const char *const *) extension_names
327     };
328     /* *INDENT-ON* */
329
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);
333       goto error;
334     }
335   }
336
337   g_free (instance_extensions);
338
339   err =
340       vkEnumeratePhysicalDevices (instance->instance,
341       &instance->n_physical_devices, NULL);
342   if (gst_vulkan_error_to_g_error (err, error,
343           "vkEnumeratePhysicalDevices") < 0)
344     goto error;
345   g_assert (instance->n_physical_devices > 0);
346   instance->physical_devices =
347       g_new0 (VkPhysicalDevice, instance->n_physical_devices);
348   err =
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)
353     goto error;
354
355   if (have_debug_extension) {
356     VkDebugReportCallbackCreateInfoEXT info = { 0, };
357
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");
365       goto error;
366     }
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");
374       goto error;
375     }
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");
382       goto error;
383     }
384
385     info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
386     info.pNext = NULL;
387     info.flags =
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;
393
394     err =
395         instance->dbgCreateDebugReportCallback (instance->instance, &info, NULL,
396         &instance->msg_callback);
397     if (gst_vulkan_error_to_g_error (err, error,
398             "vkCreateDebugReportCallback") < 0)
399       goto error;
400   }
401
402   instance->priv->opened = TRUE;
403   GST_OBJECT_UNLOCK (instance);
404
405   return TRUE;
406
407 error:
408   {
409     GST_OBJECT_UNLOCK (instance);
410     return FALSE;
411   }
412 }
413
414 /**
415  * gst_vulkan_instance_get_proc_address:
416  * @instance: a #GstVulkanInstance
417  * @name: name of the function to retrieve
418  *
419  * Performs vkGetInstanceProcAddr() with @instance and @name
420  *
421  * Returns: the function pointer for @name or %NULL
422  *
423  * Since: 1.18
424  */
425 gpointer
426 gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
427     const gchar * name)
428 {
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);
432
433   GST_TRACE_OBJECT (instance, "%s", name);
434
435   return vkGetInstanceProcAddr (instance->instance, name);
436 }
437
438 /**
439  * gst_vulkan_instance_create_device:
440  * @instance: a #GstVulkanIncstance
441  *
442  * Returns: (transfer full): a new #GstVulkanDevice
443  *
444  * Since: 1.18
445  */
446 GstVulkanDevice *
447 gst_vulkan_instance_create_device (GstVulkanInstance * instance,
448     GError ** error)
449 {
450   GstVulkanDevice *device;
451
452   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
453
454   g_signal_emit (instance, gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE], 0,
455       &device);
456
457   if (!device) {
458     device = gst_vulkan_device_new_with_index (instance, 0);
459   }
460
461   if (!gst_vulkan_device_open (device, error)) {
462     gst_object_unref (device);
463     device = NULL;
464   }
465
466   return device;
467 }
468
469 /**
470  * gst_context_set_vulkan_instance:
471  * @context: a #GstContext
472  * @instance: a #GstVulkanInstance
473  *
474  * Sets @instance on @context
475  *
476  * Since: 1.18
477  */
478 void
479 gst_context_set_vulkan_instance (GstContext * context,
480     GstVulkanInstance * instance)
481 {
482   GstStructure *s;
483
484   g_return_if_fail (context != NULL);
485   g_return_if_fail (gst_context_is_writable (context));
486
487   if (instance)
488     GST_CAT_LOG (GST_CAT_CONTEXT,
489         "setting GstVulkanInstance(%" GST_PTR_FORMAT ") on context(%"
490         GST_PTR_FORMAT ")", instance, context);
491
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);
495 }
496
497 /**
498  * gst_context_get_vulkan_instance:
499  * @context: a #GstContext
500  * @instance: resulting #GstVulkanInstance
501  *
502  * Returns: Whether @instance was in @context
503  *
504  * Since: 1.18
505  */
506 gboolean
507 gst_context_get_vulkan_instance (GstContext * context,
508     GstVulkanInstance ** instance)
509 {
510   const GstStructure *s;
511   gboolean ret;
512
513   g_return_val_if_fail (instance != NULL, FALSE);
514   g_return_val_if_fail (context != NULL, FALSE);
515
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);
519
520   GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVulkanInstance(%" GST_PTR_FORMAT
521       ") from context(%" GST_PTR_FORMAT ")", *instance, context);
522
523   return ret;
524 }
525
526 /**
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
531  *
532  * If a #GstVulkanInstance is requested in @query, sets @instance as the reply.
533  *
534  * Intended for use with element query handlers to respond to #GST_QUERY_CONTEXT
535  * for a #GstVulkanInstance.
536  *
537  * Returns: whether @query was responded to with @instance
538  *
539  * Since: 1.18
540  */
541 gboolean
542 gst_vulkan_instance_handle_context_query (GstElement * element,
543     GstQuery * query, GstVulkanInstance * instance)
544 {
545   gboolean res = FALSE;
546   const gchar *context_type;
547   GstContext *context, *old_context;
548
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);
552
553   if (!instance)
554     return FALSE;
555
556   gst_query_parse_context_type (query, &context_type);
557
558   if (g_strcmp0 (context_type, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
559     gst_query_parse_context (query, &old_context);
560
561     if (old_context)
562       context = gst_context_copy (old_context);
563     else
564       context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
565
566     gst_context_set_vulkan_instance (context, instance);
567     gst_query_set_context (query, context);
568     gst_context_unref (context);
569
570     res = instance != NULL;
571   }
572
573   return res;
574 }
575
576 /**
577  * gst_vulkan_instance_run_context_query:
578  * @element: a #GstElement
579  * @instance: (inout): a #GstVulkanInstance
580  *
581  * Attempt to retrieve a #GstVulkanInstance using #GST_QUERY_CONTEXT from the
582  * surrounding elements of @element.
583  *
584  * Returns: whether @instance contains a valid #GstVulkanInstance
585  *
586  * Since: 1.18
587  */
588 gboolean
589 gst_vulkan_instance_run_context_query (GstElement * element,
590     GstVulkanInstance ** instance)
591 {
592   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
593   g_return_val_if_fail (instance != NULL, FALSE);
594
595   _init_debug ();
596
597   if (*instance && GST_IS_VULKAN_INSTANCE (*instance))
598     return TRUE;
599
600   gst_vulkan_global_context_query (element,
601       GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
602
603   GST_DEBUG_OBJECT (element, "found instance %p", *instance);
604
605   if (*instance)
606     return TRUE;
607
608   return FALSE;
609 }