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