• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /**
30  * SECTION:vkswapper
31  * @title: GstVulkanSwapper
32  * @short_description: Vulkan helper object for rendering to a surface
33  * @see_also: #GstVulkanWindow, #GstVulkanQueue
34  *
35  * #GstVulkanSwapper is a helper object for rendering to a surface exposed by
36  * #GstVulkanWindow.
37  */
38 
39 #define GST_CAT_DEFAULT gst_vulkan_swapper_debug
40 GST_DEBUG_CATEGORY (GST_CAT_DEFAULT);
41 
42 struct _GstVulkanSwapperPrivate
43 {
44   VkSurfaceKHR surface;
45 
46   VkSurfaceCapabilitiesKHR surf_props;
47   VkSurfaceFormatKHR *surf_formats;
48   guint32 n_surf_formats;
49   VkPresentModeKHR *surf_present_modes;
50   guint32 n_surf_present_modes;
51 
52   VkSwapchainKHR swap_chain;
53   GstVulkanImageMemory **swap_chain_images;
54   guint32 n_swap_chain_images;
55 
56   GstCaps *caps;
57   GstVideoInfo v_info;
58 
59   PFN_vkGetPhysicalDeviceSurfaceSupportKHR GetPhysicalDeviceSurfaceSupportKHR;
60     PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR
61       GetPhysicalDeviceSurfaceCapabilitiesKHR;
62   PFN_vkGetPhysicalDeviceSurfaceFormatsKHR GetPhysicalDeviceSurfaceFormatsKHR;
63     PFN_vkGetPhysicalDeviceSurfacePresentModesKHR
64       GetPhysicalDeviceSurfacePresentModesKHR;
65   PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
66   PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
67   PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
68   PFN_vkAcquireNextImageKHR AcquireNextImageKHR;
69   PFN_vkQueuePresentKHR QueuePresentKHR;
70   PFN_vkDestroySurfaceKHR DestroySurfaceKHR;
71 
72   /* <private> */
73   /* runtime variables */
74   gint to_quit;
75   GstBuffer *current_buffer;
76   gboolean any_current_extent;
77 
78   /* signal handlers */
79   gulong close_id;
80   gulong draw_id;
81   gulong resize_id;
82 
83   /* properties */
84   gboolean force_aspect_ratio;
85   gint par_n;
86   gint par_d;
87 
88   GMutex render_lock;
89 
90   GstVulkanTrashList *trash_list;
91 
92   /* source sizes accounting for all aspect ratios */
93   guint dar_width;
94   guint dar_height;
95 
96   GstVideoRectangle surface_location;
97   GstVideoRectangle display_rect;
98 };
99 
100 enum
101 {
102   PROP_0,
103   PROP_FORCE_ASPECT_RATIO,
104   PROP_PIXEL_ASPECT_RATIO,
105 };
106 
107 #define DEFAULT_FORCE_ASPECT_RATIO TRUE
108 #define DEFAULT_PIXEL_ASPECT_RATIO_N 0
109 #define DEFAULT_PIXEL_ASPECT_RATIO_D 1
110 
111 #define GET_PRIV(swapper) gst_vulkan_swapper_get_instance_private (swapper)
112 
113 #define gst_vulkan_swapper_parent_class parent_class
114 G_DEFINE_TYPE_WITH_CODE (GstVulkanSwapper, gst_vulkan_swapper,
115     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanSwapper)
116     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
117         "vulkanswapper", 0, "Vulkan Swapper"));
118 
119 static void _on_window_draw (GstVulkanWindow * window,
120     GstVulkanSwapper * swapper);
121 static void _on_window_resize (GstVulkanWindow * window,
122     guint width, guint height, GstVulkanSwapper * swapper);
123 
124 static inline GMutex *
render_get_lock(gpointer swapper)125 render_get_lock (gpointer swapper)
126 {
127   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
128   return &priv->render_lock;
129 }
130 
131 #define RENDER_LOCK(o) g_mutex_lock (render_get_lock(o));
132 #define RENDER_UNLOCK(o) g_mutex_unlock (render_get_lock(o));
133 
134 static gboolean
_get_function_table(GstVulkanSwapper * swapper)135 _get_function_table (GstVulkanSwapper * swapper)
136 {
137   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
138   GstVulkanDevice *device = swapper->device;
139   GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
140 
141   if (!instance) {
142     GST_ERROR_OBJECT (swapper, "Failed to get instance from the device");
143     return FALSE;
144   }
145 
146   if (!gst_vulkan_device_is_extension_enabled (device,
147           VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
148     GST_ERROR_OBJECT (swapper, "Required extension \'%s\' is not enabled on "
149         "device %" GST_PTR_FORMAT, VK_KHR_SWAPCHAIN_EXTENSION_NAME, device);
150     return FALSE;
151   }
152 #define GET_PROC_ADDRESS_REQUIRED(type, name) \
153   G_STMT_START { \
154     priv->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \
155     if (!priv->G_PASTE(, name)) { \
156       GST_ERROR_OBJECT (swapper, "Failed to find required function vk" G_STRINGIFY(name)); \
157       gst_object_unref (instance); \
158       return FALSE; \
159     } \
160   } G_STMT_END
161 
162   GET_PROC_ADDRESS_REQUIRED (instance, GetPhysicalDeviceSurfaceSupportKHR);
163   GET_PROC_ADDRESS_REQUIRED (instance, GetPhysicalDeviceSurfaceCapabilitiesKHR);
164   GET_PROC_ADDRESS_REQUIRED (instance, GetPhysicalDeviceSurfaceFormatsKHR);
165   GET_PROC_ADDRESS_REQUIRED (instance, GetPhysicalDeviceSurfacePresentModesKHR);
166   GET_PROC_ADDRESS_REQUIRED (instance, DestroySurfaceKHR);
167   GET_PROC_ADDRESS_REQUIRED (device, CreateSwapchainKHR);
168   GET_PROC_ADDRESS_REQUIRED (device, DestroySwapchainKHR);
169   GET_PROC_ADDRESS_REQUIRED (device, GetSwapchainImagesKHR);
170   GET_PROC_ADDRESS_REQUIRED (device, AcquireNextImageKHR);
171   GET_PROC_ADDRESS_REQUIRED (device, QueuePresentKHR);
172 
173   gst_object_unref (instance);
174 
175   return TRUE;
176 
177 #undef GET_PROC_ADDRESS_REQUIRED
178 }
179 
180 static GstVideoFormat
_vk_format_to_video_format(VkFormat format)181 _vk_format_to_video_format (VkFormat format)
182 {
183   switch (format) {
184       /* double check endianness */
185     case VK_FORMAT_R8G8B8A8_UNORM:
186       return GST_VIDEO_FORMAT_RGBA;
187     case VK_FORMAT_R8G8B8_UNORM:
188       return GST_VIDEO_FORMAT_RGB;
189     case VK_FORMAT_B8G8R8A8_UNORM:
190       return GST_VIDEO_FORMAT_BGRA;
191     case VK_FORMAT_B8G8R8_UNORM:
192       return GST_VIDEO_FORMAT_BGR;
193     default:
194       return GST_VIDEO_FORMAT_UNKNOWN;
195   }
196 }
197 
198 static VkColorSpaceKHR
_vk_color_space_from_video_info(GstVideoInfo * v_info)199 _vk_color_space_from_video_info (GstVideoInfo * v_info)
200 {
201   return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
202 }
203 
204 static void
_add_vk_format_to_list(GValue * list,VkFormat format)205 _add_vk_format_to_list (GValue * list, VkFormat format)
206 {
207   GstVideoFormat v_format;
208 
209   v_format = _vk_format_to_video_format (format);
210   if (v_format) {
211     const gchar *format_str = gst_video_format_to_string (v_format);
212     GValue item = G_VALUE_INIT;
213     GValue new_list = G_VALUE_INIT;
214 
215     g_value_init (&item, G_TYPE_STRING);
216     g_value_set_string (&item, format_str);
217     gst_value_list_merge (&new_list, list, &item);
218     g_value_unset (&item);
219 
220     g_value_unset (list);
221     *list = new_list;
222   }
223 }
224 
225 static gboolean
_vulkan_swapper_ensure_surface(GstVulkanSwapper * swapper,GError ** error)226 _vulkan_swapper_ensure_surface (GstVulkanSwapper * swapper, GError ** error)
227 {
228   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
229 
230   if (!priv->surface) {
231     if (!(priv->surface =
232             gst_vulkan_window_get_surface (swapper->window, error))) {
233       return FALSE;
234     }
235   }
236 
237   return TRUE;
238 }
239 
240 struct choose_data
241 {
242   GstVulkanSwapper *swapper;
243   GstVulkanQueue *graphics_queue;
244   GstVulkanQueue *present_queue;
245 };
246 
247 static gboolean
_choose_queue(GstVulkanDevice * device,GstVulkanQueue * queue,struct choose_data * data)248 _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue,
249     struct choose_data *data)
250 {
251   GstVulkanSwapperPrivate *priv = GET_PRIV (data->swapper);
252   guint flags =
253       device->physical_device->queue_family_props[queue->family].queueFlags;
254   VkPhysicalDevice gpu;
255   gboolean supports_present;
256 
257   gpu = gst_vulkan_device_get_physical_device (data->swapper->device);
258 
259   {
260     VkResult err;
261     GError *error = NULL;
262     VkBool32 physical_device_supported;
263 
264     err =
265         priv->GetPhysicalDeviceSurfaceSupportKHR (gpu,
266         queue->index, priv->surface, &physical_device_supported);
267     if (gst_vulkan_error_to_g_error (err, &error,
268             "GetPhysicalDeviceSurfaceSupport") < 0) {
269       GST_DEBUG_OBJECT (data->swapper,
270           "surface not supported by the physical device: %s", error->message);
271       g_clear_error (&error);
272       return TRUE;
273     }
274   }
275 
276   supports_present =
277       gst_vulkan_window_get_presentation_support (data->swapper->window,
278       device, queue->index);
279 
280   if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) {
281     if (supports_present) {
282       /* found one that supports both */
283       if (data->graphics_queue)
284         gst_object_unref (data->graphics_queue);
285       data->graphics_queue = gst_object_ref (queue);
286       if (data->present_queue)
287         gst_object_unref (data->present_queue);
288       data->present_queue = gst_object_ref (queue);
289       return FALSE;
290     }
291     if (!data->graphics_queue)
292       data->present_queue = gst_object_ref (queue);
293   } else if (supports_present) {
294     if (!data->present_queue)
295       data->present_queue = gst_object_ref (queue);
296   }
297 
298   return TRUE;
299 }
300 
301 /*
302  * gst_vulkan_swapper_choose_queue:
303  * @swapper: a #GstVulkanSwapper
304  * @available_queue: (transfer none): a #GstVulkanQueue chosen elsewhere
305  * @error: a #GError
306  */
307 gboolean
gst_vulkan_swapper_choose_queue(GstVulkanSwapper * swapper,GstVulkanQueue * available_queue,GError ** error)308 gst_vulkan_swapper_choose_queue (GstVulkanSwapper * swapper,
309     GstVulkanQueue * available_queue, GError ** error)
310 {
311   if (!_vulkan_swapper_ensure_surface (swapper, error))
312     return FALSE;
313 
314   if (swapper->queue)
315     return TRUE;
316 
317   if (available_queue) {
318     guint flags =
319         swapper->device->physical_device->
320         queue_family_props[available_queue->family].queueFlags;
321     gboolean supports_present;
322 
323     supports_present =
324         gst_vulkan_window_get_presentation_support (swapper->window,
325         swapper->device, available_queue->index);
326     if (supports_present && flags & VK_QUEUE_GRAPHICS_BIT)
327       swapper->queue = gst_object_ref (available_queue);
328   }
329 
330   if (!swapper->queue) {
331     struct choose_data data;
332 
333     data.swapper = swapper;
334     data.present_queue = NULL;
335     data.graphics_queue = NULL;
336 
337     gst_vulkan_device_foreach_queue (swapper->device,
338         (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data);
339 
340     if (data.graphics_queue != data.present_queue) {
341       /* FIXME: add support for separate graphics/present queues */
342       g_set_error (error, GST_VULKAN_ERROR,
343           VK_ERROR_INITIALIZATION_FAILED,
344           "Failed to find a compatible present/graphics queue");
345       if (data.present_queue)
346         gst_object_unref (data.present_queue);
347       if (data.graphics_queue)
348         gst_object_unref (data.graphics_queue);
349       return FALSE;
350     }
351 
352     swapper->queue = gst_object_ref (data.present_queue);
353     if (data.present_queue)
354       gst_object_unref (data.present_queue);
355     if (data.graphics_queue)
356       gst_object_unref (data.graphics_queue);
357   }
358 
359   return TRUE;
360 }
361 
362 static void
dump_surface_properties(GstVulkanSwapper * swapper)363 dump_surface_properties (GstVulkanSwapper * swapper)
364 {
365   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
366 
367   GST_TRACE_OBJECT (swapper, "surface %"
368       GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT ", n images [%"
369       G_GUINT32_FORMAT ", %" G_GUINT32_FORMAT "], extent [%"
370       GST_VULKAN_EXTENT2D_FORMAT ", %" GST_VULKAN_EXTENT2D_FORMAT
371       "], max layers %" G_GUINT32_FORMAT " transforms supported 0x%x "
372       "current transform 0x%x, alpha flags 0x%x, "
373       "supported image usage flags 0x%x", priv->surface,
374       priv->surf_props.minImageCount,
375       priv->surf_props.maxImageCount,
376       GST_VULKAN_EXTENT2D_ARGS (priv->surf_props.minImageExtent),
377       GST_VULKAN_EXTENT2D_ARGS (priv->surf_props.maxImageExtent),
378       priv->surf_props.maxImageArrayLayers,
379       priv->surf_props.supportedTransforms,
380       priv->surf_props.currentTransform,
381       priv->surf_props.supportedCompositeAlpha,
382       priv->surf_props.supportedUsageFlags);
383 }
384 
385 static void
dump_surface_formats(GstVulkanSwapper * swapper)386 dump_surface_formats (GstVulkanSwapper * swapper)
387 {
388   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
389   int i;
390 
391   for (i = 0; i < priv->n_surf_formats; i++) {
392     GST_DEBUG_OBJECT (swapper, "surface %"
393         GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT
394         " format 0x%x colorspace 0x%x", priv->surface,
395         priv->surf_formats[i].format, priv->surf_formats[i].colorSpace);
396   }
397 }
398 
399 static void
dump_surface_present_modes(GstVulkanSwapper * swapper)400 dump_surface_present_modes (GstVulkanSwapper * swapper)
401 {
402   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
403   int i;
404 
405   for (i = 0; i < priv->n_surf_present_modes; i++) {
406     GST_DEBUG_OBJECT (swapper, "surface %"
407         GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT " present modes 0x%x",
408         priv->surface, priv->surf_present_modes[i]);
409   }
410 }
411 
412 static gboolean
_vulkan_swapper_retrieve_surface_properties(GstVulkanSwapper * swapper,GError ** error)413 _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
414     GError ** error)
415 {
416   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
417   VkPhysicalDevice gpu;
418   VkResult err;
419 
420   if (priv->surf_formats)
421     return TRUE;
422 
423   gpu = gst_vulkan_device_get_physical_device (swapper->device);
424 
425   if (!gst_vulkan_swapper_choose_queue (swapper, NULL, error))
426     return FALSE;
427 
428   if (!(swapper->cmd_pool =
429           gst_vulkan_queue_create_command_pool (swapper->queue, error)))
430     return FALSE;
431 
432   err =
433       priv->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu,
434       priv->surface, &priv->surf_props);
435   if (gst_vulkan_error_to_g_error (err, error,
436           "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
437     return FALSE;
438 
439   dump_surface_properties (swapper);
440 
441   err =
442       priv->GetPhysicalDeviceSurfaceFormatsKHR (gpu,
443       priv->surface, &priv->n_surf_formats, NULL);
444   if (gst_vulkan_error_to_g_error (err, error,
445           "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
446     return FALSE;
447 
448   priv->surf_formats = g_new0 (VkSurfaceFormatKHR, priv->n_surf_formats);
449   err =
450       priv->GetPhysicalDeviceSurfaceFormatsKHR (gpu,
451       priv->surface, &priv->n_surf_formats, priv->surf_formats);
452   if (gst_vulkan_error_to_g_error (err, error,
453           "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
454     return FALSE;
455 
456   dump_surface_formats (swapper);
457 
458   err =
459       priv->GetPhysicalDeviceSurfacePresentModesKHR (gpu,
460       priv->surface, &priv->n_surf_present_modes, NULL);
461   if (gst_vulkan_error_to_g_error (err, error,
462           "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
463     return FALSE;
464 
465   priv->surf_present_modes =
466       g_new0 (VkPresentModeKHR, priv->n_surf_present_modes);
467   err =
468       priv->GetPhysicalDeviceSurfacePresentModesKHR (gpu,
469       priv->surface, &priv->n_surf_present_modes, priv->surf_present_modes);
470   if (gst_vulkan_error_to_g_error (err, error,
471           "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
472     return FALSE;
473 
474   dump_surface_present_modes (swapper);
475 
476   return TRUE;
477 }
478 
479 static gboolean
_on_window_close(GstVulkanWindow * window,GstVulkanSwapper * swapper)480 _on_window_close (GstVulkanWindow * window, GstVulkanSwapper * swapper)
481 {
482   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
483 
484   g_atomic_int_set (&priv->to_quit, 1);
485 
486   return TRUE;
487 }
488 
489 static void
gst_vulkan_swapper_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)490 gst_vulkan_swapper_set_property (GObject * object, guint prop_id,
491     const GValue * value, GParamSpec * pspec)
492 {
493   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
494   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
495 
496   switch (prop_id) {
497     case PROP_FORCE_ASPECT_RATIO:
498       priv->force_aspect_ratio = g_value_get_boolean (value);
499       break;
500     case PROP_PIXEL_ASPECT_RATIO:
501       priv->par_n = gst_value_get_fraction_numerator (value);
502       priv->par_d = gst_value_get_fraction_denominator (value);
503       break;
504     default:
505       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
506       break;
507   }
508 }
509 
510 static void
gst_vulkan_swapper_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)511 gst_vulkan_swapper_get_property (GObject * object, guint prop_id,
512     GValue * value, GParamSpec * pspec)
513 {
514   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
515   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
516 
517   switch (prop_id) {
518     case PROP_FORCE_ASPECT_RATIO:
519       g_value_set_boolean (value, priv->force_aspect_ratio);
520       break;
521     case PROP_PIXEL_ASPECT_RATIO:
522       gst_value_set_fraction (value, priv->par_n, priv->par_d);
523       break;
524     default:
525       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
526       break;
527   }
528 }
529 
530 static void
gst_vulkan_swapper_finalize(GObject * object)531 gst_vulkan_swapper_finalize (GObject * object)
532 {
533   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
534   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
535   GstVulkanInstance *instance =
536       gst_vulkan_device_get_instance (swapper->device);
537   int i;
538 
539   g_signal_handler_disconnect (swapper->window, priv->draw_id);
540   priv->draw_id = 0;
541 
542   g_signal_handler_disconnect (swapper->window, priv->close_id);
543   priv->close_id = 0;
544 
545   g_signal_handler_disconnect (swapper->window, priv->resize_id);
546   priv->resize_id = 0;
547 
548   if (!gst_vulkan_trash_list_wait (priv->trash_list, -1))
549     GST_WARNING_OBJECT (swapper, "Failed to wait for all fences to complete "
550         "before shutting down");
551   gst_object_unref (priv->trash_list);
552   priv->trash_list = NULL;
553 
554   if (priv->swap_chain_images) {
555     for (i = 0; i < priv->n_swap_chain_images; i++) {
556       gst_memory_unref ((GstMemory *) priv->swap_chain_images[i]);
557       priv->swap_chain_images[i] = NULL;
558     }
559     g_free (priv->swap_chain_images);
560   }
561   priv->swap_chain_images = NULL;
562 
563   if (priv->swap_chain)
564     priv->DestroySwapchainKHR (swapper->device->device, priv->swap_chain, NULL);
565   priv->swap_chain = VK_NULL_HANDLE;
566 
567   if (priv->surface) {
568     priv->DestroySurfaceKHR (instance->instance, priv->surface, NULL);
569   }
570   priv->surface = VK_NULL_HANDLE;
571 
572   g_free (priv->surf_present_modes);
573   priv->surf_present_modes = NULL;
574 
575   g_free (priv->surf_formats);
576   priv->surf_formats = NULL;
577 
578   gst_buffer_replace (&priv->current_buffer, NULL);
579   gst_caps_replace (&priv->caps, NULL);
580 
581   g_mutex_clear (&priv->render_lock);
582 
583   if (swapper->cmd_pool)
584     gst_object_unref (swapper->cmd_pool);
585   swapper->cmd_pool = NULL;
586 
587   if (swapper->queue)
588     gst_object_unref (swapper->queue);
589   swapper->queue = NULL;
590 
591   if (swapper->device)
592     gst_object_unref (swapper->device);
593   swapper->device = NULL;
594 
595   if (swapper->window)
596     gst_object_unref (swapper->window);
597   swapper->window = NULL;
598 
599   gst_object_unref (instance);
600 
601   G_OBJECT_CLASS (parent_class)->finalize (object);
602 }
603 
604 static void
gst_vulkan_swapper_init(GstVulkanSwapper * swapper)605 gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
606 {
607   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
608 
609   g_mutex_init (&priv->render_lock);
610 
611   priv->force_aspect_ratio = DEFAULT_FORCE_ASPECT_RATIO;
612   priv->par_n = DEFAULT_PIXEL_ASPECT_RATIO_N;
613   priv->par_d = DEFAULT_PIXEL_ASPECT_RATIO_D;
614 
615   priv->trash_list = gst_vulkan_trash_fence_list_new ();
616 }
617 
618 static void
gst_vulkan_swapper_class_init(GstVulkanSwapperClass * klass)619 gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
620 {
621   GObjectClass *gobject_class = (GObjectClass *) klass;
622 
623   gobject_class->set_property = gst_vulkan_swapper_set_property;
624   gobject_class->get_property = gst_vulkan_swapper_get_property;
625   gobject_class->finalize = gst_vulkan_swapper_finalize;
626 
627   g_object_class_install_property (gobject_class, PROP_FORCE_ASPECT_RATIO,
628       g_param_spec_boolean ("force-aspect-ratio", "Force aspect ratio",
629           "When enabled, scaling will respect original aspect ratio",
630           DEFAULT_FORCE_ASPECT_RATIO,
631           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
632 
633   g_object_class_install_property (gobject_class, PROP_PIXEL_ASPECT_RATIO,
634       gst_param_spec_fraction ("pixel-aspect-ratio", "Pixel Aspect Ratio",
635           "The pixel aspect ratio of the device", 0, 1, G_MAXINT, 1, 1, 1,
636           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
637 }
638 
639 GstVulkanSwapper *
gst_vulkan_swapper_new(GstVulkanDevice * device,GstVulkanWindow * window)640 gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
641 {
642   GstVulkanSwapper *swapper;
643   GstVulkanSwapperPrivate *priv;
644 
645   swapper = g_object_new (GST_TYPE_VULKAN_SWAPPER, NULL);
646   gst_object_ref_sink (swapper);
647   swapper->device = gst_object_ref (device);
648   swapper->window = gst_object_ref (window);
649 
650   if (!_get_function_table (swapper)) {
651     gst_object_unref (swapper);
652     return NULL;
653   }
654   priv = GET_PRIV (swapper);
655 
656   priv->close_id = g_signal_connect (swapper->window, "close",
657       (GCallback) _on_window_close, swapper);
658   priv->draw_id = g_signal_connect (swapper->window, "draw",
659       (GCallback) _on_window_draw, swapper);
660   priv->resize_id = g_signal_connect (swapper->window, "resize",
661       (GCallback) _on_window_resize, swapper);
662 
663   return swapper;
664 }
665 
666 GstCaps *
gst_vulkan_swapper_get_supported_caps(GstVulkanSwapper * swapper,GError ** error)667 gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
668     GError ** error)
669 {
670   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
671   GstStructure *s;
672   GstCaps *caps;
673 
674   g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
675 
676   if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
677     return NULL;
678 
679   caps = gst_caps_new_empty_simple ("video/x-raw");
680   gst_caps_set_features (caps, 0,
681       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE));
682   s = gst_caps_get_structure (caps, 0);
683 
684   {
685     int i;
686     GValue list = G_VALUE_INIT;
687 
688     g_value_init (&list, GST_TYPE_LIST);
689 
690     if (priv->n_surf_formats
691         && priv->surf_formats[0].format == VK_FORMAT_UNDEFINED) {
692       _add_vk_format_to_list (&list, VK_FORMAT_B8G8R8A8_UNORM);
693     } else {
694       for (i = 0; i < priv->n_surf_formats; i++) {
695         _add_vk_format_to_list (&list, priv->surf_formats[i].format);
696       }
697     }
698 
699     gst_structure_set_value (s, "format", &list);
700     g_value_unset (&list);
701   }
702 
703   {
704     guint32 max_dim =
705         swapper->device->physical_device->properties.limits.maxImageDimension2D;
706 
707     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, (gint) max_dim,
708         "height", GST_TYPE_INT_RANGE, 1, (gint) max_dim, "pixel-aspect-ratio",
709         GST_TYPE_FRACTION, 1, 1, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
710         G_MAXINT, 1, NULL);
711   }
712 
713   GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
714 
715   return caps;
716 }
717 
718 static gboolean
_allocate_swapchain(GstVulkanSwapper * swapper,GstCaps * caps,GError ** error)719 _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
720     GError ** error)
721 {
722   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
723   VkSurfaceTransformFlagsKHR preTransform;
724   VkCompositeAlphaFlagsKHR alpha_flags;
725   VkPresentModeKHR present_mode;
726   VkImageUsageFlags usage = 0;
727   VkColorSpaceKHR color_space;
728   VkImage *swap_chain_images;
729   VkExtent2D swapchain_dims;
730   guint32 n_images_wanted;
731   VkPhysicalDevice gpu;
732   VkFormat format;
733   VkResult err;
734   guint32 i;
735 
736   if (!_vulkan_swapper_ensure_surface (swapper, error))
737     return FALSE;
738 
739   gpu = gst_vulkan_device_get_physical_device (swapper->device);
740   err =
741       priv->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu,
742       priv->surface, &priv->surf_props);
743   if (gst_vulkan_error_to_g_error (err, error,
744           "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
745     return FALSE;
746 
747   /* width and height are either both -1, or both not -1. */
748   if (priv->surf_props.currentExtent.width == -1) {
749     /* If the surface size is undefined, the size is set to
750      * the size of the images requested. */
751     guint width, height;
752     gst_vulkan_window_get_surface_dimensions (swapper->window, &width, &height);
753     swapchain_dims.width = width;
754     swapchain_dims.height = height;
755     priv->any_current_extent = TRUE;
756     GST_DEBUG_OBJECT (swapper, "using requested swapchain dimensions %ux%u "
757         "from window", width, height);
758   } else {
759     /* If the surface size is defined, the swap chain size must match */
760     swapchain_dims = priv->surf_props.currentExtent;
761     priv->any_current_extent = FALSE;
762     GST_DEBUG_OBJECT (swapper, "using current swapchain dimensions %ux%u",
763         priv->surf_props.currentExtent.width,
764         priv->surf_props.currentExtent.height);
765   }
766   priv->surface_location.w = swapchain_dims.width;
767   priv->surface_location.h = swapchain_dims.height;
768 
769   /* If mailbox mode is available, use it, as is the lowest-latency non-
770    * tearing mode.  If not, try IMMEDIATE which will usually be available,
771    * and is fastest (though it tears).  If not, fall back to FIFO which is
772    * always available. */
773   present_mode = VK_PRESENT_MODE_FIFO_KHR;
774   for (i = 0; i < priv->n_surf_present_modes; i++) {
775     GST_TRACE_OBJECT (swapper,
776         "surface %" GST_VULKAN_NON_DISPATCHABLE_HANDLE_FORMAT
777         " has present mode \'%s\' (0x%x)", priv->surface,
778         gst_vulkan_present_mode_to_string (priv->surf_present_modes[i]),
779         priv->surf_present_modes[i]);
780 
781     if (priv->surf_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
782       present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
783       break;
784     }
785     if ((present_mode != VK_PRESENT_MODE_MAILBOX_KHR) &&
786         (priv->surf_present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
787       present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
788     }
789   }
790   GST_DEBUG_OBJECT (swapper, "using present mode \'%s\'",
791       gst_vulkan_present_mode_to_string (present_mode));
792 
793   /* Determine the number of VkImage's to use in the swap chain (we desire to
794    * own only 1 image at a time, besides the images being displayed and
795    * queued for display): */
796   n_images_wanted = priv->surf_props.minImageCount + 1;
797   if ((priv->surf_props.maxImageCount > 0) &&
798       (n_images_wanted > priv->surf_props.maxImageCount)) {
799     /* Application must settle for fewer images than desired: */
800     n_images_wanted = priv->surf_props.maxImageCount;
801   }
802 
803   if (priv->surf_props.
804       supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
805     preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
806   } else {
807     preTransform = priv->surf_props.currentTransform;
808   }
809 
810   format = gst_vulkan_format_from_video_info (&priv->v_info, 0);
811   color_space = _vk_color_space_from_video_info (&priv->v_info);
812 
813   if ((priv->surf_props.supportedCompositeAlpha &
814           VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0) {
815     alpha_flags = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
816   } else if ((priv->surf_props.supportedCompositeAlpha &
817           VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0) {
818     alpha_flags = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
819   } else if ((priv->surf_props.supportedCompositeAlpha &
820           VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) != 0) {
821     alpha_flags = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
822   } else {
823     g_set_error (error, GST_VULKAN_ERROR,
824         VK_ERROR_INITIALIZATION_FAILED,
825         "Incorrect alpha flags (0x%x) available for the swap images",
826         priv->surf_props.supportedCompositeAlpha);
827     return FALSE;
828   }
829 
830   if ((priv->surf_props.supportedUsageFlags &
831           VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0) {
832     usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
833   } else {
834     g_set_error (error, GST_VULKAN_ERROR,
835         VK_ERROR_INITIALIZATION_FAILED,
836         "Incorrect usage flags (0x%x) available for the swap images",
837         priv->surf_props.supportedUsageFlags);
838     return FALSE;
839   }
840   if ((priv->
841           surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
842       != 0) {
843     usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
844   } else {
845     g_set_error (error, GST_VULKAN_ERROR,
846         VK_ERROR_INITIALIZATION_FAILED,
847         "Incorrect usage flags (0x%x) available for the swap images",
848         priv->surf_props.supportedUsageFlags);
849     return FALSE;
850   }
851 
852   {
853     VkSwapchainCreateInfoKHR swap_chain_info = { 0, };
854     VkSwapchainKHR old_swap_chain;
855 
856     old_swap_chain = priv->swap_chain;
857 
858     /* *INDENT-OFF* */
859     swap_chain_info = (VkSwapchainCreateInfoKHR) {
860         .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
861         .pNext = NULL,
862         .surface = priv->surface,
863         .minImageCount = n_images_wanted,
864         .imageFormat = format,
865         .imageColorSpace = color_space,
866         .imageExtent = swapchain_dims,
867         .imageArrayLayers = 1,
868         .imageUsage = usage,
869         .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
870         .queueFamilyIndexCount = 0,
871         .pQueueFamilyIndices = NULL,
872         .preTransform = preTransform,
873         .presentMode = present_mode,
874         .compositeAlpha = alpha_flags,
875         .clipped = TRUE,
876         .oldSwapchain = old_swap_chain
877     };
878     /* *INDENT-ON* */
879 
880     err =
881         priv->CreateSwapchainKHR (swapper->device->device,
882         &swap_chain_info, NULL, &priv->swap_chain);
883     if (gst_vulkan_error_to_g_error (err, error, "vkCreateSwapchainKHR") < 0)
884       return FALSE;
885 
886     if (old_swap_chain != VK_NULL_HANDLE) {
887       priv->DestroySwapchainKHR (swapper->device->device, old_swap_chain, NULL);
888     }
889   }
890 
891   err =
892       priv->GetSwapchainImagesKHR (swapper->device->device,
893       priv->swap_chain, &priv->n_swap_chain_images, NULL);
894   if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0)
895     return FALSE;
896 
897   swap_chain_images = g_new0 (VkImage, priv->n_swap_chain_images);
898   err =
899       priv->GetSwapchainImagesKHR (swapper->device->device,
900       priv->swap_chain, &priv->n_swap_chain_images, swap_chain_images);
901   if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
902     g_free (swap_chain_images);
903     return FALSE;
904   }
905 
906   priv->swap_chain_images =
907       g_new0 (GstVulkanImageMemory *, priv->n_swap_chain_images);
908   for (i = 0; i < priv->n_swap_chain_images; i++) {
909     priv->swap_chain_images[i] = (GstVulkanImageMemory *)
910         gst_vulkan_image_memory_wrapped (swapper->device, swap_chain_images[i],
911         format, swapchain_dims.width, swapchain_dims.height,
912         VK_IMAGE_TILING_OPTIMAL, usage, NULL, NULL);
913 
914     priv->swap_chain_images[i]->barrier.parent.pipeline_stages =
915         VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
916     priv->swap_chain_images[i]->barrier.parent.access_flags =
917         VK_ACCESS_MEMORY_READ_BIT;
918     priv->swap_chain_images[i]->barrier.image_layout =
919         VK_IMAGE_LAYOUT_UNDEFINED;
920   }
921 
922   g_free (swap_chain_images);
923   return TRUE;
924 }
925 
926 static gboolean
_swapchain_resize(GstVulkanSwapper * swapper,GError ** error)927 _swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
928 {
929   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
930   int i;
931 
932   if (!swapper->queue) {
933     if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
934       return FALSE;
935     }
936   }
937 
938   if (priv->swap_chain_images) {
939     for (i = 0; i < priv->n_swap_chain_images; i++) {
940       if (priv->swap_chain_images[i])
941         gst_memory_unref ((GstMemory *) priv->swap_chain_images[i]);
942     }
943     g_free (priv->swap_chain_images);
944     priv->swap_chain_images = NULL;
945   }
946 
947   return _allocate_swapchain (swapper, priv->caps, error);
948 }
949 
950 
951 static gboolean
configure_display_from_info(GstVulkanSwapper * swapper,GstVideoInfo * vinfo)952 configure_display_from_info (GstVulkanSwapper * swapper, GstVideoInfo * vinfo)
953 {
954   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
955   gint width;
956   gint height;
957   gboolean ok;
958   gint par_n, par_d;
959   gint display_par_n, display_par_d;
960   guint display_ratio_num, display_ratio_den;
961 
962   width = GST_VIDEO_INFO_WIDTH (vinfo);
963   height = GST_VIDEO_INFO_HEIGHT (vinfo);
964 
965   par_n = GST_VIDEO_INFO_PAR_N (vinfo);
966   par_d = GST_VIDEO_INFO_PAR_D (vinfo);
967 
968   if (!par_n)
969     par_n = 1;
970 
971   /* get display's PAR */
972   if (priv->par_n != 0 && priv->par_d != 0) {
973     display_par_n = priv->par_n;
974     display_par_d = priv->par_d;
975   } else {
976     display_par_n = 1;
977     display_par_d = 1;
978   }
979 
980   ok = gst_video_calculate_display_ratio (&display_ratio_num,
981       &display_ratio_den, width, height, par_n, par_d, display_par_n,
982       display_par_d);
983 
984   if (!ok)
985     return FALSE;
986 
987   GST_TRACE_OBJECT (swapper, "PAR: %u/%u DAR:%u/%u", par_n, par_d,
988       display_par_n, display_par_d);
989 
990   if (height % display_ratio_den == 0) {
991     GST_DEBUG_OBJECT (swapper, "keeping video height");
992     priv->dar_width = (guint)
993         gst_util_uint64_scale_int (height, display_ratio_num,
994         display_ratio_den);
995     priv->dar_height = height;
996   } else if (width % display_ratio_num == 0) {
997     GST_DEBUG_OBJECT (swapper, "keeping video width");
998     priv->dar_width = width;
999     priv->dar_height = (guint)
1000         gst_util_uint64_scale_int (width, display_ratio_den, display_ratio_num);
1001   } else {
1002     GST_DEBUG_OBJECT (swapper, "approximating while keeping video height");
1003     priv->dar_width = (guint)
1004         gst_util_uint64_scale_int (height, display_ratio_num,
1005         display_ratio_den);
1006     priv->dar_height = height;
1007   }
1008   GST_DEBUG_OBJECT (swapper, "scaling to %dx%d", priv->dar_width,
1009       priv->dar_height);
1010 
1011   return TRUE;
1012 }
1013 
1014 gboolean
gst_vulkan_swapper_set_caps(GstVulkanSwapper * swapper,GstCaps * caps,GError ** error)1015 gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
1016     GError ** error)
1017 {
1018   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
1019 
1020   if (!gst_video_info_from_caps (&priv->v_info, caps)) {
1021     g_set_error (error, GST_VULKAN_ERROR,
1022         VK_ERROR_INITIALIZATION_FAILED, "Failed to get GstVideoInfo from caps");
1023     return FALSE;
1024   }
1025 
1026   if (!configure_display_from_info (swapper, &priv->v_info)) {
1027     g_set_error (error, GST_VULKAN_ERROR,
1028         VK_ERROR_INITIALIZATION_FAILED, "Failed to configure display sizes");
1029     return FALSE;
1030   }
1031 
1032   gst_caps_replace (&priv->caps, caps);
1033 
1034   return _swapchain_resize (swapper, error);
1035 }
1036 
1037 static gboolean
_build_render_buffer_cmd(GstVulkanSwapper * swapper,guint32 swap_idx,GstBuffer * buffer,GstVulkanCommandBuffer ** cmd_ret,GError ** error)1038 _build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
1039     GstBuffer * buffer, GstVulkanCommandBuffer ** cmd_ret, GError ** error)
1040 {
1041   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
1042   GstMemory *in_mem;
1043   GstVulkanImageMemory *swap_img;
1044   GstVulkanCommandBuffer *cmd_buf;
1045   GstVideoRectangle src;
1046   VkResult err;
1047 
1048   g_return_val_if_fail (swap_idx < priv->n_swap_chain_images, FALSE);
1049   swap_img = priv->swap_chain_images[swap_idx];
1050 
1051   if (!(cmd_buf = gst_vulkan_command_pool_create (swapper->cmd_pool, error)))
1052     return FALSE;
1053 
1054   {
1055     /* *INDENT-OFF* */
1056     VkCommandBufferBeginInfo cmd_buf_info = {
1057         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
1058         .pNext = NULL,
1059         .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
1060         .pInheritanceInfo = NULL
1061     };
1062     /* *INDENT-ON* */
1063 
1064     gst_vulkan_command_buffer_lock (cmd_buf);
1065     err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
1066     if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
1067       goto unlock_error;
1068   }
1069 
1070   {
1071     /* *INDENT-OFF* */
1072     VkImageMemoryBarrier image_memory_barrier = {
1073         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1074         .pNext = NULL,
1075         .srcAccessMask = swap_img->barrier.parent.access_flags,
1076         .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
1077         .oldLayout = swap_img->barrier.image_layout,
1078         .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
1079         /* FIXME: implement exclusive transfers */
1080         .srcQueueFamilyIndex = 0,
1081         .dstQueueFamilyIndex = 0,
1082         .image = swap_img->image,
1083         .subresourceRange = swap_img->barrier.subresource_range
1084     };
1085     /* *INDENT-ON* */
1086 
1087     vkCmdPipelineBarrier (cmd_buf->cmd,
1088         swap_img->barrier.parent.pipeline_stages,
1089         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1090         &image_memory_barrier);
1091 
1092     swap_img->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1093     swap_img->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1094     swap_img->barrier.image_layout = image_memory_barrier.newLayout;
1095   }
1096 
1097   src.x = src.y = 0;
1098   src.w = priv->dar_width;
1099   src.h = priv->dar_height;
1100 
1101   g_assert (priv->surface_location.w ==
1102       gst_vulkan_image_memory_get_width (swap_img));
1103   g_assert (priv->surface_location.h ==
1104       gst_vulkan_image_memory_get_height (swap_img));
1105 
1106   gst_video_sink_center_rect (src, priv->surface_location, &priv->display_rect,
1107       priv->force_aspect_ratio);
1108 
1109   GST_TRACE_OBJECT (swapper, "rendering into result rectangle %ux%u+%u,%u "
1110       "src %ux%u dst %ux%u", priv->display_rect.w, priv->display_rect.h,
1111       priv->display_rect.x, priv->display_rect.y, src.w, src.h,
1112       priv->surface_location.w, priv->surface_location.h);
1113 
1114   in_mem = gst_buffer_peek_memory (buffer, 0);
1115   {
1116     GstVulkanImageMemory *img_mem = (GstVulkanImageMemory *) in_mem;
1117     /* *INDENT-OFF* */
1118     VkImageBlit blit = {
1119         .srcSubresource = {
1120             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1121             .mipLevel = 0,
1122             .baseArrayLayer = 0,
1123             .layerCount = 1,
1124         },
1125         .srcOffsets = {
1126             {0, 0, 0},
1127             {GST_VIDEO_INFO_WIDTH (&priv->v_info), GST_VIDEO_INFO_HEIGHT (&priv->v_info), 1},
1128         },
1129         .dstSubresource = {
1130             .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1131             .mipLevel = 0,
1132             .baseArrayLayer = 0,
1133             .layerCount = 1,
1134         },
1135         .dstOffsets = {
1136             {priv->display_rect.x, priv->display_rect.y, 0},
1137             {priv->display_rect.x + priv->display_rect.w, priv->display_rect.y + priv->display_rect.h, 1},
1138         },
1139     };
1140     VkImageMemoryBarrier image_memory_barrier = {
1141         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1142         .pNext = NULL,
1143         .srcAccessMask = img_mem->barrier.parent.access_flags,
1144         .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
1145         .oldLayout = img_mem->barrier.image_layout,
1146         .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
1147         /* FIXME: implement exclusive transfers */
1148         .srcQueueFamilyIndex = 0,
1149         .dstQueueFamilyIndex = 0,
1150         .image = img_mem->image,
1151         .subresourceRange = img_mem->barrier.subresource_range
1152     };
1153     VkClearColorValue clear = {{0.0, 0.0, 0.0, 1.0}};
1154     VkImageSubresourceRange clear_range = {
1155         .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
1156         .baseMipLevel = 0,
1157         .levelCount = 1,
1158         .baseArrayLayer = 0,
1159         .layerCount = 1,
1160     };
1161     /* *INDENT-ON* */
1162 
1163     vkCmdPipelineBarrier (cmd_buf->cmd, img_mem->barrier.parent.pipeline_stages,
1164         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1165         &image_memory_barrier);
1166 
1167     img_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1168     img_mem->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1169     img_mem->barrier.image_layout = image_memory_barrier.newLayout;
1170 
1171     vkCmdClearColorImage (cmd_buf->cmd, swap_img->image,
1172         swap_img->barrier.image_layout, &clear, 1, &clear_range);
1173     vkCmdBlitImage (cmd_buf->cmd, img_mem->image, img_mem->barrier.image_layout,
1174         swap_img->image, swap_img->barrier.image_layout, 1, &blit,
1175         VK_FILTER_LINEAR);
1176   }
1177   {
1178     /* *INDENT-OFF* */
1179     VkImageMemoryBarrier image_memory_barrier = {
1180         .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
1181         .pNext = NULL,
1182         .srcAccessMask = swap_img->barrier.parent.access_flags,
1183         .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
1184         .oldLayout = swap_img->barrier.image_layout,
1185         .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
1186         /* FIXME: implement exclusive transfers */
1187         .srcQueueFamilyIndex = 0,
1188         .dstQueueFamilyIndex = 0,
1189         .image = swap_img->image,
1190         .subresourceRange = swap_img->barrier.subresource_range
1191     };
1192     /* *INDENT-ON* */
1193 
1194     vkCmdPipelineBarrier (cmd_buf->cmd,
1195         swap_img->barrier.parent.pipeline_stages,
1196         VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1,
1197         &image_memory_barrier);
1198 
1199     swap_img->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
1200     swap_img->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
1201     swap_img->barrier.image_layout = image_memory_barrier.newLayout;
1202   }
1203 
1204   err = vkEndCommandBuffer (cmd_buf->cmd);
1205   if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
1206     goto unlock_error;
1207   gst_vulkan_command_buffer_unlock (cmd_buf);
1208 
1209   *cmd_ret = cmd_buf;
1210 
1211   return TRUE;
1212 
1213 unlock_error:
1214   gst_vulkan_command_buffer_unlock (cmd_buf);
1215   return FALSE;
1216 }
1217 
1218 static gboolean
_render_buffer_unlocked(GstVulkanSwapper * swapper,GstBuffer * buffer,GError ** error)1219 _render_buffer_unlocked (GstVulkanSwapper * swapper,
1220     GstBuffer * buffer, GError ** error)
1221 {
1222   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
1223   VkSemaphore acquire_semaphore = { 0, };
1224   VkSemaphore present_semaphore = { 0, };
1225   VkSemaphoreCreateInfo semaphore_info = { 0, };
1226   GstVulkanFence *fence = NULL;
1227   VkPresentInfoKHR present;
1228   GstVulkanCommandBuffer *cmd_buf = NULL;
1229   guint32 swap_idx;
1230   VkResult err, present_err = VK_SUCCESS;
1231 
1232   gst_vulkan_trash_list_gc (priv->trash_list);
1233 
1234   if (!buffer) {
1235     g_set_error (error, GST_VULKAN_ERROR,
1236         VK_ERROR_INITIALIZATION_FAILED, "Invalid buffer");
1237     goto error;
1238   }
1239 
1240   if (g_atomic_int_get (&priv->to_quit)) {
1241     g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_SURFACE_LOST_KHR,
1242         "Output window was closed");
1243     goto error;
1244   }
1245 
1246   gst_buffer_replace (&priv->current_buffer, buffer);
1247 
1248   /* *INDENT-OFF* */
1249   semaphore_info = (VkSemaphoreCreateInfo) {
1250       .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
1251       .pNext = NULL,
1252       .flags = 0,
1253   };
1254   /* *INDENT-ON* */
1255 
1256 reacquire:
1257   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1258       NULL, &acquire_semaphore);
1259   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1260     goto error;
1261 
1262   err =
1263       priv->AcquireNextImageKHR (swapper->device->device,
1264       priv->swap_chain, -1, acquire_semaphore, VK_NULL_HANDLE, &swap_idx);
1265   /* TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR */
1266   if (err == VK_ERROR_OUT_OF_DATE_KHR) {
1267     GST_DEBUG_OBJECT (swapper, "out of date frame acquired");
1268 
1269     vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1270     acquire_semaphore = VK_NULL_HANDLE;
1271     if (!_swapchain_resize (swapper, error))
1272       goto error;
1273     goto reacquire;
1274   } else if (gst_vulkan_error_to_g_error (err, error,
1275           "vkAcquireNextImageKHR") < 0) {
1276     goto error;
1277   }
1278 
1279   if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd_buf, error))
1280     goto error;
1281 
1282   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
1283       NULL, &present_semaphore);
1284   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
1285     goto error;
1286 
1287   {
1288     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1289     VkSubmitInfo submit_info = { 0, };
1290 
1291     /* *INDENT-OFF* */
1292     submit_info = (VkSubmitInfo) {
1293         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1294         .pNext = NULL,
1295         .waitSemaphoreCount = 1,
1296         .pWaitSemaphores = &acquire_semaphore,
1297         .pWaitDstStageMask = &stages,
1298         .commandBufferCount = 1,
1299         .pCommandBuffers = &cmd_buf->cmd,
1300         .signalSemaphoreCount = 1,
1301         .pSignalSemaphores = &present_semaphore,
1302     };
1303     /* *INDENT-ON* */
1304 
1305     fence = gst_vulkan_device_create_fence (swapper->device, error);
1306     if (!fence)
1307       goto error;
1308 
1309     gst_vulkan_queue_submit_lock (swapper->queue);
1310     err =
1311         vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
1312         GST_VULKAN_FENCE_FENCE (fence));
1313     gst_vulkan_queue_submit_unlock (swapper->queue);
1314     if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
1315       goto error;
1316 
1317     gst_vulkan_trash_list_add (priv->trash_list,
1318         gst_vulkan_trash_new_mini_object_unref (fence,
1319             GST_MINI_OBJECT_CAST (cmd_buf)));
1320     gst_vulkan_trash_list_add (priv->trash_list,
1321         gst_vulkan_trash_new_free_semaphore (fence, acquire_semaphore));
1322     acquire_semaphore = VK_NULL_HANDLE;
1323 
1324     gst_vulkan_command_buffer_unlock (cmd_buf);
1325     cmd_buf = NULL;
1326     gst_vulkan_fence_unref (fence);
1327     fence = NULL;
1328   }
1329 
1330   /* *INDENT-OFF* */
1331   present = (VkPresentInfoKHR) {
1332       .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
1333       .pNext = NULL,
1334       .waitSemaphoreCount = 1,
1335       .pWaitSemaphores = &present_semaphore,
1336       .swapchainCount = 1,
1337       .pSwapchains = &priv->swap_chain,
1338       .pImageIndices = &swap_idx,
1339       .pResults = &present_err,
1340   };
1341   /* *INDENT-ON* */
1342 
1343   err = priv->QueuePresentKHR (swapper->queue->queue, &present);
1344 
1345   if (present_err == VK_ERROR_OUT_OF_DATE_KHR) {
1346     GST_DEBUG_OBJECT (swapper, "out of date frame submitted");
1347 
1348     if (!_swapchain_resize (swapper, error))
1349       goto error;
1350   } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
1351     goto error;
1352 
1353   {
1354     VkSubmitInfo submit_info = { 0, };
1355     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
1356 
1357     /* *INDENT-OFF* */
1358     submit_info = (VkSubmitInfo) {
1359         .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
1360         .pWaitDstStageMask = &stages,
1361         0,
1362     };
1363     /* *INDENT-ON* */
1364 
1365     fence = gst_vulkan_device_create_fence (swapper->device, error);
1366     if (!fence)
1367       goto error;
1368 
1369     gst_vulkan_queue_submit_lock (swapper->queue);
1370     err =
1371         vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
1372         GST_VULKAN_FENCE_FENCE (fence));
1373     gst_vulkan_queue_submit_unlock (swapper->queue);
1374     if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
1375       goto error;
1376 
1377     gst_vulkan_trash_list_add (priv->trash_list,
1378         gst_vulkan_trash_new_free_semaphore (fence, present_semaphore));
1379     gst_vulkan_trash_list_add (priv->trash_list,
1380         gst_vulkan_trash_new_mini_object_unref (fence,
1381             (GstMiniObject *) gst_buffer_ref (buffer)));
1382     gst_vulkan_fence_unref (fence);
1383     fence = NULL;
1384   }
1385 
1386   return TRUE;
1387 
1388 error:
1389   {
1390     if (acquire_semaphore)
1391       vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1392     if (present_semaphore)
1393       vkDestroySemaphore (swapper->device->device, present_semaphore, NULL);
1394     if (cmd_buf) {
1395       gst_vulkan_command_buffer_unlock (cmd_buf);
1396       gst_vulkan_command_buffer_unref (cmd_buf);
1397     }
1398     return FALSE;
1399   }
1400 }
1401 
1402 gboolean
gst_vulkan_swapper_render_buffer(GstVulkanSwapper * swapper,GstBuffer * buffer,GError ** error)1403 gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
1404     GstBuffer * buffer, GError ** error)
1405 {
1406   GstMemory *mem;
1407   gboolean ret;
1408 
1409   mem = gst_buffer_peek_memory (buffer, 0);
1410   if (!mem) {
1411     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1412         "Buffer has no memory");
1413     return FALSE;
1414   }
1415   if (!gst_is_vulkan_image_memory (mem)) {
1416     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1417         "Incorrect memory type");
1418     return FALSE;
1419   }
1420 
1421   RENDER_LOCK (swapper);
1422   ret = _render_buffer_unlocked (swapper, buffer, error);
1423   RENDER_UNLOCK (swapper);
1424 
1425   return ret;
1426 }
1427 
1428 static void
_on_window_draw(GstVulkanWindow * window,GstVulkanSwapper * swapper)1429 _on_window_draw (GstVulkanWindow * window, GstVulkanSwapper * swapper)
1430 {
1431   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
1432   GError *error = NULL;
1433 
1434   RENDER_LOCK (swapper);
1435   if (!priv->current_buffer) {
1436     GST_DEBUG_OBJECT (swapper, "No buffer to render");
1437     RENDER_UNLOCK (swapper);
1438     return;
1439   }
1440 
1441   /* TODO: perform some rate limiting of the number of redraw events */
1442   if (!_render_buffer_unlocked (swapper, priv->current_buffer, &error))
1443     GST_ERROR_OBJECT (swapper, "Failed to redraw buffer %p %s",
1444         priv->current_buffer, error->message);
1445   g_clear_error (&error);
1446   RENDER_UNLOCK (swapper);
1447 }
1448 
1449 static void
_on_window_resize(GstVulkanWindow * window,guint width,guint height,GstVulkanSwapper * swapper)1450 _on_window_resize (GstVulkanWindow * window, guint width, guint height,
1451     GstVulkanSwapper * swapper)
1452 {
1453   GstVulkanSwapperPrivate *priv = GET_PRIV (swapper);
1454   GError *error = NULL;
1455 
1456   RENDER_LOCK (swapper);
1457   if (priv->any_current_extent) {
1458     if (!_swapchain_resize (swapper, &error))
1459       GST_ERROR_OBJECT (swapper, "Failed to resize swapchain: %s",
1460           error->message);
1461     g_clear_error (&error);
1462   }
1463   RENDER_UNLOCK (swapper);
1464 }
1465 
1466 /**
1467  * gst_vulkan_swapper_get_surface_rectangles:
1468  * @swapper: a #GstVulkanSwapper
1469  * @input_image: (out) (nullable): The #GstVideoRectangle for the configured
1470  *      caps modified for DAR.
1471  * @surface_location: (out) (nullable): The #GstVideoRectangle for where the
1472  *      output surface is located relative to its parent
1473  * @display_rect: (out) (nullable): The #GstVideoRectangle for where the input
1474  *      images are placed inside @surface_location
1475  *
1476  * Since: 1.18
1477  */
1478 void
gst_vulkan_swapper_get_surface_rectangles(GstVulkanSwapper * swapper,GstVideoRectangle * input_image,GstVideoRectangle * surface_location,GstVideoRectangle * display_rect)1479 gst_vulkan_swapper_get_surface_rectangles (GstVulkanSwapper * swapper,
1480     GstVideoRectangle * input_image, GstVideoRectangle * surface_location,
1481     GstVideoRectangle * display_rect)
1482 {
1483   GstVulkanSwapperPrivate *priv;
1484 
1485   g_return_if_fail (GST_IS_VULKAN_SWAPPER (swapper));
1486 
1487   priv = GET_PRIV (swapper);
1488 
1489   RENDER_LOCK (swapper);
1490   if (input_image) {
1491     input_image->x = input_image->y = 0;
1492     input_image->w = priv->dar_width;
1493     input_image->h = priv->dar_height;
1494   }
1495 
1496   if (surface_location) {
1497     *display_rect = priv->surface_location;
1498   }
1499 
1500   if (display_rect) {
1501     *display_rect = priv->display_rect;
1502   }
1503   RENDER_UNLOCK (swapper);
1504 }
1505