1 /*
2 * GStreamer
3 * Copyright (C) 2016 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 /**
22 * SECTION:element-vulkanupload
23 * @title: vulkanupload
24 *
25 * vulkanupload uploads data into Vulkan memory objects.
26 */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <string.h>
33 #include "gstvulkanelements.h"
34 #include "vkupload.h"
35
36 GST_DEBUG_CATEGORY (gst_debug_vulkan_upload);
37 #define GST_CAT_DEFAULT gst_debug_vulkan_upload
38
39 static GstCaps *
_set_caps_features_with_passthrough(const GstCaps * caps,const gchar * feature_name,GstCapsFeatures * passthrough)40 _set_caps_features_with_passthrough (const GstCaps * caps,
41 const gchar * feature_name, GstCapsFeatures * passthrough)
42 {
43 guint i, j, m, n;
44 GstCaps *tmp;
45
46 tmp = gst_caps_copy (caps);
47
48 n = gst_caps_get_size (caps);
49 for (i = 0; i < n; i++) {
50 GstCapsFeatures *features, *orig_features;
51
52 orig_features = gst_caps_get_features (caps, i);
53 features = gst_caps_features_new (feature_name, NULL);
54
55 m = gst_caps_features_get_size (orig_features);
56 for (j = 0; j < m; j++) {
57 const gchar *feature = gst_caps_features_get_nth (orig_features, j);
58
59 /* if we already have the features */
60 if (gst_caps_features_contains (features, feature))
61 continue;
62
63 if (g_strcmp0 (feature, GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY) == 0)
64 continue;
65
66 if (passthrough && gst_caps_features_contains (passthrough, feature)) {
67 gst_caps_features_add (features, feature);
68 }
69 }
70
71 gst_caps_set_features (tmp, i, features);
72 }
73
74 return tmp;
75 }
76
77 struct BufferUpload
78 {
79 GstVulkanUpload *upload;
80 };
81
82 static gpointer
_buffer_new_impl(GstVulkanUpload * upload)83 _buffer_new_impl (GstVulkanUpload * upload)
84 {
85 struct BufferUpload *raw = g_new0 (struct BufferUpload, 1);
86
87 raw->upload = upload;
88
89 return raw;
90 }
91
92 static GstCaps *
_buffer_transform_caps(gpointer impl,GstPadDirection direction,GstCaps * caps)93 _buffer_transform_caps (gpointer impl, GstPadDirection direction,
94 GstCaps * caps)
95 {
96 return gst_caps_ref (caps);
97 }
98
99 static gboolean
_buffer_set_caps(gpointer impl,GstCaps * in_caps,GstCaps * out_caps)100 _buffer_set_caps (gpointer impl, GstCaps * in_caps, GstCaps * out_caps)
101 {
102 return TRUE;
103 }
104
105 static void
_buffer_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)106 _buffer_propose_allocation (gpointer impl, GstQuery * decide_query,
107 GstQuery * query)
108 {
109 struct BufferUpload *raw = impl;
110 gboolean need_pool;
111 GstCaps *caps;
112 GstVideoInfo info;
113 guint size;
114 GstBufferPool *pool = NULL;
115
116 gst_query_parse_allocation (query, &caps, &need_pool);
117
118 if (caps == NULL)
119 return;
120
121 if (!gst_video_info_from_caps (&info, caps))
122 return;
123
124 /* the normal size of a frame */
125 size = info.size;
126
127 if (need_pool) {
128 GstStructure *config;
129
130 pool = gst_vulkan_buffer_pool_new (raw->upload->device);
131
132 config = gst_buffer_pool_get_config (pool);
133 gst_buffer_pool_config_set_params (config, caps, size, 0, 0);
134
135 if (!gst_buffer_pool_set_config (pool, config)) {
136 g_object_unref (pool);
137 return;
138 }
139 }
140
141 gst_query_add_allocation_pool (query, pool, size, 1, 0);
142 if (pool)
143 g_object_unref (pool);
144
145 return;
146 }
147
148 static GstFlowReturn
_buffer_perform(gpointer impl,GstBuffer * inbuf,GstBuffer ** outbuf)149 _buffer_perform (gpointer impl, GstBuffer * inbuf, GstBuffer ** outbuf)
150 {
151 if (!gst_is_vulkan_buffer_memory (gst_buffer_peek_memory (inbuf, 0)))
152 return GST_FLOW_ERROR;
153
154 *outbuf = inbuf;
155
156 return GST_FLOW_OK;
157 }
158
159 static void
_buffer_free(gpointer impl)160 _buffer_free (gpointer impl)
161 {
162 g_free (impl);
163 }
164
165 static GstStaticCaps _buffer_in_templ =
166 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER ") ;"
167 "video/x-raw");
168 static GstStaticCaps _buffer_out_templ =
169 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER ")");
170
171 static const struct UploadMethod buffer_upload = {
172 "VulkanBuffer",
173 &_buffer_in_templ,
174 &_buffer_out_templ,
175 _buffer_new_impl,
176 _buffer_transform_caps,
177 _buffer_set_caps,
178 _buffer_propose_allocation,
179 _buffer_perform,
180 _buffer_free,
181 };
182
183 struct RawToBufferUpload
184 {
185 GstVulkanUpload *upload;
186
187 GstVideoInfo in_info;
188 GstVideoInfo out_info;
189
190 GstBufferPool *pool;
191 gboolean pool_active;
192
193 gsize alloc_sizes[GST_VIDEO_MAX_PLANES];
194 };
195
196 static gpointer
_raw_to_buffer_new_impl(GstVulkanUpload * upload)197 _raw_to_buffer_new_impl (GstVulkanUpload * upload)
198 {
199 struct RawToBufferUpload *raw = g_new0 (struct RawToBufferUpload, 1);
200
201 raw->upload = upload;
202
203 return raw;
204 }
205
206 static GstCaps *
_raw_to_buffer_transform_caps(gpointer impl,GstPadDirection direction,GstCaps * caps)207 _raw_to_buffer_transform_caps (gpointer impl, GstPadDirection direction,
208 GstCaps * caps)
209 {
210 GstCaps *ret;
211
212 if (direction == GST_PAD_SINK) {
213 ret =
214 _set_caps_features_with_passthrough (caps,
215 GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER, NULL);
216 } else {
217 ret =
218 _set_caps_features_with_passthrough (caps,
219 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, NULL);
220 }
221
222 return ret;
223 }
224
225 static gboolean
_raw_to_buffer_set_caps(gpointer impl,GstCaps * in_caps,GstCaps * out_caps)226 _raw_to_buffer_set_caps (gpointer impl, GstCaps * in_caps, GstCaps * out_caps)
227 {
228 struct RawToBufferUpload *raw = impl;
229 guint out_width, out_height;
230 guint i;
231
232 if (!gst_video_info_from_caps (&raw->in_info, in_caps))
233 return FALSE;
234
235 if (!gst_video_info_from_caps (&raw->out_info, out_caps))
236 return FALSE;
237
238 out_width = GST_VIDEO_INFO_WIDTH (&raw->out_info);
239 out_height = GST_VIDEO_INFO_HEIGHT (&raw->out_info);
240
241 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->out_info); i++) {
242 GstVulkanImageMemory *img_mem;
243 VkFormat vk_format;
244
245 vk_format = gst_vulkan_format_from_video_info (&raw->out_info, i);
246
247 img_mem = (GstVulkanImageMemory *)
248 gst_vulkan_image_memory_alloc (raw->upload->device, vk_format,
249 out_width, out_height, VK_IMAGE_TILING_OPTIMAL,
250 VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT,
251 VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
252
253 raw->alloc_sizes[i] = img_mem->requirements.size;
254
255 gst_memory_unref (GST_MEMORY_CAST (img_mem));
256 }
257
258 return TRUE;
259 }
260
261 static void
_raw_to_buffer_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)262 _raw_to_buffer_propose_allocation (gpointer impl, GstQuery * decide_query,
263 GstQuery * query)
264 {
265 /* a little trickery with the impl pointer */
266 _buffer_propose_allocation (impl, decide_query, query);
267 }
268
269 static GstFlowReturn
_raw_to_buffer_perform(gpointer impl,GstBuffer * inbuf,GstBuffer ** outbuf)270 _raw_to_buffer_perform (gpointer impl, GstBuffer * inbuf, GstBuffer ** outbuf)
271 {
272 struct RawToBufferUpload *raw = impl;
273 GstVideoFrame v_frame;
274 GstFlowReturn ret;
275 guint i;
276
277 if (!raw->pool) {
278 GstStructure *config;
279 guint min = 0, max = 0;
280 gsize size = 1;
281
282 raw->pool = gst_vulkan_buffer_pool_new (raw->upload->device);
283 config = gst_buffer_pool_get_config (raw->pool);
284 gst_buffer_pool_config_set_params (config, raw->upload->out_caps, size, min,
285 max);
286 gst_buffer_pool_set_config (raw->pool, config);
287 }
288 if (!raw->pool_active) {
289 gst_buffer_pool_set_active (raw->pool, TRUE);
290 raw->pool_active = TRUE;
291 }
292
293 if ((ret =
294 gst_buffer_pool_acquire_buffer (raw->pool, outbuf,
295 NULL)) != GST_FLOW_OK)
296 goto out;
297
298 if (!gst_video_frame_map (&v_frame, &raw->in_info, inbuf, GST_MAP_READ)) {
299 GST_ELEMENT_ERROR (raw->upload, RESOURCE, NOT_FOUND,
300 ("%s", "Failed to map input buffer"), NULL);
301 return GST_FLOW_ERROR;
302 }
303
304 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->out_info); i++) {
305 GstMapInfo map_info;
306 gsize plane_size;
307 GstMemory *mem;
308
309 mem = gst_buffer_peek_memory (*outbuf, i);
310 if (!gst_memory_map (GST_MEMORY_CAST (mem), &map_info, GST_MAP_WRITE)) {
311 GST_ELEMENT_ERROR (raw->upload, RESOURCE, NOT_FOUND,
312 ("%s", "Failed to map output memory"), NULL);
313 gst_buffer_unref (*outbuf);
314 *outbuf = NULL;
315 ret = GST_FLOW_ERROR;
316 goto out;
317 }
318
319 plane_size =
320 GST_VIDEO_INFO_PLANE_STRIDE (&raw->out_info,
321 i) * GST_VIDEO_INFO_COMP_HEIGHT (&raw->out_info, i);
322 g_assert (plane_size < map_info.size);
323 memcpy (map_info.data, v_frame.data[i], plane_size);
324
325 gst_memory_unmap (GST_MEMORY_CAST (mem), &map_info);
326 }
327
328 gst_video_frame_unmap (&v_frame);
329
330 ret = GST_FLOW_OK;
331
332 out:
333 return ret;
334 }
335
336 static void
_raw_to_buffer_free(gpointer impl)337 _raw_to_buffer_free (gpointer impl)
338 {
339 struct RawToBufferUpload *raw = impl;
340
341 if (raw->pool) {
342 if (raw->pool_active) {
343 gst_buffer_pool_set_active (raw->pool, FALSE);
344 }
345 raw->pool_active = FALSE;
346 gst_object_unref (raw->pool);
347 raw->pool = NULL;
348 }
349
350 g_free (impl);
351 }
352
353 static GstStaticCaps _raw_to_buffer_in_templ = GST_STATIC_CAPS ("video/x-raw");
354 static GstStaticCaps _raw_to_buffer_out_templ =
355 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER ")");
356
357 static const struct UploadMethod raw_to_buffer_upload = {
358 "RawToVulkanBuffer",
359 &_raw_to_buffer_in_templ,
360 &_raw_to_buffer_out_templ,
361 _raw_to_buffer_new_impl,
362 _raw_to_buffer_transform_caps,
363 _raw_to_buffer_set_caps,
364 _raw_to_buffer_propose_allocation,
365 _raw_to_buffer_perform,
366 _raw_to_buffer_free,
367 };
368
369 struct BufferToImageUpload
370 {
371 GstVulkanUpload *upload;
372
373 GstVideoInfo in_info;
374 GstVideoInfo out_info;
375
376 GstBufferPool *pool;
377 gboolean pool_active;
378
379 GstVulkanCommandPool *cmd_pool;
380 GstVulkanTrashList *trash_list;
381 };
382
383 static gpointer
_buffer_to_image_new_impl(GstVulkanUpload * upload)384 _buffer_to_image_new_impl (GstVulkanUpload * upload)
385 {
386 struct BufferToImageUpload *raw = g_new0 (struct BufferToImageUpload, 1);
387
388 raw->upload = upload;
389 raw->trash_list = gst_vulkan_trash_fence_list_new ();
390
391 return raw;
392 }
393
394 static GstCaps *
_buffer_to_image_transform_caps(gpointer impl,GstPadDirection direction,GstCaps * caps)395 _buffer_to_image_transform_caps (gpointer impl, GstPadDirection direction,
396 GstCaps * caps)
397 {
398 GstCaps *ret;
399
400 if (direction == GST_PAD_SINK) {
401 ret =
402 _set_caps_features_with_passthrough (caps,
403 GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE, NULL);
404 } else {
405 ret =
406 _set_caps_features_with_passthrough (caps,
407 GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER, NULL);
408 }
409
410 return ret;
411 }
412
413 static gboolean
_buffer_to_image_set_caps(gpointer impl,GstCaps * in_caps,GstCaps * out_caps)414 _buffer_to_image_set_caps (gpointer impl, GstCaps * in_caps, GstCaps * out_caps)
415 {
416 struct BufferToImageUpload *raw = impl;
417
418 if (!gst_video_info_from_caps (&raw->in_info, in_caps))
419 return FALSE;
420
421 if (!gst_video_info_from_caps (&raw->out_info, out_caps))
422 return FALSE;
423
424 return TRUE;
425 }
426
427 static void
_buffer_to_image_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)428 _buffer_to_image_propose_allocation (gpointer impl, GstQuery * decide_query,
429 GstQuery * query)
430 {
431 /* a little trickery with the impl pointer */
432 _buffer_propose_allocation (impl, decide_query, query);
433 }
434
435 static GstFlowReturn
_buffer_to_image_perform(gpointer impl,GstBuffer * inbuf,GstBuffer ** outbuf)436 _buffer_to_image_perform (gpointer impl, GstBuffer * inbuf, GstBuffer ** outbuf)
437 {
438 struct BufferToImageUpload *raw = impl;
439 GstFlowReturn ret;
440 GError *error = NULL;
441 VkResult err;
442 GstVulkanCommandBuffer *cmd_buf;
443 guint i;
444
445 if (!raw->cmd_pool) {
446 if (!(raw->cmd_pool =
447 gst_vulkan_queue_create_command_pool (raw->upload->queue,
448 &error))) {
449 goto error;
450 }
451 }
452
453 if (!(cmd_buf = gst_vulkan_command_pool_create (raw->cmd_pool, &error)))
454 goto error;
455
456 if (!raw->pool) {
457 GstStructure *config;
458 guint min = 0, max = 0;
459 gsize size = 1;
460
461 raw->pool = gst_vulkan_image_buffer_pool_new (raw->upload->device);
462 config = gst_buffer_pool_get_config (raw->pool);
463 gst_buffer_pool_config_set_params (config, raw->upload->out_caps, size, min,
464 max);
465 gst_buffer_pool_set_config (raw->pool, config);
466 }
467 if (!raw->pool_active) {
468 gst_buffer_pool_set_active (raw->pool, TRUE);
469 raw->pool_active = TRUE;
470 }
471
472 if ((ret =
473 gst_buffer_pool_acquire_buffer (raw->pool, outbuf,
474 NULL)) != GST_FLOW_OK)
475 goto out;
476
477 {
478 /* *INDENT-OFF* */
479 VkCommandBufferBeginInfo cmd_buf_info = {
480 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
481 .pNext = NULL,
482 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
483 .pInheritanceInfo = NULL
484 };
485 /* *INDENT-ON* */
486
487 gst_vulkan_command_buffer_lock (cmd_buf);
488 err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
489 if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
490 goto unlock_error;
491 }
492
493 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->out_info); i++) {
494 VkBufferImageCopy region;
495 GstMemory *in_mem, *out_mem;
496 GstVulkanBufferMemory *buf_mem;
497 GstVulkanImageMemory *img_mem;
498 VkImageMemoryBarrier image_memory_barrier;
499 VkBufferMemoryBarrier buffer_memory_barrier;
500
501 in_mem = gst_buffer_peek_memory (inbuf, i);
502 if (!gst_is_vulkan_buffer_memory (in_mem)) {
503 GST_WARNING_OBJECT (raw->upload, "Input is not a GstVulkanBufferMemory");
504 goto unlock_error;
505 }
506 buf_mem = (GstVulkanBufferMemory *) in_mem;
507
508 out_mem = gst_buffer_peek_memory (*outbuf, i);
509 if (!gst_is_vulkan_image_memory (out_mem)) {
510 GST_WARNING_OBJECT (raw->upload, "Output is not a GstVulkanImageMemory");
511 goto unlock_error;
512 }
513 img_mem = (GstVulkanImageMemory *) out_mem;
514
515 /* *INDENT-OFF* */
516 region = (VkBufferImageCopy) {
517 .bufferOffset = 0,
518 .bufferRowLength = GST_VIDEO_INFO_COMP_WIDTH (&raw->in_info, i),
519 .bufferImageHeight = GST_VIDEO_INFO_COMP_HEIGHT (&raw->in_info, i),
520 .imageSubresource = {
521 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
522 .mipLevel = 0,
523 .baseArrayLayer = 0,
524 .layerCount = 1,
525 },
526 .imageOffset = { .x = 0, .y = 0, .z = 0, },
527 .imageExtent = {
528 .width = GST_VIDEO_INFO_COMP_WIDTH (&raw->out_info, i),
529 .height = GST_VIDEO_INFO_COMP_HEIGHT (&raw->out_info, i),
530 .depth = 1,
531 }
532 };
533
534 image_memory_barrier = (VkImageMemoryBarrier) {
535 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
536 .pNext = NULL,
537 .srcAccessMask = img_mem->barrier.parent.access_flags,
538 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
539 .oldLayout = img_mem->barrier.image_layout,
540 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
541 /* FIXME: implement exclusive transfers */
542 .srcQueueFamilyIndex = 0,
543 .dstQueueFamilyIndex = 0,
544 .image = img_mem->image,
545 .subresourceRange = img_mem->barrier.subresource_range
546 };
547
548 buffer_memory_barrier = (VkBufferMemoryBarrier) {
549 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
550 .pNext = NULL,
551 .srcAccessMask = buf_mem->barrier.parent.access_flags,
552 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
553 /* FIXME: implement exclusive transfers */
554 .srcQueueFamilyIndex = 0,
555 .dstQueueFamilyIndex = 0,
556 .buffer = buf_mem->buffer,
557 .offset = region.bufferOffset,
558 .size = region.bufferRowLength * region.bufferImageHeight
559 };
560 /* *INDENT-ON* */
561
562 vkCmdPipelineBarrier (cmd_buf->cmd,
563 buf_mem->barrier.parent.pipeline_stages | img_mem->barrier.
564 parent.pipeline_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1,
565 &buffer_memory_barrier, 1, &image_memory_barrier);
566
567 buf_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
568 buf_mem->barrier.parent.access_flags = buffer_memory_barrier.dstAccessMask;
569
570 img_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
571 img_mem->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
572 img_mem->barrier.image_layout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
573
574 vkCmdCopyBufferToImage (cmd_buf->cmd, buf_mem->buffer, img_mem->image,
575 img_mem->barrier.image_layout, 1, ®ion);
576 }
577
578 err = vkEndCommandBuffer (cmd_buf->cmd);
579 gst_vulkan_command_buffer_unlock (cmd_buf);
580 if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0)
581 goto error;
582
583 {
584 VkSubmitInfo submit_info = { 0, };
585 VkPipelineStageFlags stages = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
586 GstVulkanFence *fence;
587
588 /* *INDENT-OFF* */
589 submit_info = (VkSubmitInfo) {
590 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
591 .pNext = NULL,
592 .waitSemaphoreCount = 0,
593 .pWaitSemaphores = NULL,
594 .pWaitDstStageMask = &stages,
595 .commandBufferCount = 1,
596 .pCommandBuffers = &cmd_buf->cmd,
597 .signalSemaphoreCount = 0,
598 .pSignalSemaphores = NULL
599 };
600 /* *INDENT-ON* */
601
602 fence = gst_vulkan_device_create_fence (raw->upload->device, &error);
603 if (!fence)
604 goto error;
605
606 err =
607 vkQueueSubmit (raw->upload->queue->queue, 1, &submit_info,
608 GST_VULKAN_FENCE_FENCE (fence));
609 if (gst_vulkan_error_to_g_error (err, &error, "vkQueueSubmit") < 0)
610 goto error;
611
612 gst_vulkan_trash_list_add (raw->trash_list,
613 gst_vulkan_trash_list_acquire (raw->trash_list, fence,
614 gst_vulkan_trash_mini_object_unref,
615 GST_MINI_OBJECT_CAST (cmd_buf)));
616 gst_vulkan_fence_unref (fence);
617 }
618
619 gst_vulkan_trash_list_gc (raw->trash_list);
620
621 ret = GST_FLOW_OK;
622
623 out:
624 return ret;
625
626 unlock_error:
627 if (cmd_buf) {
628 gst_vulkan_command_buffer_unlock (cmd_buf);
629 gst_vulkan_command_buffer_unref (cmd_buf);
630 }
631 error:
632 if (error) {
633 GST_WARNING_OBJECT (raw->upload, "Error: %s", error->message);
634 g_clear_error (&error);
635 }
636 gst_clear_buffer (outbuf);
637 ret = GST_FLOW_ERROR;
638 goto out;
639 }
640
641 static void
_buffer_to_image_free(gpointer impl)642 _buffer_to_image_free (gpointer impl)
643 {
644 struct BufferToImageUpload *raw = impl;
645
646 if (raw->pool) {
647 if (raw->pool_active) {
648 gst_buffer_pool_set_active (raw->pool, FALSE);
649 }
650 raw->pool_active = FALSE;
651 gst_object_unref (raw->pool);
652 raw->pool = NULL;
653 }
654
655 if (raw->cmd_pool)
656 gst_object_unref (raw->cmd_pool);
657 raw->cmd_pool = NULL;
658
659 if (!gst_vulkan_trash_list_wait (raw->trash_list, -1))
660 GST_WARNING_OBJECT (raw->upload,
661 "Failed to wait for all fences to complete " "before shutting down");
662 gst_object_unref (raw->trash_list);
663 raw->trash_list = NULL;
664
665 g_free (impl);
666 }
667
668 static GstStaticCaps _buffer_to_image_in_templ =
669 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_BUFFER ")");
670 static GstStaticCaps _buffer_to_image_out_templ =
671 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE ")");
672
673 static const struct UploadMethod buffer_to_image_upload = {
674 "BufferToVulkanImage",
675 &_buffer_to_image_in_templ,
676 &_buffer_to_image_out_templ,
677 _buffer_to_image_new_impl,
678 _buffer_to_image_transform_caps,
679 _buffer_to_image_set_caps,
680 _buffer_to_image_propose_allocation,
681 _buffer_to_image_perform,
682 _buffer_to_image_free,
683 };
684
685 struct RawToImageUpload
686 {
687 GstVulkanUpload *upload;
688
689 GstVideoInfo in_info;
690 GstVideoInfo out_info;
691
692 GstBufferPool *pool;
693 gboolean pool_active;
694
695 GstBufferPool *in_pool;
696 gboolean in_pool_active;
697
698 GstVulkanCommandPool *cmd_pool;
699 GstVulkanTrashList *trash_list;
700 };
701
702 static gpointer
_raw_to_image_new_impl(GstVulkanUpload * upload)703 _raw_to_image_new_impl (GstVulkanUpload * upload)
704 {
705 struct RawToImageUpload *raw = g_new0 (struct RawToImageUpload, 1);
706
707 raw->upload = upload;
708 raw->trash_list = gst_vulkan_trash_fence_list_new ();
709
710 return raw;
711 }
712
713 static GstCaps *
_raw_to_image_transform_caps(gpointer impl,GstPadDirection direction,GstCaps * caps)714 _raw_to_image_transform_caps (gpointer impl, GstPadDirection direction,
715 GstCaps * caps)
716 {
717 GstCaps *ret;
718
719 if (direction == GST_PAD_SINK) {
720 ret =
721 _set_caps_features_with_passthrough (caps,
722 GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE, NULL);
723 } else {
724 ret =
725 _set_caps_features_with_passthrough (caps,
726 GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY, NULL);
727 }
728
729 return ret;
730 }
731
732 static gboolean
_raw_to_image_set_caps(gpointer impl,GstCaps * in_caps,GstCaps * out_caps)733 _raw_to_image_set_caps (gpointer impl, GstCaps * in_caps, GstCaps * out_caps)
734 {
735 struct RawToImageUpload *raw = impl;
736
737 if (!gst_video_info_from_caps (&raw->in_info, in_caps))
738 return FALSE;
739
740 if (!gst_video_info_from_caps (&raw->out_info, out_caps))
741 return FALSE;
742
743 if (raw->in_pool) {
744 if (raw->in_pool_active) {
745 gst_buffer_pool_set_active (raw->in_pool, FALSE);
746 }
747 raw->in_pool_active = FALSE;
748 gst_object_unref (raw->in_pool);
749 raw->in_pool = NULL;
750 }
751
752 return TRUE;
753 }
754
755 static void
_raw_to_image_propose_allocation(gpointer impl,GstQuery * decide_query,GstQuery * query)756 _raw_to_image_propose_allocation (gpointer impl, GstQuery * decide_query,
757 GstQuery * query)
758 {
759 /* a little trickery with the impl pointer */
760 _buffer_propose_allocation (impl, decide_query, query);
761 }
762
763 static GstFlowReturn
_raw_to_image_perform(gpointer impl,GstBuffer * inbuf,GstBuffer ** outbuf)764 _raw_to_image_perform (gpointer impl, GstBuffer * inbuf, GstBuffer ** outbuf)
765 {
766 struct RawToImageUpload *raw = impl;
767 GstFlowReturn ret;
768 GstBuffer *in_vk_copy = NULL;
769 GstVulkanCommandBuffer *cmd_buf;
770 GError *error = NULL;
771 VkResult err;
772 guint i;
773
774 if (!raw->cmd_pool) {
775 if (!(raw->cmd_pool =
776 gst_vulkan_queue_create_command_pool (raw->upload->queue,
777 &error))) {
778 goto error;
779 }
780 }
781
782 if (!(cmd_buf = gst_vulkan_command_pool_create (raw->cmd_pool, &error)))
783 goto error;
784
785 if (!raw->pool) {
786 GstStructure *config;
787 guint min = 0, max = 0;
788 gsize size = 1;
789
790 raw->pool = gst_vulkan_image_buffer_pool_new (raw->upload->device);
791 config = gst_buffer_pool_get_config (raw->pool);
792 gst_buffer_pool_config_set_params (config, raw->upload->out_caps, size, min,
793 max);
794 gst_buffer_pool_set_config (raw->pool, config);
795 }
796 if (!raw->pool_active) {
797 gst_buffer_pool_set_active (raw->pool, TRUE);
798 raw->pool_active = TRUE;
799 }
800
801 if ((ret =
802 gst_buffer_pool_acquire_buffer (raw->pool, outbuf,
803 NULL)) != GST_FLOW_OK)
804 goto out;
805
806 {
807 /* *INDENT-OFF* */
808 VkCommandBufferBeginInfo cmd_buf_info = {
809 .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
810 .pNext = NULL,
811 .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
812 .pInheritanceInfo = NULL
813 };
814 /* *INDENT-ON* */
815
816 gst_vulkan_command_buffer_lock (cmd_buf);
817 err = vkBeginCommandBuffer (cmd_buf->cmd, &cmd_buf_info);
818 if (gst_vulkan_error_to_g_error (err, &error, "vkBeginCommandBuffer") < 0)
819 return FALSE;
820 }
821
822 for (i = 0; i < GST_VIDEO_INFO_N_PLANES (&raw->out_info); i++) {
823 VkBufferImageCopy region;
824 GstMemory *in_mem, *out_mem;
825 GstVulkanBufferMemory *buf_mem;
826 GstVulkanImageMemory *img_mem;
827 VkImageMemoryBarrier image_memory_barrier;
828 VkBufferMemoryBarrier buffer_memory_barrier;
829
830 in_mem = gst_buffer_peek_memory (inbuf, i);
831 if (gst_is_vulkan_buffer_memory (in_mem)) {
832 GST_TRACE_OBJECT (raw->upload, "Input is a GstVulkanBufferMemory");
833 buf_mem = (GstVulkanBufferMemory *) in_mem;
834 } else if (in_vk_copy) {
835 GST_TRACE_OBJECT (raw->upload,
836 "Have buffer copy of GstVulkanBufferMemory");
837 in_mem = gst_buffer_peek_memory (in_vk_copy, i);
838 g_assert (gst_is_vulkan_buffer_memory (in_mem));
839 buf_mem = (GstVulkanBufferMemory *) in_mem;
840 } else {
841 GstVideoFrame in_frame, out_frame;
842
843 GST_TRACE_OBJECT (raw->upload,
844 "Copying input to a new GstVulkanBufferMemory");
845 if (!raw->in_pool) {
846 GstStructure *config;
847 guint min = 0, max = 0;
848 gsize size = 1;
849
850 raw->in_pool = gst_vulkan_buffer_pool_new (raw->upload->device);
851 config = gst_buffer_pool_get_config (raw->in_pool);
852 gst_buffer_pool_config_set_params (config, raw->upload->in_caps, size,
853 min, max);
854 gst_buffer_pool_set_config (raw->in_pool, config);
855 }
856 if (!raw->in_pool_active) {
857 gst_buffer_pool_set_active (raw->in_pool, TRUE);
858 raw->in_pool_active = TRUE;
859 }
860
861 if ((ret =
862 gst_buffer_pool_acquire_buffer (raw->in_pool, &in_vk_copy,
863 NULL)) != GST_FLOW_OK) {
864 goto unlock_error;
865 }
866
867 if (!gst_video_frame_map (&in_frame, &raw->in_info, inbuf, GST_MAP_READ)) {
868 GST_WARNING_OBJECT (raw->upload, "Failed to map input buffer");
869 goto unlock_error;
870 }
871
872 if (!gst_video_frame_map (&out_frame, &raw->in_info, in_vk_copy,
873 GST_MAP_WRITE)) {
874 gst_video_frame_unmap (&in_frame);
875 GST_WARNING_OBJECT (raw->upload, "Failed to map input buffer");
876 goto unlock_error;
877 }
878
879 if (!gst_video_frame_copy (&out_frame, &in_frame)) {
880 gst_video_frame_unmap (&in_frame);
881 gst_video_frame_unmap (&out_frame);
882 GST_WARNING_OBJECT (raw->upload, "Failed to copy input buffer");
883 goto unlock_error;
884 }
885
886 gst_video_frame_unmap (&in_frame);
887 gst_video_frame_unmap (&out_frame);
888
889 in_mem = gst_buffer_peek_memory (in_vk_copy, i);
890 buf_mem = (GstVulkanBufferMemory *) in_mem;
891 }
892
893 out_mem = gst_buffer_peek_memory (*outbuf, i);
894 if (!gst_is_vulkan_image_memory (out_mem)) {
895 GST_WARNING_OBJECT (raw->upload, "Output is not a GstVulkanImageMemory");
896 goto unlock_error;
897 }
898 img_mem = (GstVulkanImageMemory *) out_mem;
899
900 /* *INDENT-OFF* */
901 region = (VkBufferImageCopy) {
902 .bufferOffset = 0,
903 .bufferRowLength = GST_VIDEO_INFO_COMP_WIDTH (&raw->in_info, i),
904 .bufferImageHeight = GST_VIDEO_INFO_COMP_HEIGHT (&raw->in_info, i),
905 .imageSubresource = {
906 .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
907 .mipLevel = 0,
908 .baseArrayLayer = 0,
909 .layerCount = 1,
910 },
911 .imageOffset = { .x = 0, .y = 0, .z = 0, },
912 .imageExtent = {
913 .width = GST_VIDEO_INFO_COMP_WIDTH (&raw->out_info, i),
914 .height = GST_VIDEO_INFO_COMP_HEIGHT (&raw->out_info, i),
915 .depth = 1,
916 }
917 };
918
919 buffer_memory_barrier = (VkBufferMemoryBarrier) {
920 .sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
921 .pNext = NULL,
922 .srcAccessMask = buf_mem->barrier.parent.access_flags,
923 .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
924 /* FIXME: implement exclusive transfers */
925 .srcQueueFamilyIndex = 0,
926 .dstQueueFamilyIndex = 0,
927 .buffer = buf_mem->buffer,
928 .offset = region.bufferOffset,
929 .size = region.bufferRowLength * region.bufferImageHeight,
930 };
931
932 image_memory_barrier = (VkImageMemoryBarrier) {
933 .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
934 .pNext = NULL,
935 .srcAccessMask = img_mem->barrier.parent.access_flags,
936 .dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
937 .oldLayout = img_mem->barrier.image_layout,
938 .newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
939 /* FIXME: implement exclusive transfers */
940 .srcQueueFamilyIndex = 0,
941 .dstQueueFamilyIndex = 0,
942 .image = img_mem->image,
943 .subresourceRange = img_mem->barrier.subresource_range,
944 };
945 /* *INDENT-ON* */
946
947 vkCmdPipelineBarrier (cmd_buf->cmd,
948 buf_mem->barrier.parent.pipeline_stages | img_mem->barrier.
949 parent.pipeline_stages, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 1,
950 &buffer_memory_barrier, 1, &image_memory_barrier);
951
952 buf_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
953 buf_mem->barrier.parent.access_flags = buffer_memory_barrier.dstAccessMask;
954
955 img_mem->barrier.parent.pipeline_stages = VK_PIPELINE_STAGE_TRANSFER_BIT;
956 img_mem->barrier.parent.access_flags = image_memory_barrier.dstAccessMask;
957 img_mem->barrier.image_layout = image_memory_barrier.newLayout;
958
959 vkCmdCopyBufferToImage (cmd_buf->cmd, buf_mem->buffer, img_mem->image,
960 img_mem->barrier.image_layout, 1, ®ion);
961 }
962
963 err = vkEndCommandBuffer (cmd_buf->cmd);
964 gst_vulkan_command_buffer_unlock (cmd_buf);
965 if (gst_vulkan_error_to_g_error (err, &error, "vkEndCommandBuffer") < 0) {
966 goto error;
967 }
968
969 {
970 VkSubmitInfo submit_info = { 0, };
971 GstVulkanFence *fence;
972
973 /* *INDENT-OFF* */
974 submit_info = (VkSubmitInfo) {
975 .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
976 .pNext = NULL,
977 .waitSemaphoreCount = 0,
978 .pWaitSemaphores = NULL,
979 .pWaitDstStageMask = NULL,
980 .commandBufferCount = 1,
981 .pCommandBuffers = &cmd_buf->cmd,
982 .signalSemaphoreCount = 0,
983 .pSignalSemaphores = NULL,
984 };
985 /* *INDENT-ON* */
986
987 fence = gst_vulkan_device_create_fence (raw->upload->device, &error);
988 if (!fence)
989 goto error;
990
991 gst_vulkan_queue_submit_lock (raw->upload->queue);
992 err =
993 vkQueueSubmit (raw->upload->queue->queue, 1, &submit_info,
994 GST_VULKAN_FENCE_FENCE (fence));
995 gst_vulkan_queue_submit_unlock (raw->upload->queue);
996 if (gst_vulkan_error_to_g_error (err, &error, "vkQueueSubmit") < 0)
997 goto error;
998
999 gst_vulkan_trash_list_add (raw->trash_list,
1000 gst_vulkan_trash_list_acquire (raw->trash_list, fence,
1001 gst_vulkan_trash_mini_object_unref,
1002 GST_MINI_OBJECT_CAST (cmd_buf)));
1003 gst_vulkan_fence_unref (fence);
1004 }
1005
1006 gst_vulkan_trash_list_gc (raw->trash_list);
1007
1008 ret = GST_FLOW_OK;
1009
1010 out:
1011 if (in_vk_copy)
1012 gst_buffer_unref (in_vk_copy);
1013
1014 return ret;
1015
1016 unlock_error:
1017 if (cmd_buf) {
1018 gst_vulkan_command_buffer_lock (cmd_buf);
1019 gst_vulkan_command_buffer_unref (cmd_buf);
1020 }
1021 error:
1022 if (error) {
1023 GST_WARNING_OBJECT (raw->upload, "Error: %s", error->message);
1024 g_clear_error (&error);
1025 }
1026 gst_clear_buffer (outbuf);
1027 ret = GST_FLOW_ERROR;
1028 goto out;
1029 }
1030
1031 static void
_raw_to_image_free(gpointer impl)1032 _raw_to_image_free (gpointer impl)
1033 {
1034 struct RawToImageUpload *raw = impl;
1035
1036 if (raw->pool) {
1037 if (raw->pool_active) {
1038 gst_buffer_pool_set_active (raw->pool, FALSE);
1039 }
1040 raw->pool_active = FALSE;
1041 gst_object_unref (raw->pool);
1042 raw->pool = NULL;
1043 }
1044
1045 if (raw->in_pool) {
1046 if (raw->in_pool_active) {
1047 gst_buffer_pool_set_active (raw->in_pool, FALSE);
1048 }
1049 raw->in_pool_active = FALSE;
1050 gst_object_unref (raw->in_pool);
1051 raw->in_pool = NULL;
1052 }
1053
1054 if (raw->cmd_pool)
1055 gst_object_unref (raw->cmd_pool);
1056 raw->cmd_pool = NULL;
1057
1058 if (!gst_vulkan_trash_list_wait (raw->trash_list, -1))
1059 GST_WARNING_OBJECT (raw->upload,
1060 "Failed to wait for all fences to complete " "before shutting down");
1061 gst_object_unref (raw->trash_list);
1062 raw->trash_list = NULL;
1063
1064 g_free (impl);
1065 }
1066
1067 static GstStaticCaps _raw_to_image_in_templ = GST_STATIC_CAPS ("video/x-raw");
1068 static GstStaticCaps _raw_to_image_out_templ =
1069 GST_STATIC_CAPS ("video/x-raw(" GST_CAPS_FEATURE_MEMORY_VULKAN_IMAGE ")");
1070
1071 static const struct UploadMethod raw_to_image_upload = {
1072 "RawToVulkanImage",
1073 &_raw_to_image_in_templ,
1074 &_raw_to_image_out_templ,
1075 _raw_to_image_new_impl,
1076 _raw_to_image_transform_caps,
1077 _raw_to_image_set_caps,
1078 _raw_to_image_propose_allocation,
1079 _raw_to_image_perform,
1080 _raw_to_image_free,
1081 };
1082
1083 static const struct UploadMethod *upload_methods[] = {
1084 &buffer_upload,
1085 &raw_to_buffer_upload,
1086 &raw_to_image_upload,
1087 &buffer_to_image_upload,
1088 };
1089
1090 static GstCaps *
_get_input_template_caps(void)1091 _get_input_template_caps (void)
1092 {
1093 GstCaps *ret = NULL;
1094 gint i;
1095
1096 /* FIXME: cache this and invalidate on changes to upload_methods */
1097 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1098 GstCaps *template = gst_static_caps_get (upload_methods[i]->in_template);
1099 ret = ret == NULL ? template : gst_caps_merge (ret, template);
1100 }
1101
1102 ret = gst_caps_simplify (ret);
1103
1104 return ret;
1105 }
1106
1107 static GstCaps *
_get_output_template_caps(void)1108 _get_output_template_caps (void)
1109 {
1110 GstCaps *ret = NULL;
1111 gint i;
1112
1113 /* FIXME: cache this and invalidate on changes to upload_methods */
1114 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1115 GstCaps *template = gst_static_caps_get (upload_methods[i]->out_template);
1116 ret = ret == NULL ? template : gst_caps_merge (ret, template);
1117 }
1118
1119 ret = gst_caps_simplify (ret);
1120
1121 return ret;
1122 }
1123
1124 static void gst_vulkan_upload_finalize (GObject * object);
1125 static void gst_vulkan_upload_set_property (GObject * object, guint prop_id,
1126 const GValue * value, GParamSpec * param_spec);
1127 static void gst_vulkan_upload_get_property (GObject * object, guint prop_id,
1128 GValue * value, GParamSpec * param_spec);
1129
1130 static gboolean gst_vulkan_upload_query (GstBaseTransform * bt,
1131 GstPadDirection direction, GstQuery * query);
1132 static void gst_vulkan_upload_set_context (GstElement * element,
1133 GstContext * context);
1134 static GstStateChangeReturn gst_vulkan_upload_change_state (GstElement *
1135 element, GstStateChange transition);
1136
1137 static gboolean gst_vulkan_upload_set_caps (GstBaseTransform * bt,
1138 GstCaps * in_caps, GstCaps * out_caps);
1139 static GstCaps *gst_vulkan_upload_transform_caps (GstBaseTransform * bt,
1140 GstPadDirection direction, GstCaps * caps, GstCaps * filter);
1141 static gboolean gst_vulkan_upload_propose_allocation (GstBaseTransform * bt,
1142 GstQuery * decide_query, GstQuery * query);
1143 static gboolean gst_vulkan_upload_decide_allocation (GstBaseTransform * bt,
1144 GstQuery * query);
1145 static GstFlowReturn gst_vulkan_upload_transform (GstBaseTransform * bt,
1146 GstBuffer * inbuf, GstBuffer * outbuf);
1147 static GstFlowReturn gst_vulkan_upload_prepare_output_buffer (GstBaseTransform *
1148 bt, GstBuffer * inbuf, GstBuffer ** outbuf);
1149
1150 enum
1151 {
1152 PROP_0,
1153 };
1154
1155 enum
1156 {
1157 SIGNAL_0,
1158 LAST_SIGNAL
1159 };
1160
1161 /* static guint gst_vulkan_upload_signals[LAST_SIGNAL] = { 0 }; */
1162
1163 #define gst_vulkan_upload_parent_class parent_class
1164 G_DEFINE_TYPE_WITH_CODE (GstVulkanUpload, gst_vulkan_upload,
1165 GST_TYPE_BASE_TRANSFORM, GST_DEBUG_CATEGORY_INIT (gst_debug_vulkan_upload,
1166 "vulkanupload", 0, "Vulkan Uploader"));
1167 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (vulkanupload, "vulkanupload",
1168 GST_RANK_NONE, GST_TYPE_VULKAN_UPLOAD, vulkan_element_init (plugin));
1169
1170 static void
gst_vulkan_upload_class_init(GstVulkanUploadClass * klass)1171 gst_vulkan_upload_class_init (GstVulkanUploadClass * klass)
1172 {
1173 GObjectClass *gobject_class;
1174 GstElementClass *gstelement_class;
1175 GstBaseTransformClass *gstbasetransform_class;
1176
1177 gobject_class = (GObjectClass *) klass;
1178 gstelement_class = (GstElementClass *) klass;
1179 gstbasetransform_class = (GstBaseTransformClass *) klass;
1180
1181 gobject_class->set_property = gst_vulkan_upload_set_property;
1182 gobject_class->get_property = gst_vulkan_upload_get_property;
1183
1184 gst_element_class_set_metadata (gstelement_class, "Vulkan Uploader",
1185 "Filter/Video", "A Vulkan data uploader",
1186 "Matthew Waters <matthew@centricular.com>");
1187
1188 {
1189 GstCaps *caps;
1190
1191 caps = _get_input_template_caps ();
1192 gst_element_class_add_pad_template (gstelement_class,
1193 gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps));
1194 gst_caps_unref (caps);
1195
1196 caps = _get_output_template_caps ();
1197 gst_element_class_add_pad_template (gstelement_class,
1198 gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, caps));
1199 gst_caps_unref (caps);
1200 }
1201
1202 gobject_class->finalize = gst_vulkan_upload_finalize;
1203
1204 gstelement_class->change_state = gst_vulkan_upload_change_state;
1205 gstelement_class->set_context = gst_vulkan_upload_set_context;
1206 gstbasetransform_class->query = GST_DEBUG_FUNCPTR (gst_vulkan_upload_query);
1207 gstbasetransform_class->set_caps = gst_vulkan_upload_set_caps;
1208 gstbasetransform_class->transform_caps = gst_vulkan_upload_transform_caps;
1209 gstbasetransform_class->propose_allocation =
1210 gst_vulkan_upload_propose_allocation;
1211 gstbasetransform_class->decide_allocation =
1212 gst_vulkan_upload_decide_allocation;
1213 gstbasetransform_class->transform = gst_vulkan_upload_transform;
1214 gstbasetransform_class->prepare_output_buffer =
1215 gst_vulkan_upload_prepare_output_buffer;
1216 }
1217
1218 static void
gst_vulkan_upload_init(GstVulkanUpload * vk_upload)1219 gst_vulkan_upload_init (GstVulkanUpload * vk_upload)
1220 {
1221 guint i, n;
1222
1223 n = G_N_ELEMENTS (upload_methods);
1224 vk_upload->upload_impls = g_malloc (sizeof (gpointer) * n);
1225 for (i = 0; i < n; i++) {
1226 vk_upload->upload_impls[i] = upload_methods[i]->new_impl (vk_upload);
1227 }
1228 }
1229
1230 static void
gst_vulkan_upload_finalize(GObject * object)1231 gst_vulkan_upload_finalize (GObject * object)
1232 {
1233 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (object);
1234 guint i;
1235
1236 gst_caps_replace (&vk_upload->in_caps, NULL);
1237 gst_caps_replace (&vk_upload->out_caps, NULL);
1238
1239 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1240 upload_methods[i]->free (vk_upload->upload_impls[i]);
1241 }
1242 g_free (vk_upload->upload_impls);
1243 vk_upload->upload_impls = NULL;
1244
1245 G_OBJECT_CLASS (parent_class)->finalize (object);
1246 }
1247
1248 static void
gst_vulkan_upload_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1249 gst_vulkan_upload_set_property (GObject * object, guint prop_id,
1250 const GValue * value, GParamSpec * pspec)
1251 {
1252 // GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (object);
1253
1254 switch (prop_id) {
1255 default:
1256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1257 break;
1258 }
1259 }
1260
1261 static void
gst_vulkan_upload_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1262 gst_vulkan_upload_get_property (GObject * object, guint prop_id,
1263 GValue * value, GParamSpec * pspec)
1264 {
1265 // GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (object);
1266
1267 switch (prop_id) {
1268 default:
1269 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1270 break;
1271 }
1272 }
1273
1274 static gboolean
gst_vulkan_upload_query(GstBaseTransform * bt,GstPadDirection direction,GstQuery * query)1275 gst_vulkan_upload_query (GstBaseTransform * bt, GstPadDirection direction,
1276 GstQuery * query)
1277 {
1278 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (bt);
1279
1280 switch (GST_QUERY_TYPE (query)) {
1281 case GST_QUERY_CONTEXT:{
1282 if (gst_vulkan_handle_context_query (GST_ELEMENT (vk_upload), query,
1283 NULL, vk_upload->instance, vk_upload->device))
1284 return TRUE;
1285
1286 if (gst_vulkan_queue_handle_context_query (GST_ELEMENT (vk_upload), query,
1287 vk_upload->queue))
1288 return TRUE;
1289
1290 break;
1291 }
1292 default:
1293 break;
1294 }
1295
1296 return GST_BASE_TRANSFORM_CLASS (parent_class)->query (bt, direction, query);
1297 }
1298
1299 static void
gst_vulkan_upload_set_context(GstElement * element,GstContext * context)1300 gst_vulkan_upload_set_context (GstElement * element, GstContext * context)
1301 {
1302 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (element);
1303
1304 gst_vulkan_handle_set_context (element, context, NULL, &vk_upload->instance);
1305
1306 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
1307 }
1308
1309 struct choose_data
1310 {
1311 GstVulkanUpload *upload;
1312 GstVulkanQueue *queue;
1313 };
1314
1315 static gboolean
_choose_queue(GstVulkanDevice * device,GstVulkanQueue * queue,struct choose_data * data)1316 _choose_queue (GstVulkanDevice * device, GstVulkanQueue * queue,
1317 struct choose_data *data)
1318 {
1319 guint flags =
1320 device->physical_device->queue_family_props[queue->family].queueFlags;
1321
1322 if ((flags & VK_QUEUE_GRAPHICS_BIT) != 0) {
1323 if (data->queue)
1324 gst_object_unref (data->queue);
1325 data->queue = gst_object_ref (queue);
1326 return FALSE;
1327 }
1328
1329 return TRUE;
1330 }
1331
1332 static GstVulkanQueue *
_find_graphics_queue(GstVulkanUpload * upload)1333 _find_graphics_queue (GstVulkanUpload * upload)
1334 {
1335 struct choose_data data;
1336
1337 data.upload = upload;
1338 data.queue = NULL;
1339
1340 gst_vulkan_device_foreach_queue (upload->device,
1341 (GstVulkanDeviceForEachQueueFunc) _choose_queue, &data);
1342
1343 return data.queue;
1344 }
1345
1346 static GstStateChangeReturn
gst_vulkan_upload_change_state(GstElement * element,GstStateChange transition)1347 gst_vulkan_upload_change_state (GstElement * element, GstStateChange transition)
1348 {
1349 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (element);
1350 GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1351
1352 GST_DEBUG ("changing state: %s => %s",
1353 gst_element_state_get_name (GST_STATE_TRANSITION_CURRENT (transition)),
1354 gst_element_state_get_name (GST_STATE_TRANSITION_NEXT (transition)));
1355
1356 switch (transition) {
1357 case GST_STATE_CHANGE_NULL_TO_READY:
1358 break;
1359 case GST_STATE_CHANGE_READY_TO_PAUSED:
1360 if (!gst_vulkan_ensure_element_data (element, NULL, &vk_upload->instance)) {
1361 GST_ELEMENT_ERROR (vk_upload, RESOURCE, NOT_FOUND,
1362 ("Failed to retrieve vulkan instance"), (NULL));
1363 return GST_STATE_CHANGE_FAILURE;
1364 }
1365 if (!gst_vulkan_device_run_context_query (GST_ELEMENT (vk_upload),
1366 &vk_upload->device)) {
1367 GError *error = NULL;
1368 GST_DEBUG_OBJECT (vk_upload, "No device retrieved from peer elements");
1369 if (!(vk_upload->device =
1370 gst_vulkan_instance_create_device (vk_upload->instance,
1371 &error))) {
1372 GST_ELEMENT_ERROR (vk_upload, RESOURCE, NOT_FOUND,
1373 ("Failed to create vulkan device"), ("%s",
1374 error ? error->message : ""));
1375 g_clear_error (&error);
1376 return GST_STATE_CHANGE_FAILURE;
1377 }
1378 }
1379
1380 if (!gst_vulkan_queue_run_context_query (GST_ELEMENT (vk_upload),
1381 &vk_upload->queue)) {
1382 GST_DEBUG_OBJECT (vk_upload, "No queue retrieved from peer elements");
1383 vk_upload->queue = _find_graphics_queue (vk_upload);
1384 }
1385 if (!vk_upload->queue) {
1386 GST_ELEMENT_ERROR (vk_upload, RESOURCE, NOT_FOUND,
1387 ("Failed to create/retrieve vulkan queue"), (NULL));
1388 return GST_STATE_CHANGE_FAILURE;
1389 }
1390 break;
1391 case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
1392 break;
1393 default:
1394 break;
1395 }
1396
1397 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1398 if (ret == GST_STATE_CHANGE_FAILURE)
1399 return ret;
1400
1401 switch (transition) {
1402 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
1403 break;
1404 case GST_STATE_CHANGE_PAUSED_TO_READY:
1405 if (vk_upload->queue)
1406 gst_object_unref (vk_upload->queue);
1407 vk_upload->queue = NULL;
1408 if (vk_upload->device)
1409 gst_object_unref (vk_upload->device);
1410 vk_upload->device = NULL;
1411 if (vk_upload->instance)
1412 gst_object_unref (vk_upload->instance);
1413 vk_upload->instance = NULL;
1414 break;
1415 case GST_STATE_CHANGE_READY_TO_NULL:
1416 break;
1417 default:
1418 break;
1419 }
1420
1421 return ret;
1422 }
1423
1424 static GstCaps *
gst_vulkan_upload_transform_caps(GstBaseTransform * bt,GstPadDirection direction,GstCaps * caps,GstCaps * filter)1425 gst_vulkan_upload_transform_caps (GstBaseTransform * bt,
1426 GstPadDirection direction, GstCaps * caps, GstCaps * filter)
1427 {
1428 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (bt);
1429 GstCaps *result, *tmp;
1430 gint i;
1431
1432 tmp = gst_caps_new_empty ();
1433
1434 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1435 GstCaps *tmp2;
1436 GstCaps *templ;
1437
1438 if (direction == GST_PAD_SINK) {
1439 templ = gst_static_caps_get (upload_methods[i]->in_template);
1440 } else {
1441 templ = gst_static_caps_get (upload_methods[i]->out_template);
1442 }
1443 if (!gst_caps_can_intersect (caps, templ)) {
1444 gst_caps_unref (templ);
1445 continue;
1446 }
1447 gst_caps_unref (templ);
1448
1449 tmp2 = upload_methods[i]->transform_caps (vk_upload->upload_impls[i],
1450 direction, caps);
1451
1452 if (tmp2)
1453 tmp = gst_caps_merge (tmp, tmp2);
1454 }
1455
1456 if (filter) {
1457 result = gst_caps_intersect_full (filter, tmp, GST_CAPS_INTERSECT_FIRST);
1458 gst_caps_unref (tmp);
1459 } else {
1460 result = tmp;
1461 }
1462
1463 return result;
1464 }
1465
1466 static gboolean
gst_vulkan_upload_set_caps(GstBaseTransform * bt,GstCaps * in_caps,GstCaps * out_caps)1467 gst_vulkan_upload_set_caps (GstBaseTransform * bt, GstCaps * in_caps,
1468 GstCaps * out_caps)
1469 {
1470 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (bt);
1471 gboolean found_method = FALSE;
1472 guint i;
1473
1474 gst_caps_replace (&vk_upload->in_caps, in_caps);
1475 gst_caps_replace (&vk_upload->out_caps, out_caps);
1476
1477 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1478 GstCaps *templ;
1479
1480 templ = gst_static_caps_get (upload_methods[i]->in_template);
1481 if (!gst_caps_can_intersect (in_caps, templ)) {
1482 gst_caps_unref (templ);
1483 continue;
1484 }
1485 gst_caps_unref (templ);
1486
1487 templ = gst_static_caps_get (upload_methods[i]->out_template);
1488 if (!gst_caps_can_intersect (out_caps, templ)) {
1489 gst_caps_unref (templ);
1490 continue;
1491 }
1492 gst_caps_unref (templ);
1493
1494 if (!upload_methods[i]->set_caps (vk_upload->upload_impls[i], in_caps,
1495 out_caps))
1496 continue;
1497
1498 GST_LOG_OBJECT (bt, "uploader %s accepted caps in: %" GST_PTR_FORMAT
1499 " out: %" GST_PTR_FORMAT, upload_methods[i]->name, in_caps, out_caps);
1500
1501 vk_upload->current_impl = i;
1502 found_method = TRUE;
1503 break;
1504 }
1505
1506 GST_DEBUG_OBJECT (bt,
1507 "set caps in: %" GST_PTR_FORMAT " out: %" GST_PTR_FORMAT, in_caps,
1508 out_caps);
1509
1510 return found_method;
1511 }
1512
1513 static gboolean
gst_vulkan_upload_propose_allocation(GstBaseTransform * bt,GstQuery * decide_query,GstQuery * query)1514 gst_vulkan_upload_propose_allocation (GstBaseTransform * bt,
1515 GstQuery * decide_query, GstQuery * query)
1516 {
1517 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (bt);
1518 guint i;
1519
1520 for (i = 0; i < G_N_ELEMENTS (upload_methods); i++) {
1521 GstCaps *templ;
1522
1523 templ = gst_static_caps_get (upload_methods[i]->in_template);
1524 if (!gst_caps_can_intersect (vk_upload->in_caps, templ)) {
1525 gst_caps_unref (templ);
1526 continue;
1527 }
1528 gst_caps_unref (templ);
1529
1530 templ = gst_static_caps_get (upload_methods[i]->out_template);
1531 if (!gst_caps_can_intersect (vk_upload->out_caps, templ)) {
1532 gst_caps_unref (templ);
1533 continue;
1534 }
1535 gst_caps_unref (templ);
1536
1537 upload_methods[i]->propose_allocation (vk_upload->upload_impls[i],
1538 decide_query, query);
1539 }
1540
1541 return TRUE;
1542 }
1543
1544 static gboolean
gst_vulkan_upload_decide_allocation(GstBaseTransform * bt,GstQuery * query)1545 gst_vulkan_upload_decide_allocation (GstBaseTransform * bt, GstQuery * query)
1546 {
1547 return TRUE;
1548 }
1549
1550 static gboolean
_upload_find_method(GstVulkanUpload * vk_upload)1551 _upload_find_method (GstVulkanUpload * vk_upload)
1552 {
1553 vk_upload->current_impl++;
1554
1555 if (vk_upload->current_impl >= G_N_ELEMENTS (upload_methods))
1556 return FALSE;
1557
1558 GST_DEBUG_OBJECT (vk_upload, "attempting upload with uploader %s",
1559 upload_methods[vk_upload->current_impl]->name);
1560
1561 return TRUE;
1562 }
1563
1564 static GstFlowReturn
gst_vulkan_upload_prepare_output_buffer(GstBaseTransform * bt,GstBuffer * inbuf,GstBuffer ** outbuf)1565 gst_vulkan_upload_prepare_output_buffer (GstBaseTransform * bt,
1566 GstBuffer * inbuf, GstBuffer ** outbuf)
1567 {
1568 GstBaseTransformClass *bclass = GST_BASE_TRANSFORM_GET_CLASS (bt);
1569 GstVulkanUpload *vk_upload = GST_VULKAN_UPLOAD (bt);
1570 GstFlowReturn ret;
1571
1572 restart:
1573 {
1574 gpointer method_impl;
1575 const struct UploadMethod *method;
1576
1577 method = upload_methods[vk_upload->current_impl];
1578 method_impl = vk_upload->upload_impls[vk_upload->current_impl];
1579
1580 ret = method->perform (method_impl, inbuf, outbuf);
1581 if (ret != GST_FLOW_OK) {
1582 next_method:
1583 if (!_upload_find_method (vk_upload)) {
1584 GST_ELEMENT_ERROR (bt, RESOURCE, NOT_FOUND,
1585 ("Could not find suitable uploader"), (NULL));
1586 return GST_FLOW_ERROR;
1587 }
1588
1589 method = upload_methods[vk_upload->current_impl];
1590 method_impl = vk_upload->upload_impls[vk_upload->current_impl];
1591 if (!method->set_caps (method_impl, vk_upload->in_caps,
1592 vk_upload->out_caps))
1593 /* try the next method */
1594 goto next_method;
1595
1596 /* try the uploading with the next method */
1597 goto restart;
1598 }
1599 }
1600
1601 if (ret == GST_FLOW_OK) {
1602 /* basetransform doesn't unref if they're the same */
1603 if (inbuf != *outbuf)
1604 bclass->copy_metadata (bt, inbuf, *outbuf);
1605 }
1606
1607 return ret;
1608 }
1609
1610 static GstFlowReturn
gst_vulkan_upload_transform(GstBaseTransform * bt,GstBuffer * inbuf,GstBuffer * outbuf)1611 gst_vulkan_upload_transform (GstBaseTransform * bt, GstBuffer * inbuf,
1612 GstBuffer * outbuf)
1613 {
1614 return GST_FLOW_OK;
1615 }
1616