0bac548bee4cd3e302972560688f226c286a7021
[platform/upstream/gstreamer.git] / gst-libs / gst / vulkan / gstvkswapper.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 <string.h>
26
27 #include "gstvkswapper.h"
28
29 #define GST_CAT_DEFAULT gst_vulkan_swapper_debug
30 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
31
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));
35
36 struct _GstVulkanSwapperPrivate
37 {
38   VkSurfaceKHR surface;
39
40   VkSurfaceCapabilitiesKHR surf_props;
41   VkSurfaceFormatKHR *surf_formats;
42   guint32 n_surf_formats;
43   VkPresentModeKHR *surf_present_modes;
44   guint32 n_surf_present_modes;
45
46   VkSwapchainKHR swap_chain;
47   GstVulkanImageMemory **swap_chain_images;
48   guint32 n_swap_chain_images;
49
50   GstCaps *caps;
51   GstVideoInfo v_info;
52
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;
65
66   /* <private> */
67   /* runtime variables */
68   gint to_quit;
69   GstBuffer *current_buffer;
70   gboolean any_current_extent;
71
72   /* signal handlers */
73   gulong close_id;
74   gulong draw_id;
75   gulong resize_id;
76
77   /* properties */
78   gboolean force_aspect_ratio;
79   gint par_n;
80   gint par_d;
81
82   GMutex render_lock;
83
84   GstVulkanTrashList *trash_list;
85
86   /* source sizes accounting for all aspect ratios */
87   guint dar_width;
88   guint dar_height;
89 };
90
91 enum
92 {
93   PROP_0,
94   PROP_FORCE_ASPECT_RATIO,
95   PROP_PIXEL_ASPECT_RATIO,
96 };
97
98 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
99 #define DEFAULT_PIXEL_ASPECT_RATIO_N 0
100 #define DEFAULT_PIXEL_ASPECT_RATIO_D 1
101
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"));
107
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);
112
113 static gboolean
114 _get_function_table (GstVulkanSwapper * swapper)
115 {
116   GstVulkanDevice *device = swapper->device;
117   GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
118
119   if (!instance) {
120     GST_ERROR_OBJECT (swapper, "Failed to get instance from the device");
121     return FALSE;
122   }
123 #define GET_PROC_ADDRESS_REQUIRED(obj, type, name) \
124   G_STMT_START { \
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); \
129       return FALSE; \
130     } \
131   } G_STMT_END
132
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);
147
148   gst_object_unref (instance);
149
150   return TRUE;
151
152 #undef GET_PROC_ADDRESS_REQUIRED
153 }
154
155 static GstVideoFormat
156 _vk_format_to_video_format (VkFormat format)
157 {
158   switch (format) {
159       /* double check endianess */
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;
172     default:
173       return GST_VIDEO_FORMAT_UNKNOWN;
174   }
175 }
176
177 static VkFormat
178 _vk_format_from_video_info (GstVideoInfo * v_info)
179 {
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;
185       else
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;
191       else
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;
197       else
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;
203       else
204         return VK_FORMAT_B8G8R8_UNORM;
205     default:
206       return VK_FORMAT_UNDEFINED;
207   }
208 }
209
210 static VkColorSpaceKHR
211 _vk_color_space_from_video_info (GstVideoInfo * v_info)
212 {
213   return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
214 }
215
216 static void
217 _add_vk_format_to_list (GValue * list, VkFormat format)
218 {
219   GstVideoFormat v_format;
220
221   v_format = _vk_format_to_video_format (format);
222   if (v_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;
226
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);
231
232     g_value_unset (list);
233     *list = new_list;
234   }
235 }
236
237 static gboolean
238 _vulkan_swapper_ensure_surface (GstVulkanSwapper * swapper, GError ** error)
239 {
240   if (!swapper->priv->surface) {
241     if (!(swapper->priv->surface =
242             gst_vulkan_window_get_surface (swapper->window, error))) {
243       return FALSE;
244     }
245   }
246
247   return TRUE;
248 }
249
250 struct choose_data
251 {
252   GstVulkanSwapper *swapper;
253   GstVulkanQueue *graphics_queue;
254   GstVulkanQueue *present_queue;
255 };
256
257 static gboolean
258 _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue,
259     struct choose_data *data)
260 {
261   guint flags =
262       device->physical_device->queue_family_props[queue->family].queueFlags;
263   VkPhysicalDevice gpu;
264   gboolean supports_present;
265
266   gpu = gst_vulkan_device_get_physical_device (data->swapper->device);
267
268   {
269     VkResult err;
270     GError *error = NULL;
271     VkBool32 physical_device_supported;
272
273     err =
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);
281       return TRUE;
282     }
283   }
284
285   supports_present =
286       gst_vulkan_window_get_presentation_support (data->swapper->window,
287       device, queue->index);
288
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);
298       return FALSE;
299     }
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);
305   }
306
307   return TRUE;
308 }
309
310 /*
311  * gst_vulkan_swapper_choose_queue:
312  * @swapper: a #GstVulkanSwapper
313  * @available_queue: (transfer none): a #GstVulkanQueue chosen elsewhere
314  * @error: a #GError
315  */
316 gboolean
317 gst_vulkan_swapper_choose_queue (GstVulkanSwapper * swapper,
318     GstVulkanQueue * available_queue, GError ** error)
319 {
320   if (!_vulkan_swapper_ensure_surface (swapper, error))
321     return FALSE;
322
323   if (swapper->queue)
324     return TRUE;
325
326   if (available_queue) {
327     guint flags =
328         swapper->device->physical_device->
329         queue_family_props[available_queue->family].queueFlags;
330     gboolean supports_present;
331
332     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);
337   }
338
339   if (!swapper->queue) {
340     struct choose_data data;
341
342     data.swapper = swapper;
343     data.present_queue = NULL;
344     data.graphics_queue = NULL;
345
346     gst_vulkan_device_foreach_queue (swapper->device,
347         (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data);
348
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);
358       return FALSE;
359     }
360
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);
366   }
367
368   return TRUE;
369 }
370
371 static gboolean
372 _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
373     GError ** error)
374 {
375   VkPhysicalDevice gpu;
376   VkResult err;
377
378   if (swapper->priv->surf_formats)
379     return TRUE;
380
381   gpu = gst_vulkan_device_get_physical_device (swapper->device);
382
383   if (!gst_vulkan_swapper_choose_queue (swapper, NULL, error))
384     return FALSE;
385
386   if (!(swapper->cmd_pool =
387           gst_vulkan_queue_create_command_pool (swapper->queue, error)))
388     return FALSE;
389
390   err =
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)
395     return FALSE;
396
397   err =
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)
402     return FALSE;
403
404   swapper->priv->surf_formats =
405       g_new0 (VkSurfaceFormatKHR, swapper->priv->n_surf_formats);
406   err =
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)
412     return FALSE;
413
414   err =
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)
419     return FALSE;
420
421   swapper->priv->surf_present_modes =
422       g_new0 (VkPresentModeKHR, swapper->priv->n_surf_present_modes);
423   err =
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)
429     return FALSE;
430
431   return TRUE;
432 }
433
434 static gboolean
435 _on_window_close (GstVulkanWindow * window, GstVulkanSwapper * swapper)
436 {
437   g_atomic_int_set (&swapper->priv->to_quit, 1);
438
439   return TRUE;
440 }
441
442 static void
443 gst_vulkan_swapper_set_property (GObject * object, guint prop_id,
444     const GValue * value, GParamSpec * pspec)
445 {
446   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
447
448   switch (prop_id) {
449     case PROP_FORCE_ASPECT_RATIO:
450       swapper->priv->force_aspect_ratio = g_value_get_boolean (value);
451       break;
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);
455       break;
456     default:
457       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
458       break;
459   }
460 }
461
462 static void
463 gst_vulkan_swapper_get_property (GObject * object, guint prop_id,
464     GValue * value, GParamSpec * pspec)
465 {
466   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
467
468   switch (prop_id) {
469     case PROP_FORCE_ASPECT_RATIO:
470       g_value_set_boolean (value, swapper->priv->force_aspect_ratio);
471       break;
472     case PROP_PIXEL_ASPECT_RATIO:
473       gst_value_set_fraction (value, swapper->priv->par_n,
474           swapper->priv->par_d);
475       break;
476     default:
477       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
478       break;
479   }
480 }
481
482 static void
483 gst_vulkan_swapper_finalize (GObject * object)
484 {
485   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
486   GstVulkanInstance *instance =
487       gst_vulkan_device_get_instance (swapper->device);
488   int i;
489
490   g_signal_handler_disconnect (swapper->window, swapper->priv->draw_id);
491   swapper->priv->draw_id = 0;
492
493   g_signal_handler_disconnect (swapper->window, swapper->priv->close_id);
494   swapper->priv->close_id = 0;
495
496   g_signal_handler_disconnect (swapper->window, swapper->priv->resize_id);
497   swapper->priv->resize_id = 0;
498
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;
504
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;
509     }
510     g_free (swapper->priv->swap_chain_images);
511   }
512   swapper->priv->swap_chain_images = NULL;
513
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;
518
519   if (swapper->priv->surface) {
520     swapper->priv->DestroySurfaceKHR (instance->instance,
521         swapper->priv->surface, NULL);
522   }
523   swapper->priv->surface = VK_NULL_HANDLE;
524
525   g_free (swapper->priv->surf_present_modes);
526   swapper->priv->surf_present_modes = NULL;
527
528   g_free (swapper->priv->surf_formats);
529   swapper->priv->surf_formats = NULL;
530
531   gst_buffer_replace (&swapper->priv->current_buffer, NULL);
532   gst_caps_replace (&swapper->priv->caps, NULL);
533
534   g_mutex_clear (&swapper->priv->render_lock);
535
536   if (swapper->cmd_pool)
537     gst_object_unref (swapper->cmd_pool);
538   swapper->cmd_pool = NULL;
539
540   if (swapper->queue)
541     gst_object_unref (swapper->queue);
542   swapper->queue = NULL;
543
544   if (swapper->device)
545     gst_object_unref (swapper->device);
546   swapper->device = NULL;
547
548   if (swapper->window)
549     gst_object_unref (swapper->window);
550   swapper->window = NULL;
551
552   gst_object_unref (instance);
553
554   G_OBJECT_CLASS (parent_class)->finalize (object);
555 }
556
557 static void
558 gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
559 {
560   swapper->priv = gst_vulkan_swapper_get_instance_private (swapper);
561
562   g_mutex_init (&swapper->priv->render_lock);
563
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;
567
568   swapper->priv->trash_list = gst_vulkan_trash_fence_list_new ();
569 }
570
571 static void
572 gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
573 {
574   GObjectClass *gobject_class = (GObjectClass *) klass;
575
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;
579
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));
585
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));
590 }
591
592 GstVulkanSwapper *
593 gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
594 {
595   GstVulkanSwapper *swapper;
596
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);
601
602   if (!_get_function_table (swapper)) {
603     gst_object_unref (swapper);
604     return NULL;
605   }
606
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);
613
614   return swapper;
615 }
616
617 GstCaps *
618 gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
619     GError ** error)
620 {
621   GstStructure *s;
622   GstCaps *caps;
623
624   g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
625
626   if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
627     return NULL;
628
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);
633
634   {
635     int i;
636     GValue list = G_VALUE_INIT;
637
638     g_value_init (&list, GST_TYPE_LIST);
639
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);
643     } else {
644       for (i = 0; i < swapper->priv->n_surf_formats; i++) {
645         _add_vk_format_to_list (&list, swapper->priv->surf_formats[i].format);
646       }
647     }
648
649     gst_structure_set_value (s, "format", &list);
650     g_value_unset (&list);
651   }
652
653   {
654     guint32 max_dim =
655         swapper->device->physical_device->properties.limits.maxImageDimension2D;
656
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,
660         G_MAXINT, 1, NULL);
661   }
662
663   GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
664
665   return caps;
666 }
667
668 static gboolean
669 _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
670     GError ** error)
671 {
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;
681   VkFormat format;
682   VkResult err;
683   guint32 i;
684
685   if (!_vulkan_swapper_ensure_surface (swapper, error))
686     return FALSE;
687
688   gpu = gst_vulkan_device_get_physical_device (swapper->device);
689   err =
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)
694     return FALSE;
695
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. */
700     guint width, height;
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;
705   } else {
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;
709   }
710
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;
719       break;
720     }
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;
725     }
726   }
727
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;
736   }
737
738   if (swapper->priv->surf_props.
739       supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
740     preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
741   } else {
742     preTransform = swapper->priv->surf_props.currentTransform;
743   }
744
745   format = _vk_format_from_video_info (&swapper->priv->v_info);
746   color_space = _vk_color_space_from_video_info (&swapper->priv->v_info);
747
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;
754   } else {
755     g_set_error (error, GST_VULKAN_ERROR,
756         VK_ERROR_INITIALIZATION_FAILED,
757         "Incorrect alpha flags available for the swap images");
758     return FALSE;
759   }
760
761   if ((swapper->priv->surf_props.supportedUsageFlags &
762           VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0) {
763     usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
764   } else {
765     g_set_error (error, GST_VULKAN_ERROR,
766         VK_ERROR_INITIALIZATION_FAILED,
767         "Incorrect usage flags available for the swap images");
768     return FALSE;
769   }
770   if ((swapper->priv->
771           surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
772       != 0) {
773     usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
774   } else {
775     g_set_error (error, GST_VULKAN_ERROR,
776         VK_ERROR_INITIALIZATION_FAILED,
777         "Incorrect usage flags available for the swap images");
778     return FALSE;
779   }
780
781   {
782     VkSwapchainCreateInfoKHR swap_chain_info = { 0, };
783     VkSwapchainKHR old_swap_chain;
784
785     old_swap_chain = swapper->priv->swap_chain;
786
787     /* *INDENT-OFF* */
788     swap_chain_info = (VkSwapchainCreateInfoKHR) {
789         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
790         .pNext = NULL,
791         .surface = swapper->priv->surface,
792         .minImageCount = n_images_wanted,
793         .imageFormat = format,
794         .imageColorSpace = color_space,
795         .imageExtent = swapchain_dims,
796         .imageArrayLayers = 1,
797         .imageUsage = usage,
798         .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
799         .queueFamilyIndexCount = 0,
800         .pQueueFamilyIndices = NULL,
801         .preTransform = preTransform,
802         .presentMode = present_mode,
803         .compositeAlpha = alpha_flags,
804         .clipped = TRUE,
805         .oldSwapchain = old_swap_chain
806     };
807     /* *INDENT-ON* */
808
809     err =
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)
813       return FALSE;
814
815     if (old_swap_chain != VK_NULL_HANDLE) {
816       swapper->priv->DestroySwapchainKHR (swapper->device->device,
817           old_swap_chain, NULL);
818     }
819   }
820
821   err =
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)
825     return FALSE;
826
827   swap_chain_images = g_new0 (VkImage, swapper->priv->n_swap_chain_images);
828   err =
829       swapper->priv->GetSwapchainImagesKHR (swapper->device->device,
830       swapper->priv->swap_chain, &swapper->priv->n_swap_chain_images,
831       swap_chain_images);
832   if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
833     g_free (swap_chain_images);
834     return FALSE;
835   }
836
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);
844
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;
851   }
852
853   g_free (swap_chain_images);
854   return TRUE;
855 }
856
857 static gboolean
858 _swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
859 {
860   int i;
861
862   if (!swapper->queue) {
863     if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
864       return FALSE;
865     }
866   }
867
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]);
872     }
873     g_free (swapper->priv->swap_chain_images);
874     swapper->priv->swap_chain_images = NULL;
875   }
876
877   return _allocate_swapchain (swapper, swapper->priv->caps, error);
878 }
879
880
881 static gboolean
882 configure_display_from_info (GstVulkanSwapper * swapper, GstVideoInfo * vinfo)
883 {
884   gint width;
885   gint height;
886   gboolean ok;
887   gint par_n, par_d;
888   gint display_par_n, display_par_d;
889   guint display_ratio_num, display_ratio_den;
890
891   width = GST_VIDEO_INFO_WIDTH (vinfo);
892   height = GST_VIDEO_INFO_HEIGHT (vinfo);
893
894   par_n = GST_VIDEO_INFO_PAR_N (vinfo);
895   par_d = GST_VIDEO_INFO_PAR_D (vinfo);
896
897   if (!par_n)
898     par_n = 1;
899
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;
904   } else {
905     display_par_n = 1;
906     display_par_d = 1;
907   }
908
909   ok = gst_video_calculate_display_ratio (&display_ratio_num,
910       &display_ratio_den, width, height, par_n, par_d, display_par_n,
911       display_par_d);
912
913   if (!ok)
914     return FALSE;
915
916   GST_TRACE_OBJECT (swapper, "PAR: %u/%u DAR:%u/%u", par_n, par_d,
917       display_par_n, display_par_d);
918
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,
923         display_ratio_den);
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);
930   } else {
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,
934         display_ratio_den);
935     swapper->priv->dar_height = height;
936   }
937   GST_DEBUG_OBJECT (swapper, "scaling to %dx%d", swapper->priv->dar_width,
938       swapper->priv->dar_height);
939
940   return TRUE;
941 }
942
943 gboolean
944 gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
945     GError ** error)
946 {
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");
950     return FALSE;
951   }
952
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");
956     return FALSE;
957   }
958
959   gst_caps_replace (&swapper->priv->caps, caps);
960
961   return _swapchain_resize (swapper, error);
962 }
963
964 static gboolean
965 _build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
966     GstBuffer * buffer, GstVulkanCommandBuffer ** cmd_ret, GError ** error)
967 {
968   GstMemory *in_mem;
969   GstVulkanImageMemory *swap_img;
970   GstVulkanCommandBuffer *cmd_buf;
971   GstVideoRectangle src, dst, rslt;
972   VkResult err;
973
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];
976
977   if (!(cmd_buf = gst_vulkan_command_pool_create (swapper->cmd_pool, error)))
978     return FALSE;
979
980   {
981     /* *INDENT-OFF* */
982     VkCommandBufferBeginInfo cmd_buf_info = {
983         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
984         .pNext = NULL,
985         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
986         .pInheritanceInfo = NULL
987     };
988     /* *INDENT-ON* */
989
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)
993       goto unlock_error;
994   }
995
996   {
997     /* *INDENT-OFF* */
998     VkImageMemoryBarrier image_memory_barrier = {
999         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1000         .pNext = NULL,
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
1010     };
1011     /* *INDENT-ON* */
1012
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);
1017
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;
1021   }
1022
1023   src.x = src.y = 0;
1024   src.w = swapper->priv->dar_width;
1025   src.h = swapper->priv->dar_height;
1026
1027   dst.x = dst.y = 0;
1028   dst.w = gst_vulkan_image_memory_get_width (swap_img);
1029   dst.h = gst_vulkan_image_memory_get_height (swap_img);
1030
1031   gst_video_sink_center_rect (src, dst, &rslt,
1032       swapper->priv->force_aspect_ratio);
1033
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,
1036       dst.w, dst.h);
1037
1038   in_mem = gst_buffer_peek_memory (buffer, 0);
1039   {
1040     GstVulkanImageMemory *img_mem = (GstVulkanImageMemory *) in_mem;
1041     /* *INDENT-OFF* */
1042     VkImageBlit blit = {
1043         .srcSubresource = {
1044             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1045             .mipLevel = 0,
1046             .baseArrayLayer = 0,
1047             .layerCount = 1,
1048         },
1049         .srcOffsets = {
1050             {0, 0, 0},
1051             {GST_VIDEO_INFO_WIDTH (&swapper->priv->v_info), GST_VIDEO_INFO_HEIGHT (&swapper->priv->v_info), 1},
1052         },
1053         .dstSubresource = {
1054             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1055             .mipLevel = 0,
1056             .baseArrayLayer = 0,
1057             .layerCount = 1,
1058         },
1059         .dstOffsets = {
1060             {rslt.x, rslt.y, 0},
1061             {rslt.x + rslt.w, rslt.y + rslt.h, 1},
1062         },
1063     };
1064     VkImageMemoryBarrier image_memory_barrier = {
1065         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1066         .pNext = NULL,
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
1076     };
1077     VkClearColorValue clear = {{0.0, 0.0, 0.0, 1.0}};
1078     VkImageSubresourceRange clear_range = {
1079         .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1080         .baseMipLevel = 0,
1081         .levelCount = 1,
1082         .baseArrayLayer = 0,
1083         .layerCount = 1,
1084     };
1085     /* *INDENT-ON* */
1086
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);
1090
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;
1094
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,
1099         VK_FILTER_LINEAR);
1100   }
1101   {
1102     /* *INDENT-OFF* */
1103     VkImageMemoryBarrier image_memory_barrier = {
1104         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1105         .pNext = NULL,
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
1115     };
1116     /* *INDENT-ON* */
1117
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);
1122
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;
1126   }
1127
1128   err = vkEndCommandBuffer (cmd_buf->cmd);
1129   if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
1130     goto unlock_error;
1131   gst_vulkan_command_buffer_unlock (cmd_buf);
1132
1133   *cmd_ret = cmd_buf;
1134
1135   return TRUE;
1136
1137 unlock_error:
1138   gst_vulkan_command_buffer_unlock (cmd_buf);
1139   return FALSE;
1140 }
1141
1142 static gboolean
1143 _render_buffer_unlocked (GstVulkanSwapper * swapper,
1144     GstBuffer * buffer, GError ** error)
1145 {
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;
1152   guint32 swap_idx;
1153   VkResult err, present_err = VK_SUCCESS;
1154
1155   gst_vulkan_trash_list_gc (swapper->priv->trash_list);
1156
1157   if (!buffer) {
1158     g_set_error (error, GST_VULKAN_ERROR,
1159         VK_ERROR_INITIALIZATION_FAILED, "Invalid buffer");
1160     goto error;
1161   }
1162
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");
1166     goto error;
1167   }
1168
1169   gst_buffer_replace (&swapper->priv->current_buffer, buffer);
1170
1171   /* *INDENT-OFF* */
1172   semaphore_info = (VkSemaphoreCreateInfo) {
1173       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
1174       .pNext = NULL,
1175       .flags = 0,
1176   };
1177   /* *INDENT-ON* */
1178
1179 reacquire:
1180   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1181       NULL, &acquire_semaphore);
1182   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1183     goto error;
1184
1185   err =
1186       swapper->priv->AcquireNextImageKHR (swapper->device->device,
1187       swapper->priv->swap_chain, -1, acquire_semaphore, VK_NULL_HANDLE,
1188       &swap_idx);
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");
1192
1193     vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1194     acquire_semaphore = NULL;
1195     if (!_swapchain_resize (swapper, error))
1196       goto error;
1197     goto reacquire;
1198   } else if (gst_vulkan_error_to_g_error (err, error,
1199           "vkAcquireNextImageKHR") < 0) {
1200     goto error;
1201   }
1202
1203   if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd_buf, error))
1204     goto error;
1205
1206   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1207       NULL, &present_semaphore);
1208   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1209     goto error;
1210
1211   {
1212     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1213     VkSubmitInfo submit_info = { 0, };
1214
1215     /* *INDENT-OFF* */
1216     submit_info = (VkSubmitInfo) {
1217         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1218         .pNext = NULL,
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,
1226     };
1227     /* *INDENT-ON* */
1228
1229     fence = gst_vulkan_fence_new (swapper->device, 0, error);
1230     if (!fence)
1231       goto error;
1232
1233     err =
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)
1237       goto error;
1238
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;
1245
1246     gst_vulkan_command_buffer_unlock (cmd_buf);
1247     cmd_buf = NULL;
1248     fence = NULL;
1249   }
1250
1251   /* *INDENT-OFF* */
1252   present = (VkPresentInfoKHR) {
1253       .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1254       .pNext = NULL,
1255       .waitSemaphoreCount = 1,
1256       .pWaitSemaphores = &present_semaphore,
1257       .swapchainCount = 1,
1258       .pSwapchains = &swapper->priv->swap_chain,
1259       .pImageIndices = &swap_idx,
1260       .pResults = &present_err,
1261   };
1262   /* *INDENT-ON* */
1263
1264   err = swapper->priv->QueuePresentKHR (swapper->queue->queue, &present);
1265
1266   if (present_err == VK_ERROR_OUT_OF_DATE_KHR) {
1267     GST_DEBUG_OBJECT (swapper, "out of date frame submitted");
1268
1269     if (!_swapchain_resize (swapper, error))
1270       goto error;
1271   } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
1272     goto error;
1273
1274   {
1275     VkSubmitInfo submit_info = { 0, };
1276     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1277
1278     /* *INDENT-OFF* */
1279     submit_info = (VkSubmitInfo) {
1280         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1281         .pWaitDstStageMask = &stages,
1282         0,
1283     };
1284     /* *INDENT-ON* */
1285
1286     fence = gst_vulkan_fence_new (swapper->device, 0, error);
1287     if (!fence)
1288       goto error;
1289
1290     err =
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)
1294       goto error;
1295
1296     gst_vulkan_trash_list_add (swapper->priv->trash_list,
1297         gst_vulkan_trash_new_free_semaphore (fence, present_semaphore));
1298     fence = NULL;
1299   }
1300
1301   return TRUE;
1302
1303 error:
1304   {
1305     if (acquire_semaphore)
1306       vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1307     if (present_semaphore)
1308       vkDestroySemaphore (swapper->device->device, present_semaphore, NULL);
1309     if (cmd_buf) {
1310       gst_vulkan_command_buffer_unlock (cmd_buf);
1311       gst_vulkan_command_buffer_unref (cmd_buf);
1312     }
1313     return FALSE;
1314   }
1315 }
1316
1317 gboolean
1318 gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
1319     GstBuffer * buffer, GError ** error)
1320 {
1321   GstMemory *mem;
1322   gboolean ret;
1323
1324   mem = gst_buffer_peek_memory (buffer, 0);
1325   if (!mem) {
1326     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1327         "Buffer has no memory");
1328     return FALSE;
1329   }
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");
1333     return FALSE;
1334   }
1335
1336   RENDER_LOCK (swapper);
1337   ret = _render_buffer_unlocked (swapper, buffer, error);
1338   RENDER_UNLOCK (swapper);
1339
1340   return ret;
1341 }
1342
1343 static void
1344 _on_window_draw (GstVulkanWindow * window, GstVulkanSwapper * swapper)
1345 {
1346   GError *error = NULL;
1347
1348   RENDER_LOCK (swapper);
1349   if (!swapper->priv->current_buffer) {
1350     GST_DEBUG_OBJECT (swapper, "No buffer to render");
1351     RENDER_UNLOCK (swapper);
1352     return;
1353   }
1354
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);
1361 }
1362
1363 static void
1364 _on_window_resize (GstVulkanWindow * window, guint width, guint height,
1365     GstVulkanSwapper * swapper)
1366 {
1367   GError *error = NULL;
1368
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",
1373           error->message);
1374     g_clear_error (&error);
1375   }
1376   RENDER_UNLOCK (swapper);
1377 }