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.
27 #include "gstvkswapper.h"
29 #define GST_CAT_DEFAULT gst_vulkan_swapper_debug
30 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
32 #define RENDER_GET_LOCK(o) &(GST_VULKAN_SWAPPER (o)->priv->render_lock)
33 #define RENDER_LOCK(o) g_mutex_lock (RENDER_GET_LOCK(o));
34 #define RENDER_UNLOCK(o) g_mutex_unlock (RENDER_GET_LOCK(o));
36 struct _GstVulkanSwapperPrivate
40 VkSurfaceCapabilitiesKHR surf_props;
41 VkSurfaceFormatKHR *surf_formats;
42 guint32 n_surf_formats;
43 VkPresentModeKHR *surf_present_modes;
44 guint32 n_surf_present_modes;
46 VkSwapchainKHR swap_chain;
47 GstVulkanImageMemory **swap_chain_images;
48 guint32 n_swap_chain_images;
53 PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
54 PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
55 GetPhysicalDeviceSurfaceCapabilitiesKHR;
56 PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR;
57 PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
58 GetPhysicalDeviceSurfacePresentModesKHR;
59 PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
60 PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
61 PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
62 PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
63 PFN_vkQueuePresentKHR QueuePresentKHR;
64 PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
67 /* runtime variables */
69 GstBuffer *current_buffer;
70 gboolean any_current_extent;
78 gboolean force_aspect_ratio;
84 GstVulkanTrashList *trash_list;
86 /* source sizes accounting for all aspect ratios */
94 PROP_FORCE_ASPECT_RATIO,
95 PROP_PIXEL_ASPECT_RATIO,
98 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
99 #define DEFAULT_PIXEL_ASPECT_RATIO_N 0
100 #define DEFAULT_PIXEL_ASPECT_RATIO_D 1
102 #define gst_vulkan_swapper_parent_class parent_class
103 G_DEFINE_TYPE_WITH_CODE (GstVulkanSwapper, gst_vulkan_swapper,
104 GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanSwapper)
105 GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
106 "vulkanswapper", 0, "Vulkan Swapper"));
108 static void _on_window_draw (GstVulkanWindow * window,
109 GstVulkanSwapper * swapper);
110 static void _on_window_resize (GstVulkanWindow * window,
111 guint width, guint height, GstVulkanSwapper * swapper);
114 _get_function_table (GstVulkanSwapper * swapper)
116 GstVulkanDevice *device = swapper->device;
117 GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
120 GST_ERROR_OBJECT (swapper, "Failed to get instance from the device");
123 #define GET_PROC_ADDRESS_REQUIRED(obj, type, name) \
125 obj->priv->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \
126 if (!obj->priv->G_PASTE(, name)) { \
127 GST_ERROR_OBJECT (obj, "Failed to find required function vk" G_STRINGIFY(name)); \
128 gst_object_unref (instance); \
133 GET_PROC_ADDRESS_REQUIRED (swapper, instance,
134 GetPhysicalDeviceSurfaceSupportKHR);
135 GET_PROC_ADDRESS_REQUIRED (swapper, instance,
136 GetPhysicalDeviceSurfaceCapabilitiesKHR);
137 GET_PROC_ADDRESS_REQUIRED (swapper, instance,
138 GetPhysicalDeviceSurfaceFormatsKHR);
139 GET_PROC_ADDRESS_REQUIRED (swapper, instance,
140 GetPhysicalDeviceSurfacePresentModesKHR);
141 GET_PROC_ADDRESS_REQUIRED (swapper, instance, DestroySurfaceKHR);
142 GET_PROC_ADDRESS_REQUIRED (swapper, device, CreateSwapchainKHR);
143 GET_PROC_ADDRESS_REQUIRED (swapper, device, DestroySwapchainKHR);
144 GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSwapchainImagesKHR);
145 GET_PROC_ADDRESS_REQUIRED (swapper, device, AcquireNextImageKHR);
146 GET_PROC_ADDRESS_REQUIRED (swapper, device, QueuePresentKHR);
148 gst_object_unref (instance);
152 #undef GET_PROC_ADDRESS_REQUIRED
155 static GstVideoFormat
156 _vk_format_to_video_format (VkFormat format)
159 /* double check endianness */
160 case VK_FORMAT_R8G8B8A8_UNORM:
161 case VK_FORMAT_R8G8B8A8_SRGB:
162 return GST_VIDEO_FORMAT_RGBA;
163 case VK_FORMAT_R8G8B8_UNORM:
164 case VK_FORMAT_R8G8B8_SRGB:
165 return GST_VIDEO_FORMAT_RGB;
166 case VK_FORMAT_B8G8R8A8_UNORM:
167 case VK_FORMAT_B8G8R8A8_SRGB:
168 return GST_VIDEO_FORMAT_BGRA;
169 case VK_FORMAT_B8G8R8_UNORM:
170 case VK_FORMAT_B8G8R8_SRGB:
171 return GST_VIDEO_FORMAT_BGR;
173 return GST_VIDEO_FORMAT_UNKNOWN;
178 _vk_format_from_video_info (GstVideoInfo * v_info)
180 switch (GST_VIDEO_INFO_FORMAT (v_info)) {
181 case GST_VIDEO_FORMAT_RGBA:
182 if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
183 GST_VIDEO_TRANSFER_SRGB)
184 return VK_FORMAT_R8G8B8A8_SRGB;
186 return VK_FORMAT_R8G8B8A8_UNORM;
187 case GST_VIDEO_FORMAT_RGB:
188 if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
189 GST_VIDEO_TRANSFER_SRGB)
190 return VK_FORMAT_R8G8B8_SRGB;
192 return VK_FORMAT_R8G8B8_UNORM;
193 case GST_VIDEO_FORMAT_BGRA:
194 if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
195 GST_VIDEO_TRANSFER_SRGB)
196 return VK_FORMAT_B8G8R8A8_SRGB;
198 return VK_FORMAT_B8G8R8A8_UNORM;
199 case GST_VIDEO_FORMAT_BGR:
200 if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
201 GST_VIDEO_TRANSFER_SRGB)
202 return VK_FORMAT_B8G8R8_SRGB;
204 return VK_FORMAT_B8G8R8_UNORM;
206 return VK_FORMAT_UNDEFINED;
210 static VkColorSpaceKHR
211 _vk_color_space_from_video_info (GstVideoInfo * v_info)
213 return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
217 _add_vk_format_to_list (GValue * list, VkFormat format)
219 GstVideoFormat v_format;
221 v_format = _vk_format_to_video_format (format);
223 const gchar *format_str = gst_video_format_to_string (v_format);
224 GValue item = G_VALUE_INIT;
225 GValue new_list = G_VALUE_INIT;
227 g_value_init (&item, G_TYPE_STRING);
228 g_value_set_string (&item, format_str);
229 gst_value_list_merge (&new_list, list, &item);
230 g_value_unset (&item);
232 g_value_unset (list);
238 _vulkan_swapper_ensure_surface (GstVulkanSwapper * swapper, GError ** error)
240 if (!swapper->priv->surface) {
241 if (!(swapper->priv->surface =
242 gst_vulkan_window_get_surface (swapper->window, error))) {
252 GstVulkanSwapper *swapper;
253 GstVulkanQueue *graphics_queue;
254 GstVulkanQueue *present_queue;
258 _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue,
259 struct choose_data *data)
262 device->physical_device->queue_family_props[queue->family].queueFlags;
263 VkPhysicalDevice gpu;
264 gboolean supports_present;
266 gpu = gst_vulkan_device_get_physical_device (data->swapper->device);
270 GError *error = NULL;
271 VkBool32 physical_device_supported;
274 data->swapper->priv->GetPhysicalDeviceSurfaceSupportKHR (gpu,
275 queue->index, data->swapper->priv->surface, &physical_device_supported);
276 if (gst_vulkan_error_to_g_error (err, &error,
277 "GetPhysicalDeviceSurfaceSupport") < 0) {
278 GST_DEBUG_OBJECT (data->swapper,
279 "surface not supported by the physical device: %s", error->message);
280 g_clear_error (&error);
286 gst_vulkan_window_get_presentation_support (data->swapper->window,
287 device, queue->index);
289 if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) {
290 if (supports_present) {
291 /* found one that supports both */
292 if (data->graphics_queue)
293 gst_object_unref (data->graphics_queue);
294 data->graphics_queue = gst_object_ref (queue);
295 if (data->present_queue)
296 gst_object_unref (data->present_queue);
297 data->present_queue = gst_object_ref (queue);
300 if (!data->graphics_queue)
301 data->present_queue = gst_object_ref (queue);
302 } else if (supports_present) {
303 if (!data->present_queue)
304 data->present_queue = gst_object_ref (queue);
311 * gst_vulkan_swapper_choose_queue:
312 * @swapper: a #GstVulkanSwapper
313 * @available_queue: (transfer none): a #GstVulkanQueue chosen elsewhere
317 gst_vulkan_swapper_choose_queue (GstVulkanSwapper * swapper,
318 GstVulkanQueue * available_queue, GError ** error)
320 if (!_vulkan_swapper_ensure_surface (swapper, error))
326 if (available_queue) {
328 swapper->device->physical_device->
329 queue_family_props[available_queue->family].queueFlags;
330 gboolean supports_present;
333 gst_vulkan_window_get_presentation_support (swapper->window,
334 swapper->device, available_queue->index);
335 if (supports_present && flags & VK_QUEUE_GRAPHICS_BIT)
336 swapper->queue = gst_object_ref (available_queue);
339 if (!swapper->queue) {
340 struct choose_data data;
342 data.swapper = swapper;
343 data.present_queue = NULL;
344 data.graphics_queue = NULL;
346 gst_vulkan_device_foreach_queue (swapper->device,
347 (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data);
349 if (data.graphics_queue != data.present_queue) {
350 /* FIXME: add support for separate graphics/present queues */
351 g_set_error (error, GST_VULKAN_ERROR,
352 VK_ERROR_INITIALIZATION_FAILED,
353 "Failed to find a compatible present/graphics queue");
354 if (data.present_queue)
355 gst_object_unref (data.present_queue);
356 if (data.graphics_queue)
357 gst_object_unref (data.graphics_queue);
361 swapper->queue = gst_object_ref (data.present_queue);
362 if (data.present_queue)
363 gst_object_unref (data.present_queue);
364 if (data.graphics_queue)
365 gst_object_unref (data.graphics_queue);
372 _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
375 VkPhysicalDevice gpu;
378 if (swapper->priv->surf_formats)
381 gpu = gst_vulkan_device_get_physical_device (swapper->device);
383 if (!gst_vulkan_swapper_choose_queue (swapper, NULL, error))
386 if (!(swapper->cmd_pool =
387 gst_vulkan_queue_create_command_pool (swapper->queue, error)))
391 swapper->priv->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu,
392 swapper->priv->surface, &swapper->priv->surf_props);
393 if (gst_vulkan_error_to_g_error (err, error,
394 "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
398 swapper->priv->GetPhysicalDeviceSurfaceFormatsKHR (gpu,
399 swapper->priv->surface, &swapper->priv->n_surf_formats, NULL);
400 if (gst_vulkan_error_to_g_error (err, error,
401 "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
404 swapper->priv->surf_formats =
405 g_new0 (VkSurfaceFormatKHR, swapper->priv->n_surf_formats);
407 swapper->priv->GetPhysicalDeviceSurfaceFormatsKHR (gpu,
408 swapper->priv->surface, &swapper->priv->n_surf_formats,
409 swapper->priv->surf_formats);
410 if (gst_vulkan_error_to_g_error (err, error,
411 "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
415 swapper->priv->GetPhysicalDeviceSurfacePresentModesKHR (gpu,
416 swapper->priv->surface, &swapper->priv->n_surf_present_modes, NULL);
417 if (gst_vulkan_error_to_g_error (err, error,
418 "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
421 swapper->priv->surf_present_modes =
422 g_new0 (VkPresentModeKHR, swapper->priv->n_surf_present_modes);
424 swapper->priv->GetPhysicalDeviceSurfacePresentModesKHR (gpu,
425 swapper->priv->surface, &swapper->priv->n_surf_present_modes,
426 swapper->priv->surf_present_modes);
427 if (gst_vulkan_error_to_g_error (err, error,
428 "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
435 _on_window_close (GstVulkanWindow * window, GstVulkanSwapper * swapper)
437 g_atomic_int_set (&swapper->priv->to_quit, 1);
443 gst_vulkan_swapper_set_property (GObject * object, guint prop_id,
444 const GValue * value, GParamSpec * pspec)
446 GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
449 case PROP_FORCE_ASPECT_RATIO:
450 swapper->priv->force_aspect_ratio = g_value_get_boolean (value);
452 case PROP_PIXEL_ASPECT_RATIO:
453 swapper->priv->par_n = gst_value_get_fraction_numerator (value);
454 swapper->priv->par_d = gst_value_get_fraction_denominator (value);
457 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
463 gst_vulkan_swapper_get_property (GObject * object, guint prop_id,
464 GValue * value, GParamSpec * pspec)
466 GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
469 case PROP_FORCE_ASPECT_RATIO:
470 g_value_set_boolean (value, swapper->priv->force_aspect_ratio);
472 case PROP_PIXEL_ASPECT_RATIO:
473 gst_value_set_fraction (value, swapper->priv->par_n,
474 swapper->priv->par_d);
477 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
483 gst_vulkan_swapper_finalize (GObject * object)
485 GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
486 GstVulkanInstance *instance =
487 gst_vulkan_device_get_instance (swapper->device);
490 g_signal_handler_disconnect (swapper->window, swapper->priv->draw_id);
491 swapper->priv->draw_id = 0;
493 g_signal_handler_disconnect (swapper->window, swapper->priv->close_id);
494 swapper->priv->close_id = 0;
496 g_signal_handler_disconnect (swapper->window, swapper->priv->resize_id);
497 swapper->priv->resize_id = 0;
499 if (!gst_vulkan_trash_list_wait (swapper->priv->trash_list, -1))
500 GST_WARNING_OBJECT (swapper, "Failed to wait for all fences to complete "
501 "before shutting down");
502 gst_object_unref (swapper->priv->trash_list);
503 swapper->priv->trash_list = NULL;
505 if (swapper->priv->swap_chain_images) {
506 for (i = 0; i < swapper->priv->n_swap_chain_images; i++) {
507 gst_memory_unref ((GstMemory *) swapper->priv->swap_chain_images[i]);
508 swapper->priv->swap_chain_images[i] = NULL;
510 g_free (swapper->priv->swap_chain_images);
512 swapper->priv->swap_chain_images = NULL;
514 if (swapper->priv->swap_chain)
515 swapper->priv->DestroySwapchainKHR (swapper->device->device,
516 swapper->priv->swap_chain, NULL);
517 swapper->priv->swap_chain = VK_NULL_HANDLE;
519 if (swapper->priv->surface) {
520 swapper->priv->DestroySurfaceKHR (instance->instance,
521 swapper->priv->surface, NULL);
523 swapper->priv->surface = VK_NULL_HANDLE;
525 g_free (swapper->priv->surf_present_modes);
526 swapper->priv->surf_present_modes = NULL;
528 g_free (swapper->priv->surf_formats);
529 swapper->priv->surf_formats = NULL;
531 gst_buffer_replace (&swapper->priv->current_buffer, NULL);
532 gst_caps_replace (&swapper->priv->caps, NULL);
534 g_mutex_clear (&swapper->priv->render_lock);
536 if (swapper->cmd_pool)
537 gst_object_unref (swapper->cmd_pool);
538 swapper->cmd_pool = NULL;
541 gst_object_unref (swapper->queue);
542 swapper->queue = NULL;
545 gst_object_unref (swapper->device);
546 swapper->device = NULL;
549 gst_object_unref (swapper->window);
550 swapper->window = NULL;
552 gst_object_unref (instance);
554 G_OBJECT_CLASS (parent_class)->finalize (object);
558 gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
560 swapper->priv = gst_vulkan_swapper_get_instance_private (swapper);
562 g_mutex_init (&swapper->priv->render_lock);
564 swapper->priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
565 swapper->priv->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N;
566 swapper->priv->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D;
568 swapper->priv->trash_list = gst_vulkan_trash_fence_list_new ();
572 gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
574 GObjectClass *gobject_class = (GObjectClass *) klass;
576 gobject_class->set_property = gst_vulkan_swapper_set_property;
577 gobject_class->get_property = gst_vulkan_swapper_get_property;
578 gobject_class->finalize = gst_vulkan_swapper_finalize;
580 g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
581 g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
582 "When enabled, scaling will respect original aspect ratio",
583 DEFAULT_FORCE_ASPECT_RATIO,
584 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
586 g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
587 gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
588 "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
589 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
593 gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
595 GstVulkanSwapper *swapper;
597 swapper = g_object_new (GST_TYPE_VULKAN_SWAPPER, NULL);
598 gst_object_ref_sink (swapper);
599 swapper->device = gst_object_ref (device);
600 swapper->window = gst_object_ref (window);
602 if (!_get_function_table (swapper)) {
603 gst_object_unref (swapper);
607 swapper->priv->close_id = g_signal_connect (swapper->window, "close",
608 (GCallback) _on_window_close, swapper);
609 swapper->priv->draw_id = g_signal_connect (swapper->window, "draw",
610 (GCallback) _on_window_draw, swapper);
611 swapper->priv->resize_id = g_signal_connect (swapper->window, "resize",
612 (GCallback) _on_window_resize, swapper);
618 gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
624 g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
626 if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
629 caps = gst_caps_new_empty_simple ("video/x-raw");
630 gst_caps_set_features (caps, 0,
631 gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE));
632 s = gst_caps_get_structure (caps, 0);
636 GValue list = G_VALUE_INIT;
638 g_value_init (&list, GST_TYPE_LIST);
640 if (swapper->priv->n_surf_formats
641 && swapper->priv->surf_formats[0].format == VK_FORMAT_UNDEFINED) {
642 _add_vk_format_to_list (&list, VK_FORMAT_B8G8R8A8_UNORM);
644 for (i = 0; i < swapper->priv->n_surf_formats; i++) {
645 _add_vk_format_to_list (&list, swapper->priv->surf_formats[i].format);
649 gst_structure_set_value (s, "format", &list);
650 g_value_unset (&list);
655 swapper->device->physical_device->properties.limits.maxImageDimension2D;
657 gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, (gint) max_dim,
658 "height", GST_TYPE_INT_RANGE, 1, (gint) max_dim, "pixel-aspect-ratio",
659 GST_TYPE_FRACTION, 1, 1, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
663 GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
669 _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
672 VkSurfaceTransformFlagsKHR preTransform;
673 VkCompositeAlphaFlagsKHR alpha_flags;
674 VkPresentModeKHR present_mode;
675 VkImageUsageFlags usage = 0;
676 VkColorSpaceKHR color_space;
677 VkImage *swap_chain_images;
678 VkExtent2D swapchain_dims;
679 guint32 n_images_wanted;
680 VkPhysicalDevice gpu;
685 if (!_vulkan_swapper_ensure_surface (swapper, error))
688 gpu = gst_vulkan_device_get_physical_device (swapper->device);
690 swapper->priv->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu,
691 swapper->priv->surface, &swapper->priv->surf_props);
692 if (gst_vulkan_error_to_g_error (err, error,
693 "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
696 /* width and height are either both -1, or both not -1. */
697 if (swapper->priv->surf_props.currentExtent.width == -1) {
698 /* If the surface size is undefined, the size is set to
699 * the size of the images requested. */
701 gst_vulkan_window_get_surface_dimensions (swapper->window, &width, &height);
702 swapchain_dims.width = width;
703 swapchain_dims.height = height;
704 swapper->priv->any_current_extent = TRUE;
706 /* If the surface size is defined, the swap chain size must match */
707 swapchain_dims = swapper->priv->surf_props.currentExtent;
708 swapper->priv->any_current_extent = FALSE;
711 /* If mailbox mode is available, use it, as is the lowest-latency non-
712 * tearing mode. If not, try IMMEDIATE which will usually be available,
713 * and is fastest (though it tears). If not, fall back to FIFO which is
714 * always available. */
715 present_mode = VK_PRESENT_MODE_FIFO_KHR;
716 for (i = 0; i < swapper->priv->n_surf_present_modes; i++) {
717 if (swapper->priv->surf_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
718 present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
721 if ((present_mode != VK_PRESENT_MODE_MAILBOX_KHR) &&
722 (swapper->priv->surf_present_modes[i] ==
723 VK_PRESENT_MODE_IMMEDIATE_KHR)) {
724 present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
728 /* Determine the number of VkImage's to use in the swap chain (we desire to
729 * own only 1 image at a time, besides the images being displayed and
730 * queued for display): */
731 n_images_wanted = swapper->priv->surf_props.minImageCount + 1;
732 if ((swapper->priv->surf_props.maxImageCount > 0) &&
733 (n_images_wanted > swapper->priv->surf_props.maxImageCount)) {
734 /* Application must settle for fewer images than desired: */
735 n_images_wanted = swapper->priv->surf_props.maxImageCount;
738 if (swapper->priv->surf_props.
739 supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
740 preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
742 preTransform = swapper->priv->surf_props.currentTransform;
745 format = _vk_format_from_video_info (&swapper->priv->v_info);
746 color_space = _vk_color_space_from_video_info (&swapper->priv->v_info);
748 if ((swapper->priv->surf_props.supportedCompositeAlpha &
749 VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0) {
750 alpha_flags = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
751 } else if ((swapper->priv->surf_props.supportedCompositeAlpha &
752 VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0) {
753 alpha_flags = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
755 g_set_error (error, GST_VULKAN_ERROR,
756 VK_ERROR_INITIALIZATION_FAILED,
757 "Incorrect alpha flags available for the swap images");
761 if ((swapper->priv->surf_props.supportedUsageFlags &
762 VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0) {
763 usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
765 g_set_error (error, GST_VULKAN_ERROR,
766 VK_ERROR_INITIALIZATION_FAILED,
767 "Incorrect usage flags available for the swap images");
771 surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
773 usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
775 g_set_error (error, GST_VULKAN_ERROR,
776 VK_ERROR_INITIALIZATION_FAILED,
777 "Incorrect usage flags available for the swap images");
782 VkSwapchainCreateInfoKHR swap_chain_info = { 0, };
783 VkSwapchainKHR old_swap_chain;
785 old_swap_chain = swapper->priv->swap_chain;
788 swap_chain_info = (VkSwapchainCreateInfoKHR) {
789 .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
791 .surface = swapper->priv->surface,
792 .minImageCount = n_images_wanted,
793 .imageFormat = format,
794 .imageColorSpace = color_space,
795 .imageExtent = swapchain_dims,
796 .imageArrayLayers = 1,
798 .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
799 .queueFamilyIndexCount = 0,
800 .pQueueFamilyIndices = NULL,
801 .preTransform = preTransform,
802 .presentMode = present_mode,
803 .compositeAlpha = alpha_flags,
805 .oldSwapchain = old_swap_chain
810 swapper->priv->CreateSwapchainKHR (swapper->device->device,
811 &swap_chain_info, NULL, &swapper->priv->swap_chain);
812 if (gst_vulkan_error_to_g_error (err, error, "vkCreateSwapchainKHR") < 0)
815 if (old_swap_chain != VK_NULL_HANDLE) {
816 swapper->priv->DestroySwapchainKHR (swapper->device->device,
817 old_swap_chain, NULL);
822 swapper->priv->GetSwapchainImagesKHR (swapper->device->device,
823 swapper->priv->swap_chain, &swapper->priv->n_swap_chain_images, NULL);
824 if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0)
827 swap_chain_images = g_new0 (VkImage, swapper->priv->n_swap_chain_images);
829 swapper->priv->GetSwapchainImagesKHR (swapper->device->device,
830 swapper->priv->swap_chain, &swapper->priv->n_swap_chain_images,
832 if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
833 g_free (swap_chain_images);
837 swapper->priv->swap_chain_images =
838 g_new0 (GstVulkanImageMemory *, swapper->priv->n_swap_chain_images);
839 for (i = 0; i < swapper->priv->n_swap_chain_images; i++) {
840 swapper->priv->swap_chain_images[i] = (GstVulkanImageMemory *)
841 gst_vulkan_image_memory_wrapped (swapper->device, swap_chain_images[i],
842 format, swapchain_dims.width, swapchain_dims.height,
843 VK_IMAGE_TILING_OPTIMAL, usage, NULL, NULL);
845 swapper->priv->swap_chain_images[i]->barrier.parent.pipeline_stages =
846 VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
847 swapper->priv->swap_chain_images[i]->barrier.parent.access_flags =
848 VK_ACCESS_MEMORY_READ_BIT;
849 swapper->priv->swap_chain_images[i]->barrier.image_layout =
850 VK_IMAGE_LAYOUT_UNDEFINED;
853 g_free (swap_chain_images);
858 _swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
862 if (!swapper->queue) {
863 if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
868 if (swapper->priv->swap_chain_images) {
869 for (i = 0; i < swapper->priv->n_swap_chain_images; i++) {
870 if (swapper->priv->swap_chain_images[i])
871 gst_memory_unref ((GstMemory *) swapper->priv->swap_chain_images[i]);
873 g_free (swapper->priv->swap_chain_images);
874 swapper->priv->swap_chain_images = NULL;
877 return _allocate_swapchain (swapper, swapper->priv->caps, error);
882 configure_display_from_info (GstVulkanSwapper * swapper, GstVideoInfo * vinfo)
888 gint display_par_n, display_par_d;
889 guint display_ratio_num, display_ratio_den;
891 width = GST_VIDEO_INFO_WIDTH (vinfo);
892 height = GST_VIDEO_INFO_HEIGHT (vinfo);
894 par_n = GST_VIDEO_INFO_PAR_N (vinfo);
895 par_d = GST_VIDEO_INFO_PAR_D (vinfo);
900 /* get display's PAR */
901 if (swapper->priv->par_n != 0 && swapper->priv->par_d != 0) {
902 display_par_n = swapper->priv->par_n;
903 display_par_d = swapper->priv->par_d;
909 ok = gst_video_calculate_display_ratio (&display_ratio_num,
910 &display_ratio_den, width, height, par_n, par_d, display_par_n,
916 GST_TRACE_OBJECT (swapper, "PAR: %u/%u DAR:%u/%u", par_n, par_d,
917 display_par_n, display_par_d);
919 if (height % display_ratio_den == 0) {
920 GST_DEBUG_OBJECT (swapper, "keeping video height");
921 swapper->priv->dar_width = (guint)
922 gst_util_uint64_scale_int (height, display_ratio_num,
924 swapper->priv->dar_height = height;
925 } else if (width % display_ratio_num == 0) {
926 GST_DEBUG_OBJECT (swapper, "keeping video width");
927 swapper->priv->dar_width = width;
928 swapper->priv->dar_height = (guint)
929 gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
931 GST_DEBUG_OBJECT (swapper, "approximating while keeping video height");
932 swapper->priv->dar_width = (guint)
933 gst_util_uint64_scale_int (height, display_ratio_num,
935 swapper->priv->dar_height = height;
937 GST_DEBUG_OBJECT (swapper, "scaling to %dx%d", swapper->priv->dar_width,
938 swapper->priv->dar_height);
944 gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
947 if (!gst_video_info_from_caps (&swapper->priv->v_info, caps)) {
948 g_set_error (error, GST_VULKAN_ERROR,
949 VK_ERROR_INITIALIZATION_FAILED, "Failed to get GstVideoInfo from caps");
953 if (!configure_display_from_info (swapper, &swapper->priv->v_info)) {
954 g_set_error (error, GST_VULKAN_ERROR,
955 VK_ERROR_INITIALIZATION_FAILED, "Failed to configure display sizes");
959 gst_caps_replace (&swapper->priv->caps, caps);
961 return _swapchain_resize (swapper, error);
965 _build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
966 GstBuffer * buffer, GstVulkanCommandBuffer ** cmd_ret, GError ** error)
969 GstVulkanImageMemory *swap_img;
970 GstVulkanCommandBuffer *cmd_buf;
971 GstVideoRectangle src, dst, rslt;
974 g_return_val_if_fail (swap_idx < swapper->priv->n_swap_chain_images, FALSE);
975 swap_img = swapper->priv->swap_chain_images[swap_idx];
977 if (!(cmd_buf = gst_vulkan_command_pool_create (swapper->cmd_pool, error)))
982 VkCommandBufferBeginInfo cmd_buf_info = {
983 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
985 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
986 .pInheritanceInfo = NULL
990 gst_vulkan_command_buffer_lock (cmd_buf);
991 err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
992 if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
998 VkImageMemoryBarrier image_memory_barrier = {
999 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1001 .srcAccessMask = swap_img->barrier.parent.access_flags,
1002 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
1003 .oldLayout = swap_img->barrier.image_layout,
1004 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1005 /* FIXME: implement exclusive transfers */
1006 .srcQueueFamilyIndex = 0,
1007 .dstQueueFamilyIndex = 0,
1008 .image = swap_img->image,
1009 .subresourceRange = swap_img->barrier.subresource_range
1013 vkCmdPipelineBarrier (cmd_buf->cmd,
1014 swap_img->barrier.parent.pipeline_stages,
1015 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1016 &image_memory_barrier);
1018 swap_img->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1019 swap_img->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1020 swap_img->barrier.image_layout = image_memory_barrier.newLayout;
1024 src.w = swapper->priv->dar_width;
1025 src.h = swapper->priv->dar_height;
1028 dst.w = gst_vulkan_image_memory_get_width (swap_img);
1029 dst.h = gst_vulkan_image_memory_get_height (swap_img);
1031 gst_video_sink_center_rect (src, dst, &rslt,
1032 swapper->priv->force_aspect_ratio);
1034 GST_TRACE_OBJECT (swapper, "rendering into result rectangle %ux%u+%u,%u "
1035 "src %ux%u dst %ux%u", rslt.w, rslt.h, rslt.x, rslt.y, src.w, src.h,
1038 in_mem = gst_buffer_peek_memory (buffer, 0);
1040 GstVulkanImageMemory *img_mem = (GstVulkanImageMemory *) in_mem;
1042 VkImageBlit blit = {
1044 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1046 .baseArrayLayer = 0,
1051 {GST_VIDEO_INFO_WIDTH (&swapper->priv->v_info), GST_VIDEO_INFO_HEIGHT (&swapper->priv->v_info), 1},
1054 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1056 .baseArrayLayer = 0,
1060 {rslt.x, rslt.y, 0},
1061 {rslt.x + rslt.w, rslt.y + rslt.h, 1},
1064 VkImageMemoryBarrier image_memory_barrier = {
1065 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1067 .srcAccessMask = img_mem->barrier.parent.access_flags,
1068 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
1069 .oldLayout = img_mem->barrier.image_layout,
1070 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1071 /* FIXME: implement exclusive transfers */
1072 .srcQueueFamilyIndex = 0,
1073 .dstQueueFamilyIndex = 0,
1074 .image = img_mem->image,
1075 .subresourceRange = img_mem->barrier.subresource_range
1077 VkClearColorValue clear = {{0.0, 0.0, 0.0, 1.0}};
1078 VkImageSubresourceRange clear_range = {
1079 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1082 .baseArrayLayer = 0,
1087 vkCmdPipelineBarrier (cmd_buf->cmd, img_mem->barrier.parent.pipeline_stages,
1088 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1089 &image_memory_barrier);
1091 img_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1092 img_mem->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1093 img_mem->barrier.image_layout = image_memory_barrier.newLayout;
1095 vkCmdClearColorImage (cmd_buf->cmd, swap_img->image,
1096 swap_img->barrier.image_layout, &clear, 1, &clear_range);
1097 vkCmdBlitImage (cmd_buf->cmd, img_mem->image, img_mem->barrier.image_layout,
1098 swap_img->image, swap_img->barrier.image_layout, 1, &blit,
1103 VkImageMemoryBarrier image_memory_barrier = {
1104 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1106 .srcAccessMask = swap_img->barrier.parent.access_flags,
1107 .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
1108 .oldLayout = swap_img->barrier.image_layout,
1109 .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
1110 /* FIXME: implement exclusive transfers */
1111 .srcQueueFamilyIndex = 0,
1112 .dstQueueFamilyIndex = 0,
1113 .image = swap_img->image,
1114 .subresourceRange = swap_img->barrier.subresource_range
1118 vkCmdPipelineBarrier (cmd_buf->cmd,
1119 swap_img->barrier.parent.pipeline_stages,
1120 VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1121 &image_memory_barrier);
1123 swap_img->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1124 swap_img->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1125 swap_img->barrier.image_layout = image_memory_barrier.newLayout;
1128 err = vkEndCommandBuffer (cmd_buf->cmd);
1129 if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
1131 gst_vulkan_command_buffer_unlock (cmd_buf);
1138 gst_vulkan_command_buffer_unlock (cmd_buf);
1143 _render_buffer_unlocked (GstVulkanSwapper * swapper,
1144 GstBuffer * buffer, GError ** error)
1146 VkSemaphore acquire_semaphore = { 0, };
1147 VkSemaphore present_semaphore = { 0, };
1148 VkSemaphoreCreateInfo semaphore_info = { 0, };
1149 GstVulkanFence *fence = NULL;
1150 VkPresentInfoKHR present;
1151 GstVulkanCommandBuffer *cmd_buf = NULL;
1153 VkResult err, present_err = VK_SUCCESS;
1155 gst_vulkan_trash_list_gc (swapper->priv->trash_list);
1158 g_set_error (error, GST_VULKAN_ERROR,
1159 VK_ERROR_INITIALIZATION_FAILED, "Invalid buffer");
1163 if (g_atomic_int_get (&swapper->priv->to_quit)) {
1164 g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_SURFACE_LOST_KHR,
1165 "Output window was closed");
1169 gst_buffer_replace (&swapper->priv->current_buffer, buffer);
1172 semaphore_info = (VkSemaphoreCreateInfo) {
1173 .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
1180 err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1181 NULL, &acquire_semaphore);
1182 if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1186 swapper->priv->AcquireNextImageKHR (swapper->device->device,
1187 swapper->priv->swap_chain, -1, acquire_semaphore, VK_NULL_HANDLE,
1189 /* TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR */
1190 if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1191 GST_DEBUG_OBJECT (swapper, "out of date frame acquired");
1193 vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1194 acquire_semaphore = NULL;
1195 if (!_swapchain_resize (swapper, error))
1198 } else if (gst_vulkan_error_to_g_error (err, error,
1199 "vkAcquireNextImageKHR") < 0) {
1203 if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd_buf, error))
1206 err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1207 NULL, &present_semaphore);
1208 if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1212 VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1213 VkSubmitInfo submit_info = { 0, };
1216 submit_info = (VkSubmitInfo) {
1217 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1219 .waitSemaphoreCount = 1,
1220 .pWaitSemaphores = &acquire_semaphore,
1221 .pWaitDstStageMask = &stages,
1222 .commandBufferCount = 1,
1223 .pCommandBuffers = &cmd_buf->cmd,
1224 .signalSemaphoreCount = 1,
1225 .pSignalSemaphores = &present_semaphore,
1229 fence = gst_vulkan_fence_new (swapper->device, 0, error);
1234 vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
1235 GST_VULKAN_FENCE_FENCE (fence));
1236 if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
1239 gst_vulkan_trash_list_add (swapper->priv->trash_list,
1240 gst_vulkan_trash_new_mini_object_unref (gst_vulkan_fence_ref (fence),
1241 GST_MINI_OBJECT_CAST (cmd_buf)));
1242 gst_vulkan_trash_list_add (swapper->priv->trash_list,
1243 gst_vulkan_trash_new_free_semaphore (fence, acquire_semaphore));
1244 acquire_semaphore = NULL;
1246 gst_vulkan_command_buffer_unlock (cmd_buf);
1252 present = (VkPresentInfoKHR) {
1253 .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1255 .waitSemaphoreCount = 1,
1256 .pWaitSemaphores = &present_semaphore,
1257 .swapchainCount = 1,
1258 .pSwapchains = &swapper->priv->swap_chain,
1259 .pImageIndices = &swap_idx,
1260 .pResults = &present_err,
1264 err = swapper->priv->QueuePresentKHR (swapper->queue->queue, &present);
1266 if (present_err == VK_ERROR_OUT_OF_DATE_KHR) {
1267 GST_DEBUG_OBJECT (swapper, "out of date frame submitted");
1269 if (!_swapchain_resize (swapper, error))
1271 } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
1275 VkSubmitInfo submit_info = { 0, };
1276 VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1279 submit_info = (VkSubmitInfo) {
1280 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1281 .pWaitDstStageMask = &stages,
1286 fence = gst_vulkan_fence_new (swapper->device, 0, error);
1291 vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
1292 GST_VULKAN_FENCE_FENCE (fence));
1293 if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
1296 gst_vulkan_trash_list_add (swapper->priv->trash_list,
1297 gst_vulkan_trash_new_free_semaphore (fence, present_semaphore));
1305 if (acquire_semaphore)
1306 vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1307 if (present_semaphore)
1308 vkDestroySemaphore (swapper->device->device, present_semaphore, NULL);
1310 gst_vulkan_command_buffer_unlock (cmd_buf);
1311 gst_vulkan_command_buffer_unref (cmd_buf);
1318 gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
1319 GstBuffer * buffer, GError ** error)
1324 mem = gst_buffer_peek_memory (buffer, 0);
1326 g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1327 "Buffer has no memory");
1330 if (!gst_is_vulkan_image_memory (mem)) {
1331 g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1332 "Incorrect memory type");
1336 RENDER_LOCK (swapper);
1337 ret = _render_buffer_unlocked (swapper, buffer, error);
1338 RENDER_UNLOCK (swapper);
1344 _on_window_draw (GstVulkanWindow * window, GstVulkanSwapper * swapper)
1346 GError *error = NULL;
1348 RENDER_LOCK (swapper);
1349 if (!swapper->priv->current_buffer) {
1350 GST_DEBUG_OBJECT (swapper, "No buffer to render");
1351 RENDER_UNLOCK (swapper);
1355 /* TODO: perform some rate limiting of the number of redraw events */
1356 if (!_render_buffer_unlocked (swapper, swapper->priv->current_buffer, &error))
1357 GST_ERROR_OBJECT (swapper, "Failed to redraw buffer %p %s",
1358 swapper->priv->current_buffer, error->message);
1359 g_clear_error (&error);
1360 RENDER_UNLOCK (swapper);
1364 _on_window_resize (GstVulkanWindow * window, guint width, guint height,
1365 GstVulkanSwapper * swapper)
1367 GError *error = NULL;
1369 RENDER_LOCK (swapper);
1370 if (swapper->priv->any_current_extent) {
1371 if (!_swapchain_resize (swapper, &error))
1372 GST_ERROR_OBJECT (swapper, "Failed to resize swapchain: %s",
1374 g_clear_error (&error);
1376 RENDER_UNLOCK (swapper);