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, ®ion);
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