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 "gstvkutils.h"
29 * @title: Vulkan Utils
30 * @short_description: Vulkan utilities
31 * @see_also: #GstVulkanInstance, #GstVulkanDevice
33 * GstVulkanQueue encapsulates the vulkan command queue.
36 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
39 _init_context_debug (void)
41 #ifndef GST_DISABLE_GST_DEBUG
42 static volatile gsize _init = 0;
44 if (g_once_init_enter (&_init)) {
45 GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
46 g_once_init_leave (&_init, 1);
52 _vk_pad_query (const GValue * item, GValue * value, gpointer user_data)
54 GstPad *pad = g_value_get_object (item);
55 GstQuery *query = user_data;
58 _init_context_debug ();
60 res = gst_pad_peer_query (pad, query);
63 g_value_set_boolean (value, TRUE);
67 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
72 * gst_vulkan_run_query:
73 * @element: a #GstElement
74 * @query: the #GstQuery to perform
75 * @direction: the #GstPadDirection to perform query on
77 * Returns: whether @query was answered successfully
82 gst_vulkan_run_query (GstElement * element, GstQuery * query,
83 GstPadDirection direction)
86 GstIteratorFoldFunction func = _vk_pad_query;
89 g_value_init (&res, G_TYPE_BOOLEAN);
90 g_value_set_boolean (&res, FALSE);
93 if (direction == GST_PAD_SRC)
94 it = gst_element_iterate_src_pads (element);
96 it = gst_element_iterate_sink_pads (element);
98 while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
99 gst_iterator_resync (it);
101 gst_iterator_free (it);
103 return g_value_get_boolean (&res);
107 _vulkan_local_context_query (GstElement * element,
108 const gchar * context_type, gboolean set_context)
113 _init_context_debug ();
115 /* 2a) Query downstream with GST_QUERY_CONTEXT for the context and
116 * check if downstream already has a context of the specific type
117 * 2b) Query upstream as above.
119 query = gst_query_new_context (context_type);
120 if (gst_vulkan_run_query (element, query, GST_PAD_SRC)) {
121 gst_query_parse_context (query, &ctxt);
122 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
123 "found context (%p) in downstream query", ctxt);
125 gst_element_set_context (element, ctxt);
126 } else if (gst_vulkan_run_query (element, query, GST_PAD_SINK)) {
127 gst_query_parse_context (query, &ctxt);
128 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
129 "found context (%p) in upstream query", ctxt);
131 gst_element_set_context (element, ctxt);
133 gst_query_unref (query);
141 * gst_vulkan_global_context_query:
142 * @element: a #GstElement
143 * @context_type: the context type to query for
145 * Performs the steps necessary for executing a context query including
146 * posting a message for the application to respond.
151 gst_vulkan_global_context_query (GstElement * element,
152 const gchar * context_type)
157 if ((query = _vulkan_local_context_query (element, context_type, TRUE))) {
158 gst_query_unref (query);
162 /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
163 * the required context type and afterwards check if a
164 * usable context was set now as in 1). The message could
165 * be handled by the parent bins of the element and the
168 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
169 "posting need context message");
170 msg = gst_message_new_need_context (GST_OBJECT_CAST (element), context_type);
171 gst_element_post_message (element, msg);
174 * Whomever responds to the need-context message performs a
175 * GstElement::set_context() with the required context in which the element
176 * is required to update the display_ptr or call gst_vulkan_handle_set_context().
181 * gst_vulkan_local_context_query:
182 * @element: a #GstElement
183 * @context_type: the context type to query for
185 * Performs the steps necessary for executing a context query between only
186 * other elements in the pipeline
191 gst_vulkan_local_context_query (GstElement * element,
192 const gchar * context_type)
194 return _vulkan_local_context_query (element, context_type, FALSE);
198 _vk_display_context_query (GstElement * element,
199 GstVulkanDisplay ** display_ptr)
201 gst_vulkan_global_context_query (element,
202 GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR);
205 /* 4) Create a context by itself and post a GST_MESSAGE_HAVE_CONTEXT
209 * @element: (transfer none):
210 * @context: (transfer full):
213 _vk_context_propagate (GstElement * element, GstContext * context)
217 _init_context_debug ();
219 gst_element_set_context (element, context);
221 GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
222 "posting have context (%" GST_PTR_FORMAT ") message", context);
223 msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
224 gst_element_post_message (GST_ELEMENT_CAST (element), msg);
228 * gst_vulkan_ensure_element_data:
229 * @element: a #GstElement
230 * @display_ptr: (inout) (optional): the resulting #GstVulkanDisplay
231 * @instance_ptr: (inout): the resulting #GstVulkanInstance
233 * Perform the steps necessary for retrieving a #GstVulkanInstance and
234 * (optionally) an #GstVulkanDisplay from the surrounding elements or from
235 * the application using the #GstContext mechanism.
237 * If the contents of @display_ptr or @instance_ptr are not %NULL, then no
238 * #GstContext query is necessary and no #GstVulkanInstance or #GstVulkanDisplay
239 * retrieval is performed.
241 * Returns: whether a #GstVulkanInstance exists in @instance_ptr and if
242 * @display_ptr is not %NULL, whether a #GstVulkanDisplay exists in
248 gst_vulkan_ensure_element_data (GstElement * element,
249 GstVulkanDisplay ** display_ptr, GstVulkanInstance ** instance_ptr)
251 g_return_val_if_fail (element != NULL, FALSE);
252 g_return_val_if_fail (instance_ptr != NULL, FALSE);
254 /* 1) Check if the element already has a context of the specific
257 if (!*instance_ptr) {
258 GError *error = NULL;
259 GstContext *context = NULL;
261 gst_vulkan_global_context_query (element,
262 GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR);
264 /* Neighbour found and it updated the display */
265 if (!*instance_ptr) {
266 /* If no neighboor, or application not interested, use system default */
267 *instance_ptr = gst_vulkan_instance_new ();
269 context = gst_context_new (GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR, TRUE);
270 gst_context_set_vulkan_instance (context, *instance_ptr);
273 if (!gst_vulkan_instance_open (*instance_ptr, &error)) {
274 GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND,
275 ("Failed to create vulkan instance"), ("%s", error->message));
276 gst_object_unref (*instance_ptr);
277 *instance_ptr = NULL;
278 g_clear_error (&error);
283 _vk_context_propagate (element, context);
286 /* we don't care about a display */
288 return *instance_ptr != NULL;
291 _vk_display_context_query (element, display_ptr);
293 /* Neighbour found and it updated the display */
297 /* instance is required before the display */
298 g_return_val_if_fail (*instance_ptr != NULL, FALSE);
300 /* If no neighboor, or application not interested, use system default */
301 *display_ptr = gst_vulkan_display_new (*instance_ptr);
303 context = gst_context_new (GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR, TRUE);
304 gst_context_set_vulkan_display (context, *display_ptr);
306 _vk_context_propagate (element, context);
310 return *display_ptr != NULL && *instance_ptr != NULL;
314 * gst_vulkan_handle_set_context:
315 * @element: a #GstElement
316 * @context: a #GstContext
317 * @display: (inout) (transfer full) (optional): location of a #GstVulkanDisplay
318 * @instance: (inout) (transfer full): location of a #GstVulkanInstance
320 * Helper function for implementing #GstElementClass.set_context() in
321 * Vulkan capable elements.
323 * Retrieve's the #GstVulkanDisplay or #GstVulkanInstance in @context and places
324 * the result in @display or @instance respectively.
326 * Returns: whether the @display or @instance could be set successfully
331 gst_vulkan_handle_set_context (GstElement * element, GstContext * context,
332 GstVulkanDisplay ** display, GstVulkanInstance ** instance)
334 GstVulkanDisplay *display_replacement = NULL;
335 GstVulkanInstance *instance_replacement = NULL;
336 const gchar *context_type;
338 g_return_val_if_fail (instance != NULL, FALSE);
343 context_type = gst_context_get_context_type (context);
346 && g_strcmp0 (context_type, GST_VULKAN_DISPLAY_CONTEXT_TYPE_STR) == 0) {
347 if (!gst_context_get_vulkan_display (context, &display_replacement)) {
348 GST_WARNING_OBJECT (element, "Failed to get display from context");
351 } else if (g_strcmp0 (context_type,
352 GST_VULKAN_INSTANCE_CONTEXT_TYPE_STR) == 0) {
353 if (!gst_context_get_vulkan_instance (context, &instance_replacement)) {
354 GST_WARNING_OBJECT (element, "Failed to get instance from context");
359 if (display_replacement) {
360 GstVulkanDisplay *old = *display;
361 *display = display_replacement;
364 gst_object_unref (old);
367 if (instance_replacement) {
368 GstVulkanInstance *old = *instance;
369 *instance = instance_replacement;
372 gst_object_unref (old);
379 * gst_vulkan_handle_context_query:
380 * @element: a #GstElement
381 * @query: a #GstQuery of type %GST_QUERY_CONTEXT
382 * @display: (transfer none) (nullable): a #GstVulkanDisplay
383 * @instance: (transfer none) (nullable): a #GstVulkanInstance
384 * @device: (transfer none) (nullable): a #GstVulkanInstance
386 * Returns: Whether the @query was successfully responded to from the passed
387 * @display, @instance, and @device.
392 gst_vulkan_handle_context_query (GstElement * element, GstQuery * query,
393 GstVulkanDisplay * display, GstVulkanInstance * instance,
394 GstVulkanDevice * device)
396 if (gst_vulkan_display_handle_context_query (element, query, display))
398 if (gst_vulkan_instance_handle_context_query (element, query, instance))
400 if (gst_vulkan_device_handle_context_query (element, query, device))
407 fill_vulkan_image_view_info (VkImage image, VkFormat format,
408 VkImageViewCreateInfo * info)
411 *info = (VkImageViewCreateInfo) {
412 .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
416 .viewType = VK_IMAGE_VIEW_TYPE_2D,
418 .components = (VkComponentMapping) {
419 VK_COMPONENT_SWIZZLE_R,
420 VK_COMPONENT_SWIZZLE_G,
421 VK_COMPONENT_SWIZZLE_B,
422 VK_COMPONENT_SWIZZLE_A
424 .subresourceRange = (VkImageSubresourceRange) {
425 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
436 find_compatible_view (GstVulkanImageView * view, VkImageViewCreateInfo * info)
438 return view->create_info.image == info->image
439 && view->create_info.format == info->format
440 && view->create_info.viewType == info->viewType
441 && view->create_info.flags == info->flags
442 && view->create_info.components.r == info->components.r
443 && view->create_info.components.g == info->components.g
444 && view->create_info.components.b == info->components.b
445 && view->create_info.components.a == info->components.a
446 && view->create_info.subresourceRange.aspectMask ==
447 info->subresourceRange.aspectMask
448 && view->create_info.subresourceRange.baseMipLevel ==
449 info->subresourceRange.baseMipLevel
450 && view->create_info.subresourceRange.levelCount ==
451 info->subresourceRange.levelCount
452 && view->create_info.subresourceRange.levelCount ==
453 info->subresourceRange.levelCount
454 && view->create_info.subresourceRange.baseArrayLayer ==
455 info->subresourceRange.baseArrayLayer
456 && view->create_info.subresourceRange.layerCount ==
457 info->subresourceRange.layerCount;
461 gst_vulkan_get_or_create_image_view (GstVulkanImageMemory * image)
463 VkImageViewCreateInfo create_info;
464 GstVulkanImageView *ret = NULL;
466 fill_vulkan_image_view_info (image->image, image->create_info.format,
469 ret = gst_vulkan_image_memory_find_view (image,
470 (GstVulkanImageMemoryFindViewFunc) find_compatible_view, &create_info);
472 ret = gst_vulkan_image_view_new (image, &create_info);
473 gst_vulkan_image_memory_add_view (image, ret);
479 #define SPIRV_MAGIC_NUMBER_NE 0x07230203
480 #define SPIRV_MAGIC_NUMBER_OE 0x03022307
483 gst_vulkan_create_shader (GstVulkanDevice * device, gchar * code, gsize size,
486 VkShaderModule shader;
490 VkShaderModuleCreateInfo info = {
491 .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
495 .pCode = (guint32 *) code
499 guint32 *new_code = NULL;
501 g_return_val_if_fail (size >= 4, VK_NULL_HANDLE);
502 g_return_val_if_fail (size % 4 == 0, VK_NULL_HANDLE);
504 first_word = code[0] | code[1] << 8 | code[2] << 16 | code[3] << 24;
505 g_return_val_if_fail (first_word == SPIRV_MAGIC_NUMBER_NE
506 || first_word == SPIRV_MAGIC_NUMBER_OE, VK_NULL_HANDLE);
507 if (first_word == SPIRV_MAGIC_NUMBER_OE) {
508 /* endianness swap... */
509 guint32 *old_code = (guint32 *) code;
512 GST_DEBUG ("performaing endianness conversion on spirv shader of size %"
513 G_GSIZE_FORMAT, size);
514 new_code = g_new0 (guint32, size / 4);
516 for (i = 0; i < size / 4; i++) {
517 guint32 old = old_code[i];
520 new |= (old & 0xff) << 24;
521 new |= (old & 0xff00) << 8;
522 new |= (old & 0xff0000) >> 8;
523 new |= (old & 0xff000000) >> 24;
527 first_word = ((guint32 *) new_code)[0];
528 g_assert (first_word == SPIRV_MAGIC_NUMBER_NE);
530 info.pCode = new_code;
533 res = vkCreateShaderModule (device->device, &info, NULL, &shader);
535 if (gst_vulkan_error_to_g_error (res, error, "VkCreateShaderModule") < 0)
538 return gst_vulkan_handle_new_wrapped (device, GST_VULKAN_HANDLE_TYPE_SHADER,
539 (GstVulkanHandleTypedef) shader, gst_vulkan_handle_free_shader, NULL);