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