gst: don't use volatile to mean atomic
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkdevice.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 "gstvkdevice.h"
26 #include "gstvkdebug.h"
27
28 #include <string.h>
29
30 /**
31  * SECTION:vkdevice
32  * @title: GstVulkanDevice
33  * @short_description: Vulkan device
34  * @see_also: #GstVulkanPhysicalDevice, #GstVulkanInstance
35  *
36  * A #GstVulkanDevice encapsulates a VkDevice
37  */
38
39 #define GST_CAT_DEFAULT gst_vulkan_device_debug
40 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
41 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
42
43 #define GET_PRIV(o) (gst_vulkan_device_get_instance_private (o))
44
45 enum
46 {
47   PROP_0,
48   PROP_INSTANCE,
49   PROP_PHYSICAL_DEVICE,
50 };
51
52 static void gst_vulkan_device_dispose (GObject * object);
53 static void gst_vulkan_device_finalize (GObject * object);
54
55 struct _GstVulkanDevicePrivate
56 {
57   GPtrArray *enabled_layers;
58   GPtrArray *enabled_extensions;
59
60   gboolean opened;
61   guint queue_family_id;
62   guint n_queues;
63
64   GstVulkanFenceCache *fence_cache;
65 };
66
67 static void
68 _init_debug (void)
69 {
70   static gsize init;
71
72   if (g_once_init_enter (&init)) {
73     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "vulkandevice", 0,
74         "Vulkan Device");
75     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
76     g_once_init_leave (&init, 1);
77   }
78 }
79
80 #define gst_vulkan_device_parent_class parent_class
81 G_DEFINE_TYPE_WITH_CODE (GstVulkanDevice, gst_vulkan_device, GST_TYPE_OBJECT,
82     G_ADD_PRIVATE (GstVulkanDevice);
83     _init_debug ());
84
85 /**
86  * gst_vulkan_device_new:
87  * @physical_device: the associated #GstVulkanPhysicalDevice
88  *
89  * Returns: (transfer full): a new #GstVulkanDevice
90  *
91  * Since: 1.18
92  */
93 GstVulkanDevice *
94 gst_vulkan_device_new (GstVulkanPhysicalDevice * physical_device)
95 {
96   GstVulkanDevice *device;
97
98   g_return_val_if_fail (GST_IS_VULKAN_PHYSICAL_DEVICE (physical_device), NULL);
99
100   device = g_object_new (GST_TYPE_VULKAN_DEVICE, "physical-device",
101       physical_device, NULL);
102   gst_object_ref_sink (device);
103
104   return device;
105 }
106
107 /**
108  * gst_vulkan_device_new_with_index:
109  * @instance: the associated #GstVulkanInstance
110  * @device_index: the device index to create the new #GstVulkanDevice from
111  *
112  * Returns: (transfer full): a new #GstVulkanDevice
113  *
114  * Since: 1.18
115  */
116 GstVulkanDevice *
117 gst_vulkan_device_new_with_index (GstVulkanInstance * instance,
118     guint device_index)
119 {
120   GstVulkanPhysicalDevice *physical;
121   GstVulkanDevice *device;
122
123   g_return_val_if_fail (GST_IS_VULKAN_INSTANCE (instance), NULL);
124
125   physical = gst_vulkan_physical_device_new (instance, device_index);
126   device = gst_vulkan_device_new (physical);
127   gst_object_unref (physical);
128   return device;
129 }
130
131 static void
132 gst_vulkan_device_set_property (GObject * object, guint prop_id,
133     const GValue * value, GParamSpec * pspec)
134 {
135   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
136
137   switch (prop_id) {
138     case PROP_PHYSICAL_DEVICE:
139       device->physical_device = g_value_dup_object (value);
140       break;
141     default:
142       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
143       break;
144   }
145 }
146
147 static void
148 gst_vulkan_device_get_property (GObject * object, guint prop_id,
149     GValue * value, GParamSpec * pspec)
150 {
151   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
152
153   switch (prop_id) {
154     case PROP_INSTANCE:
155       g_value_set_object (value, device->instance);
156       break;
157     case PROP_PHYSICAL_DEVICE:
158       g_value_set_object (value, device->physical_device);
159       break;
160     default:
161       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
162       break;
163   }
164 }
165
166 static void
167 gst_vulkan_device_init (GstVulkanDevice * device)
168 {
169   GstVulkanDevicePrivate *priv = GET_PRIV (device);
170
171   priv->enabled_layers = g_ptr_array_new_with_free_func (g_free);
172   priv->enabled_extensions = g_ptr_array_new_with_free_func (g_free);
173 }
174
175 static void
176 gst_vulkan_device_constructed (GObject * object)
177 {
178   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
179
180   g_object_get (device->physical_device, "instance", &device->instance, NULL);
181
182   /* by default allow vkswapper to work for rendering to an output window.
183    * Ignore the failure if the extension does not exist. */
184   gst_vulkan_device_enable_extension (device, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
185
186   G_OBJECT_CLASS (parent_class)->constructed (object);
187 }
188
189 static void
190 gst_vulkan_device_class_init (GstVulkanDeviceClass * device_class)
191 {
192   GObjectClass *gobject_class = (GObjectClass *) device_class;
193
194   gobject_class->set_property = gst_vulkan_device_set_property;
195   gobject_class->get_property = gst_vulkan_device_get_property;
196   gobject_class->finalize = gst_vulkan_device_finalize;
197   gobject_class->dispose = gst_vulkan_device_dispose;
198   gobject_class->constructed = gst_vulkan_device_constructed;
199
200   g_object_class_install_property (gobject_class, PROP_INSTANCE,
201       g_param_spec_object ("instance", "Instance",
202           "Associated Vulkan Instance",
203           GST_TYPE_VULKAN_INSTANCE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
204   g_object_class_install_property (gobject_class, PROP_PHYSICAL_DEVICE,
205       g_param_spec_object ("physical-device", "Physical Device",
206           "Associated Vulkan Physical Device",
207           GST_TYPE_VULKAN_PHYSICAL_DEVICE,
208           G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
209 }
210
211 static void
212 gst_vulkan_device_dispose (GObject * object)
213 {
214   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
215   GstVulkanDevicePrivate *priv = GET_PRIV (device);
216
217   if (priv->fence_cache) {
218     /* clear any outstanding fences */
219     g_object_run_dispose (G_OBJECT (priv->fence_cache));
220
221     /* don't double free this device */
222     priv->fence_cache->parent.device = NULL;
223   }
224   gst_clear_object (&priv->fence_cache);
225
226   G_OBJECT_CLASS (parent_class)->dispose (object);
227 }
228
229 static void
230 gst_vulkan_device_finalize (GObject * object)
231 {
232   GstVulkanDevice *device = GST_VULKAN_DEVICE (object);
233   GstVulkanDevicePrivate *priv = GET_PRIV (device);
234
235   if (device->device) {
236     vkDeviceWaitIdle (device->device);
237     vkDestroyDevice (device->device, NULL);
238   }
239   device->device = VK_NULL_HANDLE;
240
241   gst_clear_object (&device->physical_device);
242   gst_clear_object (&device->instance);
243
244   g_ptr_array_unref (priv->enabled_layers);
245   priv->enabled_layers = NULL;
246
247   g_ptr_array_unref (priv->enabled_extensions);
248   priv->enabled_extensions = NULL;
249
250   G_OBJECT_CLASS (parent_class)->finalize (object);
251 }
252
253 /**
254  * gst_vulkan_device_open:
255  * @device: a #GstVulkanDevice
256  * @error: a #GError
257  *
258  * Attempts to create the internal `VkDevice` object.
259  *
260  * Returns: whether a vulkan device could be created
261  *
262  * Since: 1.18
263  */
264 gboolean
265 gst_vulkan_device_open (GstVulkanDevice * device, GError ** error)
266 {
267   GstVulkanDevicePrivate *priv = GET_PRIV (device);
268   VkPhysicalDevice gpu;
269   VkResult err;
270   guint i;
271
272   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
273
274   GST_OBJECT_LOCK (device);
275
276   if (priv->opened) {
277     GST_OBJECT_UNLOCK (device);
278     return TRUE;
279   }
280
281   gpu = gst_vulkan_device_get_physical_device (device);
282
283   /* FIXME: allow overriding/selecting */
284   for (i = 0; i < device->physical_device->n_queue_families; i++) {
285     if (device->physical_device->
286         queue_family_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)
287       break;
288   }
289   if (i >= device->physical_device->n_queue_families) {
290     g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_INITIALIZATION_FAILED,
291         "Failed to find a compatible queue family");
292     goto error;
293   }
294   priv->queue_family_id = i;
295   priv->n_queues = 1;
296
297   GST_INFO_OBJECT (device, "Creating a device from physical %" GST_PTR_FORMAT
298       " with %u layers and %u extensions", device->physical_device,
299       priv->enabled_layers->len, priv->enabled_extensions->len);
300
301   for (i = 0; i < priv->enabled_layers->len; i++)
302     GST_DEBUG_OBJECT (device, "layer %u: %s", i,
303         (gchar *) g_ptr_array_index (priv->enabled_layers, i));
304   for (i = 0; i < priv->enabled_extensions->len; i++)
305     GST_DEBUG_OBJECT (device, "extension %u: %s", i,
306         (gchar *) g_ptr_array_index (priv->enabled_extensions, i));
307
308   {
309     VkDeviceQueueCreateInfo queue_info = { 0, };
310     VkDeviceCreateInfo device_info = { 0, };
311     gfloat queue_priority = 0.5;
312
313     queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
314     queue_info.pNext = NULL;
315     queue_info.queueFamilyIndex = priv->queue_family_id;
316     queue_info.queueCount = priv->n_queues;
317     queue_info.pQueuePriorities = &queue_priority;
318
319     device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
320     device_info.pNext = NULL;
321     device_info.queueCreateInfoCount = 1;
322     device_info.pQueueCreateInfos = &queue_info;
323     device_info.enabledLayerCount = priv->enabled_layers->len;
324     device_info.ppEnabledLayerNames =
325         (const char *const *) priv->enabled_layers->pdata;
326     device_info.enabledExtensionCount = priv->enabled_extensions->len;
327     device_info.ppEnabledExtensionNames =
328         (const char *const *) priv->enabled_extensions->pdata;
329     device_info.pEnabledFeatures = NULL;
330
331     err = vkCreateDevice (gpu, &device_info, NULL, &device->device);
332     if (gst_vulkan_error_to_g_error (err, error, "vkCreateDevice") < 0) {
333       goto error;
334     }
335   }
336
337   priv->fence_cache = gst_vulkan_fence_cache_new (device);
338   /* avoid reference loops between us and the fence cache */
339   gst_object_unref (device);
340
341   priv->opened = TRUE;
342   GST_OBJECT_UNLOCK (device);
343   return TRUE;
344
345 error:
346   {
347     GST_OBJECT_UNLOCK (device);
348     return FALSE;
349   }
350 }
351
352 /**
353  * gst_vulkan_device_get_queue:
354  * @device: a #GstVulkanDevice
355  * @queue_family: a queue family to retrieve
356  * @queue_i: index of the family to retrieve
357  *
358  * Returns: (transfer full): a new #GstVulkanQueue
359  *
360  * Since: 1.18
361  */
362 GstVulkanQueue *
363 gst_vulkan_device_get_queue (GstVulkanDevice * device, guint32 queue_family,
364     guint32 queue_i)
365 {
366   GstVulkanDevicePrivate *priv = GET_PRIV (device);
367   GstVulkanQueue *ret;
368
369   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
370   g_return_val_if_fail (device->device != NULL, NULL);
371   g_return_val_if_fail (priv->opened, NULL);
372   g_return_val_if_fail (queue_family < priv->n_queues, NULL);
373   g_return_val_if_fail (queue_i <
374       device->physical_device->queue_family_props[queue_family].queueCount,
375       NULL);
376
377   ret = g_object_new (GST_TYPE_VULKAN_QUEUE, NULL);
378   gst_object_ref_sink (ret);
379   ret->device = gst_object_ref (device);
380   ret->family = queue_family;
381   ret->index = queue_i;
382
383   vkGetDeviceQueue (device->device, queue_family, queue_i, &ret->queue);
384
385   return ret;
386 }
387
388 /**
389  * gst_vulkan_device_foreach_queue:
390  * @device: a #GstVulkanDevice
391  * @func: (scope call): a #GstVulkanDeviceForEachQueueFunc to run for each #GstVulkanQueue
392  * @user_data: (closure func): user data to pass to each call of @func
393  *
394  * Iterate over each queue family available on #GstVulkanDevice
395  *
396  * Since: 1.18
397  */
398 void
399 gst_vulkan_device_foreach_queue (GstVulkanDevice * device,
400     GstVulkanDeviceForEachQueueFunc func, gpointer user_data)
401 {
402   GstVulkanDevicePrivate *priv = GET_PRIV (device);
403   gboolean done = FALSE;
404   guint i;
405
406   for (i = 0; i < priv->n_queues; i++) {
407     GstVulkanQueue *queue =
408         gst_vulkan_device_get_queue (device, priv->queue_family_id, i);
409
410     if (!func (device, queue, user_data))
411       done = TRUE;
412
413     gst_object_unref (queue);
414
415     if (done)
416       break;
417   }
418 }
419
420 /**
421  * gst_vulkan_device_get_proc_address:
422  * @device: a #GstVulkanDevice
423  * @name: name of the function to retrieve
424  *
425  * Performs `vkGetDeviceProcAddr()` with @device and @name
426  *
427  * Returns: the function pointer for @name or %NULL
428  *
429  * Since: 1.18
430  */
431 gpointer
432 gst_vulkan_device_get_proc_address (GstVulkanDevice * device,
433     const gchar * name)
434 {
435   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
436   g_return_val_if_fail (device->device != NULL, NULL);
437   g_return_val_if_fail (name != NULL, NULL);
438
439   GST_TRACE_OBJECT (device, "%s", name);
440
441   return vkGetDeviceProcAddr (device->device, name);
442 }
443
444 /**
445  * gst_vulkan_device_get_instance:
446  * @device: a #GstVulkanDevice
447  *
448  * Returns: (transfer full): the #GstVulkanInstance used to create this @device
449  *
450  * Since: 1.18
451  */
452 GstVulkanInstance *
453 gst_vulkan_device_get_instance (GstVulkanDevice * device)
454 {
455   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
456
457   return device->instance ? gst_object_ref (device->instance) : NULL;
458 }
459
460 /**
461  * gst_vulkan_device_get_physical_device: (skip)
462  * @device: a #GstVulkanDevice
463  *
464  * Returns: The VkPhysicalDevice used to create @device
465  *
466  * Since: 1.18
467  */
468 VkPhysicalDevice
469 gst_vulkan_device_get_physical_device (GstVulkanDevice * device)
470 {
471   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
472
473   return gst_vulkan_physical_device_get_handle (device->physical_device);
474 }
475
476 /**
477  * gst_context_set_vulkan_device:
478  * @context: a #GstContext
479  * @device: a #GstVulkanDevice
480  *
481  * Sets @device on @context
482  *
483  * Since: 1.18
484  */
485 void
486 gst_context_set_vulkan_device (GstContext * context, GstVulkanDevice * device)
487 {
488   GstStructure *s;
489
490   g_return_if_fail (context != NULL);
491   g_return_if_fail (gst_context_is_writable (context));
492
493   if (device)
494     GST_CAT_LOG (GST_CAT_CONTEXT,
495         "setting GstVulkanDevice(%" GST_PTR_FORMAT ") on context(%"
496         GST_PTR_FORMAT ")", device, context);
497
498   s = gst_context_writable_structure (context);
499   gst_structure_set (s, GST_VULKAN_DEVICE_CONTEXT_TYPE_STR,
500       GST_TYPE_VULKAN_DEVICE, device, NULL);
501 }
502
503 /**
504  * gst_context_get_vulkan_device:
505  * @context: a #GstContext
506  * @device: resulting #GstVulkanDevice
507  *
508  * Returns: Whether @device was in @context
509  *
510  * Since: 1.18
511  */
512 gboolean
513 gst_context_get_vulkan_device (GstContext * context, GstVulkanDevice ** device)
514 {
515   const GstStructure *s;
516   gboolean ret;
517
518   g_return_val_if_fail (device != NULL, FALSE);
519   g_return_val_if_fail (context != NULL, FALSE);
520
521   s = gst_context_get_structure (context);
522   ret = gst_structure_get (s, GST_VULKAN_DEVICE_CONTEXT_TYPE_STR,
523       GST_TYPE_VULKAN_DEVICE, device, NULL);
524
525   GST_CAT_LOG (GST_CAT_CONTEXT, "got GstVulkanDevice(%" GST_PTR_FORMAT
526       ") from context(%" GST_PTR_FORMAT ")", *device, context);
527
528   return ret;
529 }
530
531 /**
532  * gst_vulkan_device_handle_context_query:
533  * @element: a #GstElement
534  * @query: a #GstQuery of type #GST_QUERY_CONTEXT
535  * @device: the #GstVulkanDevice
536  *
537  * If a #GstVulkanDevice is requested in @query, sets @device as the reply.
538  *
539  * Intended for use with element query handlers to respond to #GST_QUERY_CONTEXT
540  * for a #GstVulkanDevice.
541  *
542  * Returns: whether @query was responded to with @device
543  *
544  * Since: 1.18
545  */
546 gboolean
547 gst_vulkan_device_handle_context_query (GstElement * element, GstQuery * query,
548     GstVulkanDevice * device)
549 {
550   gboolean res = FALSE;
551   const gchar *context_type;
552   GstContext *context, *old_context;
553
554   g_return_val_if_fail (element != NULL, FALSE);
555   g_return_val_if_fail (query != NULL, FALSE);
556   g_return_val_if_fail (GST_QUERY_TYPE (query) == GST_QUERY_CONTEXT, FALSE);
557
558   if (!device)
559     return FALSE;
560
561   gst_query_parse_context_type (query, &context_type);
562
563   if (g_strcmp0 (context_type, GST_VULKAN_DEVICE_CONTEXT_TYPE_STR) == 0) {
564     gst_query_parse_context (query, &old_context);
565
566     if (old_context)
567       context = gst_context_copy (old_context);
568     else
569       context = gst_context_new (GST_VULKAN_DEVICE_CONTEXT_TYPE_STR, TRUE);
570
571     gst_context_set_vulkan_device (context, device);
572     gst_query_set_context (query, context);
573     gst_context_unref (context);
574
575     res = device != NULL;
576   }
577
578   return res;
579 }
580
581 /**
582  * gst_vulkan_device_run_context_query:
583  * @element: a #GstElement
584  * @device: (inout): a #GstVulkanDevice
585  *
586  * Attempt to retrieve a #GstVulkanDevice using #GST_QUERY_CONTEXT from the
587  * surrounding elements of @element.
588  *
589  * Returns: whether @device contains a valid #GstVulkanDevice
590  *
591  * Since: 1.18
592  */
593 gboolean
594 gst_vulkan_device_run_context_query (GstElement * element,
595     GstVulkanDevice ** device)
596 {
597   GstQuery *query;
598
599   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
600   g_return_val_if_fail (device != NULL, FALSE);
601
602   _init_debug ();
603
604   if (*device && GST_IS_VULKAN_DEVICE (*device))
605     return TRUE;
606
607   if ((query =
608           gst_vulkan_local_context_query (element,
609               GST_VULKAN_DEVICE_CONTEXT_TYPE_STR))) {
610     GstContext *context;
611
612     gst_query_parse_context (query, &context);
613     if (context)
614       gst_context_get_vulkan_device (context, device);
615
616     gst_query_unref (query);
617   }
618
619   GST_DEBUG_OBJECT (element, "found device %p", *device);
620
621   if (*device)
622     return TRUE;
623
624   return FALSE;
625 }
626
627 /**
628  * gst_vulkan_device_create_fence:
629  * @device: a #GstVulkanDevice
630  * @error: a #GError to fill on failure
631  *
632  * Returns: a new #GstVulkanFence or %NULL
633  *
634  * Since: 1.18
635  */
636 GstVulkanFence *
637 gst_vulkan_device_create_fence (GstVulkanDevice * device, GError ** error)
638 {
639   GstVulkanDevicePrivate *priv;
640
641   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), NULL);
642   priv = GET_PRIV (device);
643
644   return gst_vulkan_fence_cache_acquire (priv->fence_cache, error);
645 }
646
647 /* reimplement a specfic case of g_ptr_array_find_with_equal_func as that
648  * requires Glib 2.54 */
649 static gboolean
650 ptr_array_find_string (GPtrArray * array, const gchar * str, guint * index)
651 {
652   guint i;
653
654   for (i = 0; i < array->len; i++) {
655     gchar *val = (gchar *) g_ptr_array_index (array, i);
656     if (g_strcmp0 (val, str) == 0) {
657       if (index)
658         *index = i;
659       return TRUE;
660     }
661   }
662
663   return FALSE;
664 }
665
666 static gboolean
667 gst_vulkan_device_is_extension_enabled_unlocked (GstVulkanDevice * device,
668     const gchar * name, guint * index)
669 {
670   GstVulkanDevicePrivate *priv = GET_PRIV (device);
671
672   return ptr_array_find_string (priv->enabled_extensions, name, index);
673 }
674
675 /**
676  * gst_vulkan_device_is_extension_enabled:
677  * @device: a # GstVulkanDevice
678  * @name: extension name
679  *
680  * Returns: whether extension @name is enabled
681  *
682  * Since: 1.18
683  */
684 gboolean
685 gst_vulkan_device_is_extension_enabled (GstVulkanDevice * device,
686     const gchar * name)
687 {
688   gboolean ret;
689
690   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
691   g_return_val_if_fail (name != NULL, FALSE);
692
693   GST_OBJECT_LOCK (device);
694   ret = gst_vulkan_device_is_extension_enabled_unlocked (device, name, NULL);
695   GST_OBJECT_UNLOCK (device);
696
697   return ret;
698 }
699
700 static gboolean
701 gst_vulkan_device_enable_extension_unlocked (GstVulkanDevice * device,
702     const gchar * name)
703 {
704   GstVulkanDevicePrivate *priv = GET_PRIV (device);
705
706   if (gst_vulkan_device_is_extension_enabled_unlocked (device, name, NULL))
707     /* extension is already enabled */
708     return TRUE;
709
710   if (!gst_vulkan_physical_device_get_extension_info (device->physical_device,
711           name, NULL))
712     return FALSE;
713
714   g_ptr_array_add (priv->enabled_extensions, g_strdup (name));
715
716   return TRUE;
717 }
718
719 /**
720  * gst_vulkan_device_enable_extension:
721  * @device: a #GstVulkanDevice
722  * @name: extension name to enable
723  *
724  * Enable an Vulkan extension by @name.  Enabling an extension will
725  * only have an effect before the call to gst_vulkan_device_open().
726  *
727  * Returns: whether the Vulkan extension could be enabled.
728  *
729  * Since: 1.18
730  */
731 gboolean
732 gst_vulkan_device_enable_extension (GstVulkanDevice * device,
733     const gchar * name)
734 {
735   gboolean ret;
736
737   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
738   g_return_val_if_fail (name != NULL, FALSE);
739
740   GST_OBJECT_LOCK (device);
741   ret = gst_vulkan_device_enable_extension_unlocked (device, name);
742   GST_OBJECT_UNLOCK (device);
743
744   return ret;
745 }
746
747 static gboolean
748 gst_vulkan_device_disable_extension_unlocked (GstVulkanDevice * device,
749     const gchar * name)
750 {
751   GstVulkanDevicePrivate *priv = GET_PRIV (device);
752   guint i;
753
754   if (!gst_vulkan_physical_device_get_extension_info (device->physical_device,
755           name, NULL))
756     return FALSE;
757
758   if (!gst_vulkan_device_is_extension_enabled_unlocked (device, name, &i))
759     /* extension is already disabled */
760     return TRUE;
761
762   g_ptr_array_remove_index_fast (priv->enabled_extensions, i);
763
764   return TRUE;
765 }
766
767 /**
768  * gst_vulkan_device_disable_extension:
769  * @device: a #GstVulkanDevice
770  * @name: extension name to enable
771  *
772  * Disable an Vulkan extension by @name.  Disabling an extension will only have
773  * an effect before the call to gst_vulkan_device_open().
774  *
775  * Returns: whether the Vulkan extension could be disabled.
776  *
777  * Since: 1.18
778  */
779 gboolean
780 gst_vulkan_device_disable_extension (GstVulkanDevice * device,
781     const gchar * name)
782 {
783   gboolean ret;
784
785   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
786   g_return_val_if_fail (name != NULL, FALSE);
787
788   GST_OBJECT_LOCK (device);
789   ret = gst_vulkan_device_disable_extension_unlocked (device, name);
790   GST_OBJECT_UNLOCK (device);
791
792   return ret;
793 }
794
795 static gboolean
796 gst_vulkan_device_is_layer_enabled_unlocked (GstVulkanDevice * device,
797     const gchar * name)
798 {
799   GstVulkanDevicePrivate *priv = GET_PRIV (device);
800
801   return ptr_array_find_string (priv->enabled_layers, name, NULL);
802 }
803
804 /**
805  * gst_vulkan_device_is_layer_enabled:
806  * @device: a # GstVulkanDevice
807  * @name: layer name
808  *
809  * Returns: whether layer @name is enabled
810  *
811  * Since: 1.18
812  */
813 gboolean
814 gst_vulkan_device_is_layer_enabled (GstVulkanDevice * device,
815     const gchar * name)
816 {
817   gboolean ret;
818
819   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
820   g_return_val_if_fail (name != NULL, FALSE);
821
822   GST_OBJECT_LOCK (device);
823   ret = gst_vulkan_device_is_layer_enabled_unlocked (device, name);
824   GST_OBJECT_UNLOCK (device);
825
826   return ret;
827 }
828
829 static gboolean
830 gst_vulkan_device_enable_layer_unlocked (GstVulkanDevice * device,
831     const gchar * name)
832 {
833   GstVulkanDevicePrivate *priv = GET_PRIV (device);
834
835   if (gst_vulkan_device_is_layer_enabled_unlocked (device, name))
836     /* layer is already enabled */
837     return TRUE;
838
839   if (!gst_vulkan_physical_device_get_layer_info (device->physical_device,
840           name, NULL, NULL, NULL))
841     return FALSE;
842
843   g_ptr_array_add (priv->enabled_layers, g_strdup (name));
844
845   return TRUE;
846 }
847
848 /**
849  * gst_vulkan_device_enable_layer:
850  * @device: a #GstVulkanDevice
851  * @name: layer name to enable
852  *
853  * Enable an Vulkan layer by @name.  Enabling a layer will
854  * only have an effect before the call to gst_vulkan_device_open().
855  *
856  * Returns: whether the Vulkan layer could be enabled.
857  *
858  * Since: 1.18
859  */
860 gboolean
861 gst_vulkan_device_enable_layer (GstVulkanDevice * device, const gchar * name)
862 {
863   gboolean ret;
864
865   g_return_val_if_fail (GST_IS_VULKAN_DEVICE (device), FALSE);
866   g_return_val_if_fail (name != NULL, FALSE);
867
868   GST_OBJECT_LOCK (device);
869   ret = gst_vulkan_device_enable_layer_unlocked (device, name);
870   GST_OBJECT_UNLOCK (device);
871
872   return ret;
873 }