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 "vkinstance.h"
26 #include "vkutils_private.h"
27 
28 #include <string.h>
29 
30 #define APP_SHORT_NAME "GStreamer"
31 
32 static const char *instance_validation_layers[] = {
33   "VK_LAYER_GOOGLE_threading",
34   "VK_LAYER_LUNARG_mem_tracker",
35   "VK_LAYER_LUNARG_object_tracker",
36   "VK_LAYER_LUNARG_draw_state",
37   "VK_LAYER_LUNARG_param_checker",
38   "VK_LAYER_LUNARG_swapchain",
39   "VK_LAYER_LUNARG_device_limits",
40   "VK_LAYER_LUNARG_image",
41 };
42 
43 #define GST_CAT_DEFAULT gst_vulkan_instance_debug
44 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
45 GST_DEBUG_CATEGORY (GST_VULKAN_DEBUG_CAT);
46 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
47 
48 enum
49 {
50   SIGNAL_0,
51   SIGNAL_CREATE_DEVICE,
52   LAST_SIGNAL
53 };
54 
55 static guint gst_vulkan_instance_signals[LAST_SIGNAL] = { 0 };
56 
57 static void gst_vulkan_instance_finalize (GObject * object);
58 
59 struct _GstVulkanInstancePrivate
60 {
61   gboolean opened;
62 };
63 
64 #define gst_vulkan_instance_parent_class parent_class
65 G_DEFINE_TYPE_WITH_CODE (GstVulkanInstance, gst_vulkan_instance,
66     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanInstance)
67     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
68         "vulkaninstance", 0, "Vulkan Instance");
69     GST_DEBUG_CATEGORY_INIT (GST_VULKAN_DEBUG_CAT,
70         "vulkandebug", 0, "Vulkan Debug");
71     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT"));
72 
73 GstVulkanInstance *
gst_vulkan_instance_new(void)74 gst_vulkan_instance_new (void)
75 {
76   GstVulkanInstance *instance;
77 
78   instance = g_object_new (GST_TYPE_VULKAN_INSTANCE, NULL);
79   gst_object_ref_sink (instance);
80 
81   return instance;
82 }
83 
84 static void
gst_vulkan_instance_init(GstVulkanInstance * instance)85 gst_vulkan_instance_init (GstVulkanInstance * instance)
86 {
87   instance->priv = gst_vulkan_instance_get_instance_private (instance);
88 }
89 
90 static void
gst_vulkan_instance_class_init(GstVulkanInstanceClass * klass)91 gst_vulkan_instance_class_init (GstVulkanInstanceClass * klass)
92 {
93   gst_vulkan_memory_init_once ();
94   gst_vulkan_image_memory_init_once ();
95   gst_vulkan_buffer_memory_init_once ();
96 
97   /**
98    * GstVulkanInstance::create-device:
99    * @object: the #GstVulkanDisplay
100    *
101    * Overrides the #GstVulkanDevice creation mechanism.
102    * It can be called from any thread.
103    *
104    * Returns: the newly created #GstVulkanDevice.
105    */
106   gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE] =
107       g_signal_new ("create-device", G_TYPE_FROM_CLASS (klass),
108       G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_generic,
109       GST_TYPE_VULKAN_DEVICE, 0);
110 
111   G_OBJECT_CLASS (klass)->finalize = gst_vulkan_instance_finalize;
112 }
113 
114 static void
gst_vulkan_instance_finalize(GObject * object)115 gst_vulkan_instance_finalize (GObject * object)
116 {
117   GstVulkanInstance *instance = GST_VULKAN_INSTANCE (object);
118 
119   if (instance->priv->opened) {
120     if (instance->dbgDestroyDebugReportCallback)
121       instance->dbgDestroyDebugReportCallback (instance->instance,
122           instance->msg_callback, NULL);
123 
124     g_free (instance->physical_devices);
125   }
126   instance->priv->opened = FALSE;
127 
128   if (instance->instance)
129     vkDestroyInstance (instance->instance, NULL);
130   instance->instance = NULL;
131 
132   G_OBJECT_CLASS (parent_class)->finalize (object);
133 }
134 
135 static VkBool32
_gst_vk_debug_callback(VkDebugReportFlagsEXT msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)136 _gst_vk_debug_callback (VkDebugReportFlagsEXT msgFlags,
137     VkDebugReportObjectTypeEXT objType, uint64_t srcObject, size_t location,
138     int32_t msgCode, const char *pLayerPrefix, const char *pMsg,
139     void *pUserData)
140 {
141   if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
142     GST_CAT_ERROR (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
143         msgCode, pMsg);
144   } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
145     GST_CAT_WARNING (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
146         msgCode, pMsg);
147   } else if (msgFlags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
148     GST_CAT_LOG (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
149         msgCode, pMsg);
150   } else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
151     GST_CAT_FIXME (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
152         msgCode, pMsg);
153   } else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
154     GST_CAT_TRACE (GST_VULKAN_DEBUG_CAT, "[%s] Code %d : %s", pLayerPrefix,
155         msgCode, pMsg);
156   } else {
157     return FALSE;
158   }
159 
160   /*
161    * false indicates that layer should not bail-out of an
162    * API call that had validation failures. This may mean that the
163    * app dies inside the driver due to invalid parameter(s).
164    * That's what would happen without validation layers, so we'll
165    * keep that behavior here.
166    */
167   return FALSE;
168 }
169 
170 gboolean
gst_vulkan_instance_open(GstVulkanInstance * instance,GError ** error)171 gst_vulkan_instance_open (GstVulkanInstance * instance, GError ** error)
172 {
173   VkExtensionProperties *instance_extensions;
174   char *extension_names[64];    /* FIXME: make dynamic */
175   VkLayerProperties *instance_layers;
176   uint32_t instance_extension_count = 0;
177   uint32_t enabled_extension_count = 0;
178   uint32_t instance_layer_count = 0;
179   uint32_t enabled_layer_count = 0;
180   gchar **enabled_layers;
181   gboolean have_debug_extension = FALSE;
182   VkResult err;
183 
184   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), FALSE);
185 
186   GST_OBJECT_LOCK (instance);
187   if (instance->priv->opened) {
188     GST_OBJECT_UNLOCK (instance);
189     return TRUE;
190   }
191 
192   /* Look for validation layers */
193   err = vkEnumerateInstanceLayerProperties (&instance_layer_count, NULL);
194   if (gst_vulkan_error_to_g_error (err, error,
195           "vKEnumerateInstanceLayerProperties") < 0)
196     goto error;
197 
198   instance_layers = g_new0 (VkLayerProperties, instance_layer_count);
199   err =
200       vkEnumerateInstanceLayerProperties (&instance_layer_count,
201       instance_layers);
202   if (gst_vulkan_error_to_g_error (err, error,
203           "vKEnumerateInstanceLayerProperties") < 0) {
204     g_free (instance_layers);
205     goto error;
206   }
207 
208   /* TODO: allow outside selection */
209   _check_for_all_layers (G_N_ELEMENTS (instance_validation_layers),
210       instance_validation_layers, instance_layer_count, instance_layers,
211       &enabled_layer_count, &enabled_layers);
212 
213   g_free (instance_layers);
214 
215   err =
216       vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
217       NULL);
218   if (gst_vulkan_error_to_g_error (err, error,
219           "vkEnumerateInstanceExtensionProperties") < 0) {
220     g_strfreev (enabled_layers);
221     goto error;
222   }
223   GST_DEBUG_OBJECT (instance, "Found %u extensions", instance_extension_count);
224 
225   memset (extension_names, 0, sizeof (extension_names));
226   instance_extensions =
227       g_new0 (VkExtensionProperties, instance_extension_count);
228   err =
229       vkEnumerateInstanceExtensionProperties (NULL, &instance_extension_count,
230       instance_extensions);
231   if (gst_vulkan_error_to_g_error (err, error,
232           "vkEnumerateInstanceExtensionProperties") < 0) {
233     g_strfreev (enabled_layers);
234     g_free (instance_extensions);
235     goto error;
236   }
237 
238   {
239     GstVulkanDisplayType display_type;
240     gboolean swapchain_ext_found = FALSE;
241     gboolean winsys_ext_found = FALSE;
242     const gchar *winsys_ext_name;
243     uint32_t i;
244 
245     display_type = gst_vulkan_display_choose_type (instance);
246 
247     winsys_ext_name =
248         gst_vulkan_display_type_to_extension_string (display_type);
249     if (!winsys_ext_name) {
250       GST_WARNING_OBJECT (instance, "No window system extension enabled");
251       winsys_ext_found = TRUE;  /* Don't error out completely */
252     }
253 
254     /* TODO: allow outside selection */
255     for (i = 0; i < instance_extension_count; i++) {
256       GST_TRACE_OBJECT (instance, "checking instance extension %s",
257           instance_extensions[i].extensionName);
258 
259       if (!g_strcmp0 (VK_KHR_SURFACE_EXTENSION_NAME,
260               instance_extensions[i].extensionName)) {
261         swapchain_ext_found = TRUE;
262         extension_names[enabled_extension_count++] =
263             (gchar *) VK_KHR_SURFACE_EXTENSION_NAME;
264       }
265       if (!g_strcmp0 (VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
266               instance_extensions[i].extensionName)) {
267         extension_names[enabled_extension_count++] =
268             (gchar *) VK_EXT_DEBUG_REPORT_EXTENSION_NAME;
269         have_debug_extension = TRUE;
270       }
271       if (!g_strcmp0 (winsys_ext_name, instance_extensions[i].extensionName)) {
272         winsys_ext_found = TRUE;
273         extension_names[enabled_extension_count++] = (gchar *) winsys_ext_name;
274       }
275       g_assert (enabled_extension_count < 64);
276     }
277     if (!swapchain_ext_found) {
278       g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
279           "vkEnumerateInstanceExtensionProperties failed to find the required "
280           "\"" VK_KHR_SURFACE_EXTENSION_NAME "\" extension");
281       g_strfreev (enabled_layers);
282       g_free (instance_extensions);
283       goto error;
284     }
285     if (!winsys_ext_found) {
286       g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
287           "vkEnumerateInstanceExtensionProperties failed to find the required "
288           "\"%s\" window system extension", winsys_ext_name);
289       g_strfreev (enabled_layers);
290       g_free (instance_extensions);
291       goto error;
292     }
293   }
294 
295   {
296     VkApplicationInfo app = { 0, };
297     VkInstanceCreateInfo inst_info = { 0, };
298 
299     app.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
300     app.pNext = NULL;
301     app.pApplicationName = APP_SHORT_NAME;
302     app.applicationVersion = 0;
303     app.pEngineName = APP_SHORT_NAME;
304     app.engineVersion = 0;
305     app.apiVersion = VK_API_VERSION_1_0;
306 
307     inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
308     inst_info.pNext = NULL;
309     inst_info.pApplicationInfo = &app;
310 #if 0
311     inst_info.enabledLayerCount = enabled_layer_count;
312     inst_info.ppEnabledLayerNames = (const char *const *) enabled_layers;
313 #else
314     inst_info.enabledLayerCount = 0;
315     inst_info.ppEnabledLayerNames = NULL;
316 #endif
317     inst_info.enabledExtensionCount = enabled_extension_count;
318     inst_info.ppEnabledExtensionNames = (const char *const *) extension_names;
319 
320     err = vkCreateInstance (&inst_info, NULL, &instance->instance);
321     if (gst_vulkan_error_to_g_error (err, error, "vkCreateInstance") < 0) {
322       g_strfreev (enabled_layers);
323       g_free (instance_extensions);
324       goto error;
325     }
326   }
327 
328   g_free (instance_extensions);
329   g_strfreev (enabled_layers);
330 
331   err =
332       vkEnumeratePhysicalDevices (instance->instance,
333       &instance->n_physical_devices, NULL);
334   if (gst_vulkan_error_to_g_error (err, error,
335           "vkEnumeratePhysicalDevices") < 0)
336     goto error;
337   g_assert (instance->n_physical_devices > 0);
338   instance->physical_devices =
339       g_new0 (VkPhysicalDevice, instance->n_physical_devices);
340   err =
341       vkEnumeratePhysicalDevices (instance->instance,
342       &instance->n_physical_devices, instance->physical_devices);
343   if (gst_vulkan_error_to_g_error (err, error,
344           "vkEnumeratePhysicalDevices") < 0)
345     goto error;
346 
347   if (have_debug_extension) {
348     VkDebugReportCallbackCreateInfoEXT info = { 0, };
349 
350     instance->dbgCreateDebugReportCallback =
351         (PFN_vkCreateDebugReportCallbackEXT)
352         gst_vulkan_instance_get_proc_address (instance,
353         "vkCreateDebugReportCallbackEXT");
354     if (!instance->dbgCreateDebugReportCallback) {
355       g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
356           "Failed to retreive vkCreateDebugReportCallback");
357       goto error;
358     }
359     instance->dbgDestroyDebugReportCallback =
360         (PFN_vkDestroyDebugReportCallbackEXT)
361         gst_vulkan_instance_get_proc_address (instance,
362         "vkDestroyDebugReportCallbackEXT");
363     if (!instance->dbgDestroyDebugReportCallback) {
364       g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
365           "Failed to retreive vkDestroyDebugReportCallback");
366       goto error;
367     }
368     instance->dbgReportMessage = (PFN_vkDebugReportMessageEXT)
369         gst_vulkan_instance_get_proc_address (instance,
370         "vkDebugReportMessageEXT");
371     if (!instance->dbgReportMessage) {
372       g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
373           "Failed to retreive vkDebugReportMessage");
374       goto error;
375     }
376 
377     info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
378     info.pNext = NULL;
379     info.flags =
380         VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
381         VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT |
382         VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
383     info.pfnCallback = (PFN_vkDebugReportCallbackEXT) _gst_vk_debug_callback;
384     info.pUserData = NULL;
385 
386     err =
387         instance->dbgCreateDebugReportCallback (instance->instance, &info, NULL,
388         &instance->msg_callback);
389     if (gst_vulkan_error_to_g_error (err, error,
390             "vkCreateDebugReportCallback") < 0)
391       goto error;
392   }
393 
394   instance->priv->opened = TRUE;
395   GST_OBJECT_UNLOCK (instance);
396 
397   return TRUE;
398 
399 error:
400   {
401     GST_OBJECT_UNLOCK (instance);
402     return FALSE;
403   }
404 }
405 
406 gpointer
gst_vulkan_instance_get_proc_address(GstVulkanInstance * instance,const gchar * name)407 gst_vulkan_instance_get_proc_address (GstVulkanInstance * instance,
408     const gchar * name)
409 {
410   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
411   g_return_val_if_fail (instance->instance != NULL, NULL);
412   g_return_val_if_fail (name != NULL, NULL);
413 
414   GST_TRACE_OBJECT (instance, "%s", name);
415 
416   return vkGetInstanceProcAddr (instance->instance, name);
417 }
418 
419 GstVulkanDevice *
gst_vulkan_instance_create_device(GstVulkanInstance * instance,GError ** error)420 gst_vulkan_instance_create_device (GstVulkanInstance * instance,
421     GError ** error)
422 {
423   GstVulkanDevice *device;
424 
425   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
426 
427   g_signal_emit (instance, gst_vulkan_instance_signals[SIGNAL_CREATE_DEVICE], 0,
428       &device);
429 
430   if (!device)
431     device = gst_vulkan_device_new (instance);
432 
433   if (!gst_vulkan_device_open (device, error)) {
434     gst_object_unref (device);
435     device = NULL;
436   }
437 
438   return device;
439 }
440 
441 /**
442  * gst_context_set_vulkan_instance:
443  * @context: a #GstContext
444  * @instance: a #GstVulkanInstance
445  *
446  * Sets @instance on @context
447  *
448  * Since: 1.10
449  */
450 void
gst_context_set_vulkan_instance(GstContext * context,GstVulkanInstance * instance)451 gst_context_set_vulkan_instance (GstContext * context,
452     GstVulkanInstance * instance)
453 {
454   GstStructure *s;
455 
456   g_return_if_fail (context != NULL);
457   g_return_if_fail (gst_context_is_writable (context));
458 
459   if (instance)
460     GST_CAT_LOG (GST_CAT_CONTEXT,
461         "setting GstVulkanInstance(%" GST_PTR_FORMAT ") on context(%"
462         GST_PTR_FORMAT ")", instance, context);
463 
464   s = gst_context_writable_structure (context);
465   gst_structure_set (s, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR,
466       GST_TYPE_VULKAN_INSTANCE, instance, NULL);
467 }
468 
469 /**
470  * gst_context_get_vulkan_instance:
471  * @context: a #GstContext
472  * @instance: resulting #GstVulkanInstance
473  *
474  * Returns: Whether @instance was in @context
475  *
476  * Since: 1.10
477  */
478 gboolean
gst_context_get_vulkan_instance(GstContext * context,GstVulkanInstance ** instance)479 gst_context_get_vulkan_instance (GstContext * context,
480     GstVulkanInstance ** instance)
481 {
482   const GstStructure *s;
483   gboolean ret;
484 
485   g_return_val_if_fail (instance != NULL, FALSE);
486   g_return_val_if_fail (context != NULL, FALSE);
487 
488   s = gst_context_get_structure (context);
489   ret = gst_structure_get (s, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR,
490       GST_TYPE_VULKAN_INSTANCE, instance, NULL);
491 
492   GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVulkanInstance(%" GST_PTR_FORMAT
493       ") from context(%" GST_PTR_FORMAT ")", *instance, context);
494 
495   return ret;
496 }
497 
498 gboolean
gst_vulkan_instance_handle_context_query(GstElement * element,GstQuery * query,GstVulkanInstance ** instance)499 gst_vulkan_instance_handle_context_query (GstElement * element,
500     GstQuery * query, GstVulkanInstance ** instance)
501 {
502   gboolean res = FALSE;
503   const gchar *context_type;
504   GstContext *context, *old_context;
505 
506   g_return_val_if_fail (element != NULL, FALSE);
507   g_return_val_if_fail (query != NULL, FALSE);
508   g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT, FALSE);
509   g_return_val_if_fail (instance != NULL, FALSE);
510 
511   gst_query_parse_context_type (query, &context_type);
512 
513   if (g_strcmp0 (context_type, GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
514     gst_query_parse_context (query, &old_context);
515 
516     if (old_context)
517       context = gst_context_copy (old_context);
518     else
519       context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
520 
521     gst_context_set_vulkan_instance (context, *instance);
522     gst_query_set_context (query, context);
523     gst_context_unref (context);
524 
525     res = *instance != NULL;
526   }
527 
528   return res;
529 }
530 
531 gboolean
gst_vulkan_instance_run_context_query(GstElement * element,GstVulkanInstance ** instance)532 gst_vulkan_instance_run_context_query (GstElement * element,
533     GstVulkanInstance ** instance)
534 {
535   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
536   g_return_val_if_fail (instance != NULL, FALSE);
537 
538   if (*instance && GST_IS_VULKAN_INSTANCE (*instance))
539     return TRUE;
540 
541   gst_vulkan_global_context_query (element,
542       GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
543 
544   GST_DEBUG_OBJECT (element, "found instance %p", *instance);
545 
546   if (*instance)
547     return TRUE;
548 
549   return FALSE;
550 }
551