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 "vkswapper.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   GMutex render_lock;
39 
40   GList *trash_list;
41 };
42 
43 #define gst_vulkan_swapper_parent_class parent_class
44 G_DEFINE_TYPE_WITH_CODE (GstVulkanSwapper, gst_vulkan_swapper,
45     GST_TYPE_OBJECT, G_ADD_PRIVATE (GstVulkanSwapper)
46     GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT,
47         "vulkanswapper", 0, "Vulkan Swapper"));
48 
49 static void _on_window_draw (GstVulkanWindow * window,
50     GstVulkanSwapper * swapper);
51 
52 static gboolean
_get_function_table(GstVulkanSwapper * swapper)53 _get_function_table (GstVulkanSwapper * swapper)
54 {
55   GstVulkanDevice *device = swapper->device;
56   GstVulkanInstance *instance = gst_vulkan_device_get_instance (device);
57 
58   if (!instance) {
59     GST_ERROR_OBJECT (swapper, "Failed to get instance from the device");
60     return FALSE;
61   }
62 #define GET_PROC_ADDRESS_REQUIRED(obj, type, name) \
63   G_STMT_START { \
64     obj->G_PASTE (, name) = G_PASTE(G_PASTE(gst_vulkan_, type), _get_proc_address) (type, "vk" G_STRINGIFY(name)); \
65     if (!obj->G_PASTE(, name)) { \
66       GST_ERROR_OBJECT (obj, "Failed to find required function vk" G_STRINGIFY(name)); \
67       gst_object_unref (instance); \
68       return FALSE; \
69     } \
70   } G_STMT_END
71 
72   GET_PROC_ADDRESS_REQUIRED (swapper, instance,
73       GetPhysicalDeviceSurfaceSupportKHR);
74   GET_PROC_ADDRESS_REQUIRED (swapper, instance,
75       GetPhysicalDeviceSurfaceCapabilitiesKHR);
76   GET_PROC_ADDRESS_REQUIRED (swapper, instance,
77       GetPhysicalDeviceSurfaceFormatsKHR);
78   GET_PROC_ADDRESS_REQUIRED (swapper, instance,
79       GetPhysicalDeviceSurfacePresentModesKHR);
80   GET_PROC_ADDRESS_REQUIRED (swapper, device, CreateSwapchainKHR);
81   GET_PROC_ADDRESS_REQUIRED (swapper, device, DestroySwapchainKHR);
82   GET_PROC_ADDRESS_REQUIRED (swapper, device, GetSwapchainImagesKHR);
83   GET_PROC_ADDRESS_REQUIRED (swapper, device, AcquireNextImageKHR);
84   GET_PROC_ADDRESS_REQUIRED (swapper, device, QueuePresentKHR);
85 
86   gst_object_unref (instance);
87 
88   return TRUE;
89 
90 #undef GET_PROC_ADDRESS_REQUIRED
91 }
92 
93 static GstVideoFormat
_vk_format_to_video_format(VkFormat format)94 _vk_format_to_video_format (VkFormat format)
95 {
96   switch (format) {
97       /* double check endianess */
98     case VK_FORMAT_R8G8B8A8_UNORM:
99     case VK_FORMAT_R8G8B8A8_SRGB:
100       return GST_VIDEO_FORMAT_RGBA;
101     case VK_FORMAT_R8G8B8_UNORM:
102     case VK_FORMAT_R8G8B8_SRGB:
103       return GST_VIDEO_FORMAT_RGB;
104     case VK_FORMAT_B8G8R8A8_UNORM:
105     case VK_FORMAT_B8G8R8A8_SRGB:
106       return GST_VIDEO_FORMAT_BGRA;
107     case VK_FORMAT_B8G8R8_UNORM:
108     case VK_FORMAT_B8G8R8_SRGB:
109       return GST_VIDEO_FORMAT_BGR;
110     default:
111       return GST_VIDEO_FORMAT_UNKNOWN;
112   }
113 }
114 
115 static VkFormat
_vk_format_from_video_info(GstVideoInfo * v_info)116 _vk_format_from_video_info (GstVideoInfo * v_info)
117 {
118   switch (GST_VIDEO_INFO_FORMAT (v_info)) {
119     case GST_VIDEO_FORMAT_RGBA:
120       if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
121           GST_VIDEO_TRANSFER_SRGB)
122         return VK_FORMAT_R8G8B8A8_SRGB;
123       else
124         return VK_FORMAT_R8G8B8A8_UNORM;
125     case GST_VIDEO_FORMAT_RGB:
126       if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
127           GST_VIDEO_TRANSFER_SRGB)
128         return VK_FORMAT_R8G8B8_SRGB;
129       else
130         return VK_FORMAT_R8G8B8_UNORM;
131     case GST_VIDEO_FORMAT_BGRA:
132       if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
133           GST_VIDEO_TRANSFER_SRGB)
134         return VK_FORMAT_B8G8R8A8_SRGB;
135       else
136         return VK_FORMAT_B8G8R8A8_UNORM;
137     case GST_VIDEO_FORMAT_BGR:
138       if (GST_VIDEO_INFO_COLORIMETRY (v_info).transfer ==
139           GST_VIDEO_TRANSFER_SRGB)
140         return VK_FORMAT_B8G8R8_SRGB;
141       else
142         return VK_FORMAT_B8G8R8_UNORM;
143     default:
144       return VK_FORMAT_UNDEFINED;
145   }
146 }
147 
148 static VkColorSpaceKHR
_vk_color_space_from_video_info(GstVideoInfo * v_info)149 _vk_color_space_from_video_info (GstVideoInfo * v_info)
150 {
151   return VK_COLORSPACE_SRGB_NONLINEAR_KHR;
152 }
153 
154 static void
_add_vk_format_to_list(GValue * list,VkFormat format)155 _add_vk_format_to_list (GValue * list, VkFormat format)
156 {
157   GstVideoFormat v_format;
158   const gchar *format_str;
159 
160   v_format = _vk_format_to_video_format (format);
161   if (v_format) {
162     GValue item = G_VALUE_INIT;
163 
164     g_value_init (&item, G_TYPE_STRING);
165     format_str = gst_video_format_to_string (v_format);
166     g_value_set_string (&item, format_str);
167     gst_value_list_append_value (list, &item);
168     g_value_unset (&item);
169   }
170 }
171 
172 static gboolean
_vulkan_swapper_ensure_surface(GstVulkanSwapper * swapper,GError ** error)173 _vulkan_swapper_ensure_surface (GstVulkanSwapper * swapper, GError ** error)
174 {
175   if (!swapper->surface) {
176     if (!(swapper->surface =
177             gst_vulkan_window_get_surface (swapper->window, error))) {
178       return FALSE;
179     }
180   }
181 
182   return TRUE;
183 }
184 
185 struct choose_data
186 {
187   GstVulkanSwapper *swapper;
188   GstVulkanQueue *graphics_queue;
189   GstVulkanQueue *present_queue;
190 };
191 
192 static gboolean
_choose_queue(GstVulkanDevice * device,GstVulkanQueue * queue,struct choose_data * data)193 _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue,
194     struct choose_data *data)
195 {
196   guint flags = device->queue_family_props[queue->family].queueFlags;
197   VkPhysicalDevice gpu;
198   gboolean supports_present;
199 
200   gpu = gst_vulkan_device_get_physical_device (data->swapper->device);
201 
202   {
203     VkResult err;
204     GError *error = NULL;
205     VkBool32 physical_device_supported;
206 
207     err =
208         data->swapper->GetPhysicalDeviceSurfaceSupportKHR (gpu, queue->index,
209         data->swapper->surface, &physical_device_supported);
210     if (gst_vulkan_error_to_g_error (err, &error,
211             "GetPhysicalDeviceSurfaceSupport") < 0) {
212       GST_DEBUG_OBJECT (data->swapper,
213           "surface not supported by the physical device: %s", error->message);
214       return TRUE;
215     }
216   }
217 
218   supports_present =
219       gst_vulkan_window_get_presentation_support (data->swapper->window,
220       device, queue->index);
221 
222   if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) {
223     if (supports_present) {
224       /* found one that supports both */
225       if (data->graphics_queue)
226         gst_object_unref (data->graphics_queue);
227       data->graphics_queue = gst_object_ref (queue);
228       if (data->present_queue)
229         gst_object_unref (data->present_queue);
230       data->present_queue = gst_object_ref (queue);
231       return FALSE;
232     }
233     if (!data->graphics_queue)
234       data->present_queue = gst_object_ref (queue);
235   } else if (supports_present) {
236     if (!data->present_queue)
237       data->present_queue = gst_object_ref (queue);
238   }
239 
240   return TRUE;
241 }
242 
243 static gboolean
_vulkan_swapper_retrieve_surface_properties(GstVulkanSwapper * swapper,GError ** error)244 _vulkan_swapper_retrieve_surface_properties (GstVulkanSwapper * swapper,
245     GError ** error)
246 {
247   struct choose_data data;
248   VkPhysicalDevice gpu;
249   VkResult err;
250 
251   if (swapper->surf_formats)
252     return TRUE;
253 
254   if (!_vulkan_swapper_ensure_surface (swapper, error))
255     return FALSE;
256 
257   gpu = gst_vulkan_device_get_physical_device (swapper->device);
258 
259   data.swapper = swapper;
260   data.present_queue = NULL;
261   data.graphics_queue = NULL;
262 
263   gst_vulkan_device_foreach_queue (swapper->device,
264       (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data);
265 
266   if (data.graphics_queue != data.present_queue) {
267     /* FIXME: add support for separate graphics/present queues */
268     g_set_error (error, GST_VULKAN_ERROR,
269         VK_ERROR_INITIALIZATION_FAILED,
270         "Failed to find a compatible present/graphics queue");
271     if (data.present_queue)
272       gst_object_unref (data.present_queue);
273     if (data.graphics_queue)
274       gst_object_unref (data.graphics_queue);
275     return FALSE;
276   }
277 
278   swapper->queue = gst_object_ref (data.present_queue);
279   if (data.present_queue)
280     gst_object_unref (data.present_queue);
281   if (data.graphics_queue)
282     gst_object_unref (data.graphics_queue);
283 
284   err =
285       swapper->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu, swapper->surface,
286       &swapper->surf_props);
287   if (gst_vulkan_error_to_g_error (err, error,
288           "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
289     return FALSE;
290 
291   err =
292       swapper->GetPhysicalDeviceSurfaceFormatsKHR (gpu, swapper->surface,
293       &swapper->n_surf_formats, NULL);
294   if (gst_vulkan_error_to_g_error (err, error,
295           "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
296     return FALSE;
297 
298   swapper->surf_formats = g_new0 (VkSurfaceFormatKHR, swapper->n_surf_formats);
299   err =
300       swapper->GetPhysicalDeviceSurfaceFormatsKHR (gpu, swapper->surface,
301       &swapper->n_surf_formats, swapper->surf_formats);
302   if (gst_vulkan_error_to_g_error (err, error,
303           "GetPhysicalDeviceSurfaceFormatsKHR") < 0)
304     return FALSE;
305 
306   err =
307       swapper->GetPhysicalDeviceSurfacePresentModesKHR (gpu, swapper->surface,
308       &swapper->n_surf_present_modes, NULL);
309   if (gst_vulkan_error_to_g_error (err, error,
310           "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
311     return FALSE;
312 
313   swapper->surf_present_modes =
314       g_new0 (VkPresentModeKHR, swapper->n_surf_present_modes);
315   err =
316       swapper->GetPhysicalDeviceSurfacePresentModesKHR (gpu, swapper->surface,
317       &swapper->n_surf_present_modes, swapper->surf_present_modes);
318   if (gst_vulkan_error_to_g_error (err, error,
319           "GetPhysicalDeviceSurfacePresentModesKHR") < 0)
320     return FALSE;
321 
322   return TRUE;
323 }
324 
325 static gboolean
_on_window_close(GstVulkanWindow * window,GstVulkanSwapper * swapper)326 _on_window_close (GstVulkanWindow * window, GstVulkanSwapper * swapper)
327 {
328   g_atomic_int_set (&swapper->to_quit, 1);
329 
330   return TRUE;
331 }
332 
333 static void
gst_vulkan_swapper_finalize(GObject * object)334 gst_vulkan_swapper_finalize (GObject * object)
335 {
336   GstVulkanSwapper *swapper = GST_VULKAN_SWAPPER (object);
337   int i;
338 
339   if (!gst_vulkan_trash_list_wait (swapper->priv->trash_list, -1))
340     GST_WARNING_OBJECT (swapper, "Failed to wait for all fences to complete "
341         "before shutting down");
342   swapper->priv->trash_list = NULL;
343 
344   if (swapper->swap_chain_images) {
345     for (i = 0; i < swapper->n_swap_chain_images; i++) {
346       gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
347       swapper->swap_chain_images[i] = NULL;
348     }
349     g_free (swapper->swap_chain_images);
350   }
351   swapper->swap_chain_images = NULL;
352 
353   if (swapper->swap_chain)
354     swapper->DestroySwapchainKHR (swapper->device->device, swapper->swap_chain,
355         NULL);
356   swapper->swap_chain = VK_NULL_HANDLE;
357 
358   if (swapper->queue)
359     gst_object_unref (swapper->queue);
360   swapper->queue = NULL;
361 
362   if (swapper->device)
363     gst_object_unref (swapper->device);
364   swapper->device = NULL;
365 
366   g_signal_handler_disconnect (swapper->window, swapper->draw_id);
367   swapper->draw_id = 0;
368 
369   g_signal_handler_disconnect (swapper->window, swapper->close_id);
370   swapper->close_id = 0;
371 
372   if (swapper->window)
373     gst_object_unref (swapper->window);
374   swapper->window = NULL;
375 
376   g_free (swapper->surf_present_modes);
377   swapper->surf_present_modes = NULL;
378 
379   g_free (swapper->surf_formats);
380   swapper->surf_formats = NULL;
381 
382   gst_buffer_replace (&swapper->current_buffer, NULL);
383   gst_caps_replace (&swapper->caps, NULL);
384 
385   g_mutex_clear (&swapper->priv->render_lock);
386 
387   G_OBJECT_CLASS (parent_class)->finalize (object);
388 }
389 
390 static void
gst_vulkan_swapper_init(GstVulkanSwapper * swapper)391 gst_vulkan_swapper_init (GstVulkanSwapper * swapper)
392 {
393   swapper->priv = gst_vulkan_swapper_get_instance_private (swapper);
394 
395   g_mutex_init (&swapper->priv->render_lock);
396 }
397 
398 static void
gst_vulkan_swapper_class_init(GstVulkanSwapperClass * klass)399 gst_vulkan_swapper_class_init (GstVulkanSwapperClass * klass)
400 {
401   G_OBJECT_CLASS (klass)->finalize = gst_vulkan_swapper_finalize;
402 }
403 
404 GstVulkanSwapper *
gst_vulkan_swapper_new(GstVulkanDevice * device,GstVulkanWindow * window)405 gst_vulkan_swapper_new (GstVulkanDevice * device, GstVulkanWindow * window)
406 {
407   GstVulkanSwapper *swapper;
408 
409   swapper = g_object_new (GST_TYPE_VULKAN_SWAPPER, NULL);
410   gst_object_ref_sink (swapper);
411   swapper->device = gst_object_ref (device);
412   swapper->window = gst_object_ref (window);
413 
414   if (!_get_function_table (swapper)) {
415     gst_object_unref (swapper);
416     return NULL;
417   }
418 
419   swapper->close_id = g_signal_connect (swapper->window, "close",
420       (GCallback) _on_window_close, swapper);
421   swapper->draw_id = g_signal_connect (swapper->window, "draw",
422       (GCallback) _on_window_draw, swapper);
423 
424   return swapper;
425 }
426 
427 GstCaps *
gst_vulkan_swapper_get_supported_caps(GstVulkanSwapper * swapper,GError ** error)428 gst_vulkan_swapper_get_supported_caps (GstVulkanSwapper * swapper,
429     GError ** error)
430 {
431   GstStructure *s;
432   GstCaps *caps;
433 
434   g_return_val_if_fail (GST_IS_VULKAN_SWAPPER (swapper), NULL);
435 
436   if (!_vulkan_swapper_retrieve_surface_properties (swapper, error))
437     return NULL;
438 
439   caps = gst_caps_new_empty_simple ("video/x-raw");
440   gst_caps_set_features (caps, 0,
441       gst_caps_features_from_string (GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER));
442   s = gst_caps_get_structure (caps, 0);
443 
444   {
445     int i;
446     GValue list = G_VALUE_INIT;
447 
448     g_value_init (&list, GST_TYPE_LIST);
449 
450     if (swapper->n_surf_formats
451         && swapper->surf_formats[0].format == VK_FORMAT_UNDEFINED) {
452       _add_vk_format_to_list (&list, VK_FORMAT_B8G8R8A8_UNORM);
453     } else {
454       for (i = 0; i < swapper->n_surf_formats; i++) {
455         _add_vk_format_to_list (&list, swapper->surf_formats[i].format);
456       }
457     }
458 
459     gst_structure_set_value (s, "format", &list);
460     g_value_unset (&list);
461   }
462 
463   {
464     guint32 max_dim = swapper->device->gpu_props.limits.maxImageDimension2D;
465 
466     gst_structure_set (s, "width", GST_TYPE_INT_RANGE, 1, (gint) max_dim,
467         "height", GST_TYPE_INT_RANGE, 1, (gint) max_dim, "pixel-aspect-ratio",
468         GST_TYPE_FRACTION, 1, 1, "framerate", GST_TYPE_FRACTION_RANGE, 0, 1,
469         G_MAXINT, 1, NULL);
470   }
471 
472   GST_INFO_OBJECT (swapper, "Probed the following caps %" GST_PTR_FORMAT, caps);
473 
474   return caps;
475 }
476 
477 static gboolean
_swapper_set_image_layout_with_cmd(GstVulkanSwapper * swapper,VkCommandBuffer cmd,GstVulkanImageMemory * image,VkImageLayout new_image_layout,GError ** error)478 _swapper_set_image_layout_with_cmd (GstVulkanSwapper * swapper,
479     VkCommandBuffer cmd, GstVulkanImageMemory * image,
480     VkImageLayout new_image_layout, GError ** error)
481 {
482   VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
483   VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
484   VkImageMemoryBarrier image_memory_barrier;
485 
486   gst_vulkan_image_memory_set_layout (image, new_image_layout,
487       &image_memory_barrier);
488 
489   vkCmdPipelineBarrier (cmd, src_stages, dest_stages, 0, 0, NULL, 0, NULL, 1,
490       &image_memory_barrier);
491 
492   return TRUE;
493 }
494 
495 static gboolean
_swapper_set_image_layout(GstVulkanSwapper * swapper,GstVulkanImageMemory * image,VkImageLayout new_image_layout,GError ** error)496 _swapper_set_image_layout (GstVulkanSwapper * swapper,
497     GstVulkanImageMemory * image, VkImageLayout new_image_layout,
498     GError ** error)
499 {
500   VkCommandBuffer cmd = VK_NULL_HANDLE;
501   GstVulkanFence *fence = NULL;
502   VkResult err;
503 
504   if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
505     goto error;
506 
507   fence = gst_vulkan_fence_new (swapper->device, 0, error);
508   if (!fence)
509     goto error;
510 
511   {
512     VkCommandBufferInheritanceInfo buf_inh = { 0, };
513     VkCommandBufferBeginInfo cmd_buf_info = { 0, };
514 
515     buf_inh.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
516     buf_inh.pNext = NULL;
517     buf_inh.renderPass = VK_NULL_HANDLE;
518     buf_inh.subpass = 0;
519     buf_inh.framebuffer = VK_NULL_HANDLE;
520     buf_inh.occlusionQueryEnable = FALSE;
521     buf_inh.queryFlags = 0;
522     buf_inh.pipelineStatistics = 0;
523 
524     cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
525     cmd_buf_info.pNext = NULL;
526     cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
527     cmd_buf_info.pInheritanceInfo = &buf_inh;
528 
529     err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
530     if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
531       goto error;
532   }
533 
534   if (!_swapper_set_image_layout_with_cmd (swapper, cmd, image,
535           new_image_layout, error))
536     goto error;
537 
538   err = vkEndCommandBuffer (cmd);
539   if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
540     goto error;
541 
542   {
543     VkSubmitInfo submit_info = { 0, };
544     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
545 
546     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
547     submit_info.pNext = NULL;
548     submit_info.waitSemaphoreCount = 0;
549     submit_info.pWaitSemaphores = NULL;
550     submit_info.pWaitDstStageMask = &stages;
551     submit_info.commandBufferCount = 1;
552     submit_info.pCommandBuffers = &cmd;
553     submit_info.signalSemaphoreCount = 0;
554     submit_info.pSignalSemaphores = NULL;
555 
556     err =
557         vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
558         GST_VULKAN_FENCE_FENCE (fence));
559     if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
560       goto error;
561   }
562 
563   swapper->priv->trash_list = g_list_prepend (swapper->priv->trash_list,
564       gst_vulkan_trash_new_free_command_buffer (fence, cmd));
565   fence = NULL;
566 
567   return TRUE;
568 
569 error:
570   if (fence)
571     gst_vulkan_fence_unref (fence);
572   return FALSE;
573 }
574 
575 static gboolean
_allocate_swapchain(GstVulkanSwapper * swapper,GstCaps * caps,GError ** error)576 _allocate_swapchain (GstVulkanSwapper * swapper, GstCaps * caps,
577     GError ** error)
578 {
579   VkSurfaceTransformFlagsKHR preTransform;
580   VkCompositeAlphaFlagsKHR alpha_flags;
581   VkPresentModeKHR present_mode;
582   VkImageUsageFlags usage = 0;
583   VkColorSpaceKHR color_space;
584   VkImage *swap_chain_images;
585   VkExtent2D swapchain_dims;
586   guint32 n_images_wanted;
587   VkPhysicalDevice gpu;
588   VkFormat format;
589   VkResult err;
590   guint32 i;
591 
592   if (!_vulkan_swapper_ensure_surface (swapper, error))
593     return FALSE;
594 
595   gpu = gst_vulkan_device_get_physical_device (swapper->device);
596   err =
597       swapper->GetPhysicalDeviceSurfaceCapabilitiesKHR (gpu,
598       swapper->surface, &swapper->surf_props);
599   if (gst_vulkan_error_to_g_error (err, error,
600           "GetPhysicalDeviceSurfaceCapabilitiesKHR") < 0)
601     return FALSE;
602 
603   /* width and height are either both -1, or both not -1. */
604   if (swapper->surf_props.currentExtent.width == -1) {
605     /* If the surface size is undefined, the size is set to
606      * the size of the images requested. */
607     swapchain_dims.width = 320;
608     swapchain_dims.height = 240;
609   } else {
610     /* If the surface size is defined, the swap chain size must match */
611     swapchain_dims = swapper->surf_props.currentExtent;
612   }
613 
614   /* If mailbox mode is available, use it, as is the lowest-latency non-
615    * tearing mode.  If not, try IMMEDIATE which will usually be available,
616    * and is fastest (though it tears).  If not, fall back to FIFO which is
617    * always available. */
618   present_mode = VK_PRESENT_MODE_FIFO_KHR;
619   for (i = 0; i < swapper->n_surf_present_modes; i++) {
620     if (swapper->surf_present_modes[i] == VK_PRESENT_MODE_MAILBOX_KHR) {
621       present_mode = VK_PRESENT_MODE_MAILBOX_KHR;
622       break;
623     }
624     if ((present_mode != VK_PRESENT_MODE_MAILBOX_KHR) &&
625         (swapper->surf_present_modes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR)) {
626       present_mode = VK_PRESENT_MODE_IMMEDIATE_KHR;
627     }
628   }
629 
630   /* Determine the number of VkImage's to use in the swap chain (we desire to
631    * own only 1 image at a time, besides the images being displayed and
632    * queued for display): */
633   n_images_wanted = swapper->surf_props.minImageCount + 1;
634   if ((swapper->surf_props.maxImageCount > 0) &&
635       (n_images_wanted > swapper->surf_props.maxImageCount)) {
636     /* Application must settle for fewer images than desired: */
637     n_images_wanted = swapper->surf_props.maxImageCount;
638   }
639 
640   if (swapper->surf_props.
641       supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
642     preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
643   } else {
644     preTransform = swapper->surf_props.currentTransform;
645   }
646 
647   format = _vk_format_from_video_info (&swapper->v_info);
648   color_space = _vk_color_space_from_video_info (&swapper->v_info);
649 
650   if ((swapper->surf_props.supportedCompositeAlpha &
651           VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR) != 0) {
652     alpha_flags = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
653   } else if ((swapper->surf_props.supportedCompositeAlpha &
654           VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR) != 0) {
655     alpha_flags = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
656   } else {
657     g_set_error (error, GST_VULKAN_ERROR,
658         VK_ERROR_INITIALIZATION_FAILED,
659         "Incorrect alpha flags available for the swap images");
660     return FALSE;
661   }
662 
663   if ((swapper->surf_props.supportedUsageFlags &
664           VK_IMAGE_USAGE_TRANSFER_DST_BIT) != 0) {
665     usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
666   } else {
667     g_set_error (error, GST_VULKAN_ERROR,
668         VK_ERROR_INITIALIZATION_FAILED,
669         "Incorrect usage flags available for the swap images");
670     return FALSE;
671   }
672   if ((swapper->
673           surf_props.supportedUsageFlags & VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT)
674       != 0) {
675     usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
676   } else {
677     g_set_error (error, GST_VULKAN_ERROR,
678         VK_ERROR_INITIALIZATION_FAILED,
679         "Incorrect usage flags available for the swap images");
680     return FALSE;
681   }
682 
683   {
684     VkSwapchainCreateInfoKHR swap_chain_info = { 0, };
685     VkSwapchainKHR old_swap_chain;
686 
687     old_swap_chain = swapper->swap_chain;
688 
689     swap_chain_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
690     swap_chain_info.pNext = NULL;
691     swap_chain_info.surface = swapper->surface;
692     swap_chain_info.minImageCount = n_images_wanted;
693     swap_chain_info.imageFormat = format;
694     swap_chain_info.imageColorSpace = color_space;
695     swap_chain_info.imageExtent = swapchain_dims;
696     swap_chain_info.imageArrayLayers = 1;
697     swap_chain_info.imageUsage = usage;
698     swap_chain_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
699     swap_chain_info.queueFamilyIndexCount = 0;
700     swap_chain_info.pQueueFamilyIndices = NULL;
701     swap_chain_info.preTransform = preTransform;
702     swap_chain_info.presentMode = present_mode;
703     swap_chain_info.compositeAlpha = alpha_flags;
704     swap_chain_info.clipped = TRUE;
705     swap_chain_info.oldSwapchain = swapper->swap_chain;
706 
707     err =
708         swapper->CreateSwapchainKHR (swapper->device->device, &swap_chain_info,
709         NULL, &swapper->swap_chain);
710     if (gst_vulkan_error_to_g_error (err, error, "vkCreateSwapchainKHR") < 0)
711       return FALSE;
712 
713     if (old_swap_chain != VK_NULL_HANDLE) {
714       swapper->DestroySwapchainKHR (swapper->device->device, old_swap_chain,
715           NULL);
716     }
717   }
718 
719   err =
720       swapper->GetSwapchainImagesKHR (swapper->device->device,
721       swapper->swap_chain, &swapper->n_swap_chain_images, NULL);
722   if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0)
723     return FALSE;
724 
725   swap_chain_images = g_new0 (VkImage, swapper->n_swap_chain_images);
726   err =
727       swapper->GetSwapchainImagesKHR (swapper->device->device,
728       swapper->swap_chain, &swapper->n_swap_chain_images, swap_chain_images);
729   if (gst_vulkan_error_to_g_error (err, error, "vkGetSwapchainImagesKHR") < 0) {
730     g_free (swap_chain_images);
731     return FALSE;
732   }
733 
734   swapper->swap_chain_images =
735       g_new0 (GstVulkanImageMemory *, swapper->n_swap_chain_images);
736   for (i = 0; i < swapper->n_swap_chain_images; i++) {
737     swapper->swap_chain_images[i] = (GstVulkanImageMemory *)
738         gst_vulkan_image_memory_wrapped (swapper->device, swap_chain_images[i],
739         format, swapchain_dims.width, swapchain_dims.height,
740         VK_IMAGE_TILING_OPTIMAL, usage, NULL, NULL);
741 
742     if (!_swapper_set_image_layout (swapper, swapper->swap_chain_images[i],
743             VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, error)) {
744       g_free (swap_chain_images);
745       return FALSE;
746     }
747   }
748 
749   g_free (swap_chain_images);
750   return TRUE;
751 }
752 
753 static gboolean
_swapchain_resize(GstVulkanSwapper * swapper,GError ** error)754 _swapchain_resize (GstVulkanSwapper * swapper, GError ** error)
755 {
756   int i;
757 
758   if (!swapper->queue) {
759     if (!_vulkan_swapper_retrieve_surface_properties (swapper, error)) {
760       return FALSE;
761     }
762   }
763 
764   if (swapper->swap_chain_images) {
765     for (i = 0; i < swapper->n_swap_chain_images; i++) {
766       if (swapper->swap_chain_images[i])
767         gst_memory_unref ((GstMemory *) swapper->swap_chain_images[i]);
768     }
769     g_free (swapper->swap_chain_images);
770   }
771 
772   return _allocate_swapchain (swapper, swapper->caps, error);
773 }
774 
775 gboolean
gst_vulkan_swapper_set_caps(GstVulkanSwapper * swapper,GstCaps * caps,GError ** error)776 gst_vulkan_swapper_set_caps (GstVulkanSwapper * swapper, GstCaps * caps,
777     GError ** error)
778 {
779   if (!gst_video_info_from_caps (&swapper->v_info, caps)) {
780     g_set_error (error, GST_VULKAN_ERROR,
781         VK_ERROR_INITIALIZATION_FAILED,
782         "Failed to geto GstVideoInfo from caps");
783     return FALSE;
784   }
785 
786   gst_caps_replace (&swapper->caps, caps);
787 
788   return _swapchain_resize (swapper, error);
789 }
790 
791 static gboolean
_build_render_buffer_cmd(GstVulkanSwapper * swapper,guint32 swap_idx,GstBuffer * buffer,VkCommandBuffer * cmd_ret,GError ** error)792 _build_render_buffer_cmd (GstVulkanSwapper * swapper, guint32 swap_idx,
793     GstBuffer * buffer, VkCommandBuffer * cmd_ret, GError ** error)
794 {
795   GstVulkanBufferMemory *buf_mem;
796   GstVulkanImageMemory *swap_mem;
797   VkCommandBuffer cmd;
798   VkResult err;
799 
800   g_return_val_if_fail (swap_idx < swapper->n_swap_chain_images, FALSE);
801   swap_mem = swapper->swap_chain_images[swap_idx];
802 
803   if (!gst_vulkan_device_create_cmd_buffer (swapper->device, &cmd, error))
804     return FALSE;
805 
806   buf_mem = (GstVulkanBufferMemory *) gst_buffer_peek_memory (buffer, 0);
807 
808   {
809     VkCommandBufferInheritanceInfo buf_inh = { 0, };
810     VkCommandBufferBeginInfo cmd_buf_info = { 0, };
811 
812     buf_inh.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
813     buf_inh.pNext = NULL;
814     buf_inh.renderPass = VK_NULL_HANDLE;
815     buf_inh.subpass = 0;
816     buf_inh.framebuffer = VK_NULL_HANDLE;
817     buf_inh.occlusionQueryEnable = FALSE;
818     buf_inh.queryFlags = 0;
819     buf_inh.pipelineStatistics = 0;
820 
821     cmd_buf_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
822     cmd_buf_info.pNext = NULL;
823     cmd_buf_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
824     cmd_buf_info.pInheritanceInfo = &buf_inh;
825 
826     err = vkBeginCommandBuffer (cmd, &cmd_buf_info);
827     if (gst_vulkan_error_to_g_error (err, error, "vkBeginCommandBuffer") < 0)
828       return FALSE;
829   }
830 
831   if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem,
832           VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, error)) {
833     return FALSE;
834   }
835 
836   {
837     VkBufferImageCopy region = { 0, };
838     GstVideoRectangle src, dst, rslt;
839 
840     src.x = src.y = 0;
841     src.w = GST_VIDEO_INFO_WIDTH (&swapper->v_info);
842     src.h = GST_VIDEO_INFO_HEIGHT (&swapper->v_info);
843 
844     dst.x = dst.y = 0;
845     dst.w = gst_vulkan_image_memory_get_width (swap_mem);
846     dst.h = gst_vulkan_image_memory_get_height (swap_mem);
847 
848     gst_video_sink_center_rect (src, dst, &rslt, FALSE);
849 
850     GST_TRACE_OBJECT (swapper, "rendering into result rectangle %ux%u+%u,%u "
851         "src %ux%u dst %ux%u", rslt.w, rslt.h, rslt.x, rslt.y, src.w, src.h,
852         dst.w, dst.h);
853     GST_VK_BUFFER_IMAGE_COPY (region, 0, src.w, src.h,
854         GST_VK_IMAGE_SUBRESOURCE_LAYERS_INIT (VK_IMAGE_ASPECT_COLOR_BIT, 0, 0,
855             1), GST_VK_OFFSET3D_INIT (rslt.x, rslt.y, 0),
856         GST_VK_EXTENT3D_INIT (rslt.w, rslt.h, 1));
857 
858     vkCmdCopyBufferToImage (cmd, buf_mem->buffer, swap_mem->image,
859         swap_mem->image_layout, 1, &region);
860   }
861 
862   if (!_swapper_set_image_layout_with_cmd (swapper, cmd, swap_mem,
863           VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, error)) {
864     return FALSE;
865   }
866 
867   err = vkEndCommandBuffer (cmd);
868   if (gst_vulkan_error_to_g_error (err, error, "vkEndCommandBuffer") < 0)
869     return FALSE;
870 
871   *cmd_ret = cmd;
872 
873   return TRUE;
874 }
875 
876 static gboolean
_render_buffer_unlocked(GstVulkanSwapper * swapper,GstBuffer * buffer,GError ** error)877 _render_buffer_unlocked (GstVulkanSwapper * swapper,
878     GstBuffer * buffer, GError ** error)
879 {
880   VkSemaphore acquire_semaphore = { 0, };
881   VkSemaphore present_semaphore = { 0, };
882   VkSemaphoreCreateInfo semaphore_info = { 0, };
883   GstVulkanFence *fence = NULL;
884   VkPresentInfoKHR present;
885   VkCommandBuffer cmd = VK_NULL_HANDLE;
886   guint32 swap_idx;
887   VkResult err, present_err = VK_SUCCESS;
888 
889   swapper->priv->trash_list =
890       gst_vulkan_trash_list_gc (swapper->priv->trash_list);
891 
892   semaphore_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
893   semaphore_info.pNext = NULL;
894   semaphore_info.flags = 0;
895 
896   if (!buffer) {
897     g_set_error (error, GST_VULKAN_ERROR,
898         VK_ERROR_INITIALIZATION_FAILED, "Invalid buffer");
899     goto error;
900   }
901 
902   if (g_atomic_int_get (&swapper->to_quit)) {
903     g_set_error (error, GST_VULKAN_ERROR, VK_ERROR_SURFACE_LOST_KHR,
904         "Output window was closed");
905     goto error;
906   }
907 
908   gst_buffer_replace (&swapper->current_buffer, buffer);
909 
910 reacquire:
911   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
912       NULL, &acquire_semaphore);
913   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
914     goto error;
915 
916   err =
917       swapper->AcquireNextImageKHR (swapper->device->device,
918       swapper->swap_chain, -1, acquire_semaphore, VK_NULL_HANDLE, &swap_idx);
919   /* TODO: Deal with the VK_SUBOPTIMAL_KHR and VK_ERROR_OUT_OF_DATE_KHR */
920   if (err == VK_ERROR_OUT_OF_DATE_KHR) {
921     GST_DEBUG_OBJECT (swapper, "out of date frame acquired");
922 
923     vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
924     if (!_swapchain_resize (swapper, error))
925       goto error;
926     goto reacquire;
927   } else if (gst_vulkan_error_to_g_error (err, error,
928           "vkAcquireNextImageKHR") < 0) {
929     goto error;
930   }
931 
932   if (!_build_render_buffer_cmd (swapper, swap_idx, buffer, &cmd, error))
933     goto error;
934 
935   err = vkCreateSemaphore (swapper->device->device, &semaphore_info,
936       NULL, &present_semaphore);
937   if (gst_vulkan_error_to_g_error (err, error, "vkCreateSemaphore") < 0)
938     goto error;
939 
940   {
941     VkSubmitInfo submit_info = { 0, };
942     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
943 
944     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
945     submit_info.pNext = NULL;
946     submit_info.waitSemaphoreCount = 1;
947     submit_info.pWaitSemaphores = &acquire_semaphore;
948     submit_info.pWaitDstStageMask = &stages;
949     submit_info.commandBufferCount = 1;
950     submit_info.pCommandBuffers = &cmd;
951     submit_info.signalSemaphoreCount = 1;
952     submit_info.pSignalSemaphores = &present_semaphore;
953 
954     fence = gst_vulkan_fence_new (swapper->device, 0, error);
955     if (!fence)
956       goto error;
957 
958     err =
959         vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
960         GST_VULKAN_FENCE_FENCE (fence));
961     if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
962       goto error;
963 
964     swapper->priv->trash_list = g_list_prepend (swapper->priv->trash_list,
965         gst_vulkan_trash_new_free_command_buffer (gst_vulkan_fence_ref (fence),
966             cmd));
967     swapper->priv->trash_list = g_list_prepend (swapper->priv->trash_list,
968         gst_vulkan_trash_new_free_semaphore (fence, acquire_semaphore));
969 
970     cmd = VK_NULL_HANDLE;
971     fence = NULL;
972   }
973 
974   present.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
975   present.pNext = NULL;
976   present.waitSemaphoreCount = 1;
977   present.pWaitSemaphores = &present_semaphore;
978   present.swapchainCount = 1;
979   present.pSwapchains = &swapper->swap_chain;
980   present.pImageIndices = &swap_idx;
981   present.pResults = &present_err;
982 
983   err = swapper->QueuePresentKHR (swapper->queue->queue, &present);
984   if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
985     goto error;
986 
987   if (present_err == VK_ERROR_OUT_OF_DATE_KHR) {
988     GST_DEBUG_OBJECT (swapper, "out of date frame submitted");
989 
990     if (!_swapchain_resize (swapper, error))
991       goto error;
992   } else if (gst_vulkan_error_to_g_error (err, error, "vkQueuePresentKHR") < 0)
993     goto error;
994 
995   {
996     VkSubmitInfo submit_info = { 0, };
997     VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
998 
999     submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
1000     submit_info.pWaitDstStageMask = &stages;
1001 
1002     fence = gst_vulkan_fence_new (swapper->device, 0, error);
1003     if (!fence)
1004       goto error;
1005 
1006     err =
1007         vkQueueSubmit (swapper->queue->queue, 1, &submit_info,
1008         GST_VULKAN_FENCE_FENCE (fence));
1009     if (gst_vulkan_error_to_g_error (err, error, "vkQueueSubmit") < 0)
1010       goto error;
1011 
1012     swapper->priv->trash_list = g_list_prepend (swapper->priv->trash_list,
1013         gst_vulkan_trash_new_free_semaphore (fence, present_semaphore));
1014     fence = NULL;
1015   }
1016 
1017   return TRUE;
1018 
1019 error:
1020   {
1021     if (acquire_semaphore)
1022       vkDestroySemaphore (swapper->device->device, acquire_semaphore, NULL);
1023     if (present_semaphore)
1024       vkDestroySemaphore (swapper->device->device, present_semaphore, NULL);
1025     if (cmd)
1026       vkFreeCommandBuffers (swapper->device->device, swapper->device->cmd_pool,
1027           1, &cmd);
1028     return FALSE;
1029   }
1030 }
1031 
1032 gboolean
gst_vulkan_swapper_render_buffer(GstVulkanSwapper * swapper,GstBuffer * buffer,GError ** error)1033 gst_vulkan_swapper_render_buffer (GstVulkanSwapper * swapper,
1034     GstBuffer * buffer, GError ** error)
1035 {
1036   GstMemory *mem;
1037   gboolean ret;
1038 
1039   mem = gst_buffer_peek_memory (buffer, 0);
1040   if (!mem) {
1041     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1042         "Buffer has no memory");
1043     return FALSE;
1044   }
1045   if (!gst_is_vulkan_buffer_memory (mem)) {
1046     g_set_error_literal (error, GST_VULKAN_ERROR, VK_ERROR_FORMAT_NOT_SUPPORTED,
1047         "Incorrect memory type");
1048     return FALSE;
1049   }
1050 
1051   RENDER_LOCK (swapper);
1052   ret = _render_buffer_unlocked (swapper, buffer, error);
1053   RENDER_UNLOCK (swapper);
1054 
1055   return ret;
1056 }
1057 
1058 static void
_on_window_draw(GstVulkanWindow * window,GstVulkanSwapper * swapper)1059 _on_window_draw (GstVulkanWindow * window, GstVulkanSwapper * swapper)
1060 {
1061   GError *error = NULL;
1062 
1063   RENDER_LOCK (swapper);
1064   if (!swapper->current_buffer) {
1065     RENDER_UNLOCK (swapper);
1066     return;
1067   }
1068 
1069   /* TODO: perform some rate limiting of the number of redraw events */
1070   if (!_render_buffer_unlocked (swapper, swapper->current_buffer, &error))
1071     GST_ERROR_OBJECT (swapper, "Failed to redraw buffer %p %s",
1072         swapper->current_buffer, error->message);
1073   g_clear_error (&error);
1074   RENDER_UNLOCK (swapper);
1075 }
1076