cuda: Hide memory copy util function
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-bad / gst-libs / gst / cuda / gstcudautils.c
1 /* GStreamer
2  * Copyright (C) <2018-2019> Seungha Yang <seungha.yang@navercorp.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstcudautils.h"
25 #include "gstcudacontext.h"
26 #include "gstcuda-private.h"
27
28 #ifdef HAVE_NVCODEC_GST_GL
29 #include <gst/gl/gl.h>
30 #include <gst/gl/gstglfuncs.h>
31 #endif
32
33 #ifdef GST_CUDA_HAS_D3D
34 #include <gst/d3d11/gstd3d11.h>
35 #endif
36
37 #ifdef HAVE_NVCODEC_NVMM
38 #include "gstcudanvmm.h"
39 #endif
40
41 #include "gstcudamemory.h"
42
43 GST_DEBUG_CATEGORY_STATIC (gst_cuda_utils_debug);
44 #define GST_CAT_DEFAULT gst_cuda_utils_debug
45 GST_DEBUG_CATEGORY_STATIC (GST_CAT_CONTEXT);
46
47 static void
48 _init_debug (void)
49 {
50   static gsize once_init = 0;
51
52   if (g_once_init_enter (&once_init)) {
53
54     GST_DEBUG_CATEGORY_INIT (gst_cuda_utils_debug, "cudautils", 0,
55         "CUDA utils");
56     GST_DEBUG_CATEGORY_GET (GST_CAT_CONTEXT, "GST_CONTEXT");
57     g_once_init_leave (&once_init, 1);
58   }
59 }
60
61 static gboolean
62 pad_query (const GValue * item, GValue * value, gpointer user_data)
63 {
64   GstPad *pad = g_value_get_object (item);
65   GstQuery *query = user_data;
66   gboolean res;
67
68   res = gst_pad_peer_query (pad, query);
69
70   if (res) {
71     g_value_set_boolean (value, TRUE);
72     return FALSE;
73   }
74
75   GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, pad, "pad peer query failed");
76   return TRUE;
77 }
78
79 static gboolean
80 run_query (GstElement * element, GstQuery * query, GstPadDirection direction)
81 {
82   GstIterator *it;
83   GstIteratorFoldFunction func = pad_query;
84   GValue res = { 0 };
85
86   g_value_init (&res, G_TYPE_BOOLEAN);
87   g_value_set_boolean (&res, FALSE);
88
89   /* Ask neighbor */
90   if (direction == GST_PAD_SRC)
91     it = gst_element_iterate_src_pads (element);
92   else
93     it = gst_element_iterate_sink_pads (element);
94
95   while (gst_iterator_fold (it, func, &res, query) == GST_ITERATOR_RESYNC)
96     gst_iterator_resync (it);
97
98   gst_iterator_free (it);
99
100   return g_value_get_boolean (&res);
101 }
102
103 static void
104 find_cuda_context (GstElement * element, GstCudaContext ** cuda_ctx)
105 {
106   GstQuery *query;
107   GstContext *ctxt;
108
109   /*  1) Query downstream with GST_QUERY_CONTEXT for the context and
110    *      check if upstream already has a context of the specific type
111    *  2) Query upstream as above.
112    */
113   query = gst_query_new_context (GST_CUDA_CONTEXT_TYPE);
114   if (run_query (element, query, GST_PAD_SRC)) {
115     gst_query_parse_context (query, &ctxt);
116     if (ctxt) {
117       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
118           "found context (%p) in downstream query", ctxt);
119       gst_element_set_context (element, ctxt);
120     }
121   }
122
123   /* although we found cuda context above, the element does not want
124    * to use the context. Then try to find from the other direction */
125   if (*cuda_ctx == NULL && run_query (element, query, GST_PAD_SINK)) {
126     gst_query_parse_context (query, &ctxt);
127     if (ctxt) {
128       GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
129           "found context (%p) in upstream query", ctxt);
130       gst_element_set_context (element, ctxt);
131     }
132   }
133
134   if (*cuda_ctx == NULL) {
135     /* 3) Post a GST_MESSAGE_NEED_CONTEXT message on the bus with
136      *    the required context type and afterwards check if a
137      *    usable context was set now. The message could
138      *    be handled by the parent bins of the element and the
139      *    application.
140      */
141     GstMessage *msg;
142
143     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
144         "posting need context message");
145     msg = gst_message_new_need_context (GST_OBJECT_CAST (element),
146         GST_CUDA_CONTEXT_TYPE);
147     gst_element_post_message (element, msg);
148   }
149
150   /*
151    * Whomever responds to the need-context message performs a
152    * GstElement::set_context() with the required context in which the element
153    * is required to update the cuda_ctx or call gst_cuda_handle_set_context().
154    */
155
156   gst_query_unref (query);
157 }
158
159 static void
160 context_set_cuda_context (GstContext * context, GstCudaContext * cuda_ctx)
161 {
162   GstStructure *s;
163   guint device_id;
164
165   g_return_if_fail (context != NULL);
166
167   g_object_get (G_OBJECT (cuda_ctx), "cuda-device-id", &device_id, NULL);
168
169   GST_CAT_LOG (GST_CAT_CONTEXT,
170       "setting GstCudaContext(%" GST_PTR_FORMAT
171       ") with cuda-device-id %d on context(%" GST_PTR_FORMAT ")",
172       cuda_ctx, device_id, context);
173
174   s = gst_context_writable_structure (context);
175   gst_structure_set (s, GST_CUDA_CONTEXT_TYPE, GST_TYPE_CUDA_CONTEXT,
176       cuda_ctx, "cuda-device-id", G_TYPE_UINT, device_id, NULL);
177 }
178
179 /**
180  * gst_cuda_ensure_element_context:
181  * @element: the #GstElement running the query
182  * @device_id: preferred device-id, pass device_id >=0 when
183  *             the device_id explicitly required. Otherwise, set -1.
184  * @cuda_ctx: (inout): the resulting #GstCudaContext
185  *
186  * Perform the steps necessary for retrieving a #GstCudaContext from the
187  * surrounding elements or from the application using the #GstContext mechanism.
188  *
189  * If the content of @cuda_ctx is not %NULL, then no #GstContext query is
190  * necessary for #GstCudaContext.
191  *
192  * Returns: whether a #GstCudaContext exists in @cuda_ctx
193  *
194  * Since: 1.22
195  */
196 gboolean
197 gst_cuda_ensure_element_context (GstElement * element, gint device_id,
198     GstCudaContext ** cuda_ctx)
199 {
200   guint target_device_id = 0;
201   gboolean ret = TRUE;
202   static GRecMutex lock;
203   static gsize init_lock_once = 0;
204
205   g_return_val_if_fail (element != NULL, FALSE);
206   g_return_val_if_fail (cuda_ctx != NULL, FALSE);
207
208   _init_debug ();
209   if (g_once_init_enter (&init_lock_once)) {
210     g_rec_mutex_init (&lock);
211     g_once_init_leave (&init_lock_once, 1);
212   }
213
214   g_rec_mutex_lock (&lock);
215
216   if (*cuda_ctx)
217     goto out;
218
219   find_cuda_context (element, cuda_ctx);
220   if (*cuda_ctx)
221     goto out;
222
223   if (device_id > 0)
224     target_device_id = device_id;
225
226   /* No available CUDA context in pipeline, create new one here */
227   *cuda_ctx = gst_cuda_context_new (target_device_id);
228
229   if (*cuda_ctx == NULL) {
230     GST_CAT_ERROR_OBJECT (GST_CAT_CONTEXT, element,
231         "Failed to create CUDA context with device-id %d", device_id);
232     ret = FALSE;
233   } else {
234     GstContext *context;
235     GstMessage *msg;
236
237     /* Propagate new CUDA context */
238
239     context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
240     context_set_cuda_context (context, *cuda_ctx);
241
242     gst_element_set_context (element, context);
243
244     GST_CAT_INFO_OBJECT (GST_CAT_CONTEXT, element,
245         "posting have context (%p) message with CUDA context (%p)",
246         context, *cuda_ctx);
247     msg = gst_message_new_have_context (GST_OBJECT_CAST (element), context);
248     gst_element_post_message (GST_ELEMENT_CAST (element), msg);
249   }
250
251 out:
252   g_rec_mutex_unlock (&lock);
253
254   return ret;
255 }
256
257 /**
258  * gst_cuda_handle_set_context:
259  * @element: a #GstElement
260  * @context: a #GstContext
261  * @device_id: preferred device-id, pass device_id >=0 when
262  *             the device_id explicitly required. Otherwise, set -1.
263  * @cuda_ctx: (inout) (transfer full): location of a #GstCudaContext
264  *
265  * Helper function for implementing #GstElementClass.set_context() in
266  * CUDA capable elements.
267  *
268  * Retrieves the #GstCudaContext in @context and places the result in @cuda_ctx.
269  *
270  * Returns: whether the @cuda_ctx could be set successfully
271  *
272  * Since: 1.22
273  */
274 gboolean
275 gst_cuda_handle_set_context (GstElement * element,
276     GstContext * context, gint device_id, GstCudaContext ** cuda_ctx)
277 {
278   const gchar *context_type;
279
280   g_return_val_if_fail (element != NULL, FALSE);
281   g_return_val_if_fail (cuda_ctx != NULL, FALSE);
282
283   _init_debug ();
284
285   if (!context)
286     return FALSE;
287
288   context_type = gst_context_get_context_type (context);
289   if (g_strcmp0 (context_type, GST_CUDA_CONTEXT_TYPE) == 0) {
290     const GstStructure *str;
291     GstCudaContext *other_ctx = NULL;
292     guint other_device_id = 0;
293
294     /* If we had context already, will not replace it */
295     if (*cuda_ctx)
296       return TRUE;
297
298     str = gst_context_get_structure (context);
299     if (gst_structure_get (str, GST_CUDA_CONTEXT_TYPE, GST_TYPE_CUDA_CONTEXT,
300             &other_ctx, NULL)) {
301       g_object_get (other_ctx, "cuda-device-id", &other_device_id, NULL);
302
303       if (device_id == -1 || other_device_id == device_id) {
304         GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element, "Found CUDA context");
305         *cuda_ctx = other_ctx;
306
307         return TRUE;
308       }
309
310       gst_object_unref (other_ctx);
311     }
312   }
313
314   return FALSE;
315 }
316
317 /**
318  * gst_cuda_handle_context_query:
319  * @element: a #GstElement
320  * @query: a #GstQuery of type %GST_QUERY_CONTEXT
321  * @cuda_ctx: (transfer none) (nullable): a #GstCudaContext
322  *
323  * Returns: Whether the @query was successfully responded to from the passed
324  *          @context.
325  *
326  * Since: 1.22
327  */
328 gboolean
329 gst_cuda_handle_context_query (GstElement * element,
330     GstQuery * query, GstCudaContext * cuda_ctx)
331 {
332   const gchar *context_type;
333   GstContext *context, *old_context;
334
335   g_return_val_if_fail (GST_IS_ELEMENT (element), FALSE);
336   g_return_val_if_fail (GST_IS_QUERY (query), FALSE);
337   g_return_val_if_fail (cuda_ctx == NULL
338       || GST_IS_CUDA_CONTEXT (cuda_ctx), FALSE);
339
340   _init_debug ();
341
342   GST_CAT_LOG_OBJECT (GST_CAT_CONTEXT, element,
343       "handle context query %" GST_PTR_FORMAT, query);
344   gst_query_parse_context_type (query, &context_type);
345
346   if (cuda_ctx && g_strcmp0 (context_type, GST_CUDA_CONTEXT_TYPE) == 0) {
347     gst_query_parse_context (query, &old_context);
348
349     if (old_context)
350       context = gst_context_copy (old_context);
351     else
352       context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
353
354     context_set_cuda_context (context, cuda_ctx);
355     gst_query_set_context (query, context);
356     gst_context_unref (context);
357     GST_CAT_DEBUG_OBJECT (GST_CAT_CONTEXT, element,
358         "successfully set %" GST_PTR_FORMAT " on %" GST_PTR_FORMAT, cuda_ctx,
359         query);
360
361     return TRUE;
362   }
363
364   return FALSE;
365 }
366
367 /**
368  * gst_context_new_cuda_context:
369  * @cuda_ctx: (transfer none): a #GstCudaContext
370  *
371  * Returns: (transfer full): a new #GstContext embedding the @cuda_ctx
372  *
373  * Since: 1.22
374  */
375 GstContext *
376 gst_context_new_cuda_context (GstCudaContext * cuda_ctx)
377 {
378   GstContext *context;
379
380   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (cuda_ctx), NULL);
381
382   _init_debug ();
383
384   context = gst_context_new (GST_CUDA_CONTEXT_TYPE, TRUE);
385   context_set_cuda_context (context, cuda_ctx);
386
387   return context;
388 }
389
390 static const gchar *gst_cuda_quark_strings[] =
391     { "GstCudaQuarkGraphicsResource" };
392
393 static GQuark gst_cuda_quark_table[GST_CUDA_QUARK_MAX];
394
395 static void
396 init_cuda_quark_once (void)
397 {
398   static gsize once_init = 0;
399
400   if (g_once_init_enter (&once_init)) {
401     gint i;
402
403     for (i = 0; i < GST_CUDA_QUARK_MAX; i++)
404       gst_cuda_quark_table[i] =
405           g_quark_from_static_string (gst_cuda_quark_strings[i]);
406
407     g_once_init_leave (&once_init, 1);
408   }
409 }
410
411 /**
412  * gst_cuda_quark_from_id: (skip)
413  * @id: a #GstCudaQuarkId
414  *
415  * Returns: the GQuark for given @id or 0 if @id is unknown value
416  *
417  * Since: 1.22
418  */
419 GQuark
420 gst_cuda_quark_from_id (GstCudaQuarkId id)
421 {
422   g_return_val_if_fail (id < GST_CUDA_QUARK_MAX, 0);
423
424   init_cuda_quark_once ();
425   _init_debug ();
426
427   return gst_cuda_quark_table[id];
428 }
429
430 /**
431  * gst_cuda_graphics_resource_new: (skip)
432  * @context: (transfer none): a #GstCudaContext
433  * @graphics_context: (transfer none) (nullable): a graphics API specific context object
434  * @type: a #GstCudaGraphicsResourceType of resource registration
435  *
436  * Create new #GstCudaGraphicsResource with given @context and @type
437  *
438  * Returns: a new #GstCudaGraphicsResource.
439  * Free with gst_cuda_graphics_resource_free
440  *
441  * Since: 1.22
442  */
443 GstCudaGraphicsResource *
444 gst_cuda_graphics_resource_new (GstCudaContext *
445     context, GstObject * graphics_context, GstCudaGraphicsResourceType type)
446 {
447   GstCudaGraphicsResource *resource;
448
449   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), NULL);
450
451   _init_debug ();
452
453   resource = g_new0 (GstCudaGraphicsResource, 1);
454   resource->cuda_context = gst_object_ref (context);
455   if (graphics_context)
456     resource->graphics_context = gst_object_ref (graphics_context);
457
458   return resource;
459 }
460
461 /**
462  * gst_cuda_graphics_resource_register_gl_buffer: (skip)
463  * @resource a #GstCudaGraphicsResource
464  * @buffer: a GL buffer object
465  * @flags: a `CUgraphicsRegisterFlags`
466  *
467  * Register the @buffer for access by CUDA.
468  * Must be called from the gl context thread with current cuda context was
469  * pushed on the current thread
470  *
471  * Returns: whether @buffer was registered or not
472  *
473  * Since: 1.22
474  */
475 gboolean
476 gst_cuda_graphics_resource_register_gl_buffer (GstCudaGraphicsResource *
477     resource, guint buffer, CUgraphicsRegisterFlags flags)
478 {
479   CUresult cuda_ret;
480
481   g_return_val_if_fail (resource != NULL, FALSE);
482   g_return_val_if_fail (resource->registered == FALSE, FALSE);
483
484   _init_debug ();
485
486   cuda_ret = CuGraphicsGLRegisterBuffer (&resource->resource, buffer, flags);
487
488   if (!gst_cuda_result (cuda_ret))
489     return FALSE;
490
491   resource->registered = TRUE;
492   resource->type = GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER;
493   resource->flags = flags;
494
495   return TRUE;
496 }
497
498 #ifdef G_OS_WIN32
499 /**
500  * gst_cuda_graphics_resource_register_d3d11_resource: (skip)
501  * @resource a #GstCudaGraphicsResource
502  * @d3d11_resource: a ID3D11Resource
503  * @flags: a CUgraphicsRegisterFlags
504  *
505  * Register the @d3d11_resource for accessing by CUDA.
506  * Must be called with d3d11 device lock with current cuda context was
507  * pushed on the current thread
508  *
509  * Returns: whether @d3d11_resource was registered or not
510  *
511  * Since: 1.22
512  */
513 gboolean
514 gst_cuda_graphics_resource_register_d3d11_resource (GstCudaGraphicsResource *
515     resource, ID3D11Resource * d3d11_resource, CUgraphicsRegisterFlags flags)
516 {
517   CUresult cuda_ret;
518
519   g_return_val_if_fail (resource != NULL, FALSE);
520   g_return_val_if_fail (resource->registered == FALSE, FALSE);
521
522   _init_debug ();
523
524   cuda_ret = CuGraphicsD3D11RegisterResource (&resource->resource,
525       d3d11_resource, flags);
526
527   if (!gst_cuda_result (cuda_ret))
528     return FALSE;
529
530   resource->registered = TRUE;
531   resource->type = GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE;
532   resource->flags = flags;
533
534   return TRUE;
535 }
536 #endif
537
538 /**
539  * gst_cuda_graphics_resource_unregister: (skip)
540  * @resource: a #GstCudaGraphicsResource
541  *
542  * Unregister previously registered resource.
543  * For GL resource, this method must be called from gl context thread.
544  * Also, current cuda context should be pushed on the current thread
545  * before calling this method.
546  *
547  * Since: 1.22
548  */
549 void
550 gst_cuda_graphics_resource_unregister (GstCudaGraphicsResource * resource)
551 {
552   g_return_if_fail (resource != NULL);
553
554   _init_debug ();
555
556   if (!resource->registered)
557     return;
558
559   gst_cuda_result (CuGraphicsUnregisterResource (resource->resource));
560   resource->resource = NULL;
561   resource->registered = FALSE;
562
563   return;
564 }
565
566 /**
567  * gst_cuda_graphics_resource_map: (skip)
568  * @resource: a #GstCudaGraphicsResource
569  * @stream: a CUstream
570  * @flags: a CUgraphicsMapResourceFlags
571  *
572  * Map previously registered resource with map flags
573  *
574  * Returns: (nullable): the `CUgraphicsResource` if successful or %NULL when failed
575  *
576  * Since: 1.22
577  */
578 CUgraphicsResource
579 gst_cuda_graphics_resource_map (GstCudaGraphicsResource * resource,
580     CUstream stream, CUgraphicsMapResourceFlags flags)
581 {
582   CUresult cuda_ret;
583
584   g_return_val_if_fail (resource != NULL, NULL);
585   g_return_val_if_fail (resource->registered != FALSE, NULL);
586
587   _init_debug ();
588
589   cuda_ret = CuGraphicsResourceSetMapFlags (resource->resource, flags);
590   if (!gst_cuda_result (cuda_ret))
591     return NULL;
592
593   cuda_ret = CuGraphicsMapResources (1, &resource->resource, stream);
594   if (!gst_cuda_result (cuda_ret))
595     return NULL;
596
597   resource->mapped = TRUE;
598
599   return resource->resource;
600 }
601
602 /**
603  * gst_cuda_graphics_resource_unmap: (skip)
604  * @resource: a #GstCudaGraphicsResource
605  * @stream: a `CUstream`
606  *
607  * Unmap previously mapped resource
608  *
609  * Since: 1.22
610  */
611 void
612 gst_cuda_graphics_resource_unmap (GstCudaGraphicsResource * resource,
613     CUstream stream)
614 {
615   g_return_if_fail (resource != NULL);
616   g_return_if_fail (resource->registered != FALSE);
617
618   _init_debug ();
619
620   if (!resource->mapped)
621     return;
622
623   gst_cuda_result (CuGraphicsUnmapResources (1, &resource->resource, stream));
624
625   resource->mapped = FALSE;
626 }
627
628 #ifdef HAVE_NVCODEC_GST_GL
629 static void
630 unregister_resource_from_gl_thread (GstGLContext * gl_context,
631     GstCudaGraphicsResource * resource)
632 {
633   GstCudaContext *cuda_context = resource->cuda_context;
634
635   if (!gst_cuda_context_push (cuda_context)) {
636     GST_WARNING_OBJECT (cuda_context, "failed to push CUDA context");
637     return;
638   }
639
640   gst_cuda_graphics_resource_unregister (resource);
641
642   if (!gst_cuda_context_pop (NULL)) {
643     GST_WARNING_OBJECT (cuda_context, "failed to pop CUDA context");
644   }
645 }
646 #endif
647
648 #ifdef GST_CUDA_HAS_D3D
649 static void
650 unregister_d3d11_resource (GstCudaGraphicsResource * resource)
651 {
652   GstCudaContext *cuda_context = resource->cuda_context;
653   GstD3D11Device *device = GST_D3D11_DEVICE (resource->graphics_context);
654
655   if (!gst_cuda_context_push (cuda_context)) {
656     GST_WARNING_OBJECT (cuda_context, "failed to push CUDA context");
657     return;
658   }
659
660   gst_d3d11_device_lock (device);
661   gst_cuda_graphics_resource_unregister (resource);
662   gst_d3d11_device_unlock (device);
663
664   if (!gst_cuda_context_pop (NULL)) {
665     GST_WARNING_OBJECT (cuda_context, "failed to pop CUDA context");
666   }
667 }
668 #endif
669
670 /**
671  * gst_cuda_graphics_resource_free: (skip)
672  * @resource: a #GstCudaGraphicsResource
673  *
674  * Free @resource
675  *
676  * Since: 1.22
677  */
678 void
679 gst_cuda_graphics_resource_free (GstCudaGraphicsResource * resource)
680 {
681   g_return_if_fail (resource != NULL);
682
683   if (resource->registered) {
684 #ifdef HAVE_NVCODEC_GST_GL
685     if (resource->type == GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER) {
686       gst_gl_context_thread_add ((GstGLContext *) resource->graphics_context,
687           (GstGLContextThreadFunc) unregister_resource_from_gl_thread,
688           resource);
689     } else
690 #endif
691 #ifdef GST_CUDA_HAS_D3D
692     if (resource->type == GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE) {
693       unregister_d3d11_resource (resource);
694     } else
695 #endif
696     {
697       /* FIXME: currently only opengl & d3d11 */
698       g_assert_not_reached ();
699     }
700   }
701
702   gst_object_unref (resource->cuda_context);
703   if (resource->graphics_context)
704     gst_object_unref (resource->graphics_context);
705   g_free (resource);
706 }
707
708 const gchar *
709 gst_cuda_buffer_copy_type_to_string (GstCudaBufferCopyType type)
710 {
711   switch (type) {
712     case GST_CUDA_BUFFER_COPY_SYSTEM:
713       return "SYSTEM";
714     case GST_CUDA_BUFFER_COPY_CUDA:
715       return "CUDA";
716     case GST_CUDA_BUFFER_COPY_GL:
717       return "GL";
718     case GST_CUDA_BUFFER_COPY_D3D11:
719       return "D3D11";
720     case GST_CUDA_BUFFER_COPY_NVMM:
721       return "NVMM";
722     default:
723       g_assert_not_reached ();
724       break;
725   }
726
727   return "UNKNOWN";
728 }
729
730 static gboolean
731 gst_cuda_buffer_fallback_copy (GstBuffer * dst, const GstVideoInfo * dst_info,
732     GstBuffer * src, const GstVideoInfo * src_info)
733 {
734   GstVideoFrame dst_frame, src_frame;
735   guint i, j;
736
737   if (!gst_video_frame_map (&dst_frame, dst_info, dst, GST_MAP_WRITE)) {
738     GST_ERROR ("Failed to map dst buffer");
739     return FALSE;
740   }
741
742   if (!gst_video_frame_map (&src_frame, src_info, src, GST_MAP_READ)) {
743     gst_video_frame_unmap (&dst_frame);
744     GST_ERROR ("Failed to map src buffer");
745     return FALSE;
746   }
747
748   /* src and dst resolutions can be different, pick min value */
749   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&dst_frame); i++) {
750     guint dst_width_in_bytes, src_width_in_bytes;
751     guint dst_height, src_height;
752     guint width_in_bytes, height;
753     guint dst_stride, src_stride;
754     guint8 *dst_data, *src_data;
755
756     dst_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&dst_frame, i) *
757         GST_VIDEO_FRAME_COMP_PSTRIDE (&dst_frame, i);
758     src_width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (&src_frame, i) *
759         GST_VIDEO_FRAME_COMP_PSTRIDE (&src_frame, i);
760
761     width_in_bytes = MIN (dst_width_in_bytes, src_width_in_bytes);
762
763     dst_height = GST_VIDEO_FRAME_COMP_HEIGHT (&dst_frame, i);
764     src_height = GST_VIDEO_FRAME_COMP_HEIGHT (&src_frame, i);
765
766     height = MIN (dst_height, src_height);
767
768     dst_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&dst_frame, i);
769     src_stride = GST_VIDEO_FRAME_PLANE_STRIDE (&src_frame, i);
770
771     dst_data = GST_VIDEO_FRAME_PLANE_DATA (&dst_frame, i);
772     src_data = GST_VIDEO_FRAME_PLANE_DATA (&src_frame, i);
773
774     for (j = 0; j < height; j++) {
775       memcpy (dst_data, src_data, width_in_bytes);
776       dst_data += dst_stride;
777       src_data += src_stride;
778     }
779   }
780
781   gst_video_frame_unmap (&src_frame);
782   gst_video_frame_unmap (&dst_frame);
783
784   return TRUE;
785 }
786
787 static gboolean
788 map_buffer_and_fill_copy2d (GstBuffer * buf, const GstVideoInfo * info,
789     GstCudaBufferCopyType copy_type, GstVideoFrame * frame,
790     GstMapInfo * map_info, gboolean is_src,
791     CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES])
792 {
793   gboolean buffer_mapped = FALSE;
794   guint i;
795
796 #ifdef HAVE_NVCODEC_NVMM
797   if (copy_type == GST_CUDA_BUFFER_COPY_NVMM) {
798     NvBufSurface *surface;
799     NvBufSurfaceParams *surface_params;
800     NvBufSurfacePlaneParams *plane_params;
801
802     if (!gst_buffer_map (buf, map_info, GST_MAP_READ)) {
803       GST_ERROR ("Failed to map input NVMM buffer");
804       memset (map_info, 0, sizeof (GstMapInfo));
805       return FALSE;
806     }
807
808     surface = (NvBufSurface *) map_info->data;
809
810     GST_TRACE ("batch-size %d, num-filled %d, memType %d",
811         surface->batchSize, surface->numFilled, surface->memType);
812
813     surface_params = surface->surfaceList;
814     buffer_mapped = TRUE;
815     if (!surface_params) {
816       GST_ERROR ("NVMM memory doesn't hold buffer");
817       goto error;
818     }
819
820     plane_params = &surface_params->planeParams;
821     if (plane_params->num_planes != GST_VIDEO_INFO_N_PLANES (info)) {
822       GST_ERROR ("num_planes mismatch, %d / %d",
823           plane_params->num_planes, GST_VIDEO_INFO_N_PLANES (info));
824       goto error;
825     }
826
827     switch (surface->memType) {
828         /* TODO: NVBUF_MEM_DEFAULT on jetson is SURFACE_ARRAY */
829       case NVBUF_MEM_DEFAULT:
830       case NVBUF_MEM_CUDA_DEVICE:
831       {
832         for (i = 0; i < plane_params->num_planes; i++) {
833           if (is_src) {
834             copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
835             copy_params[i].srcDevice = (CUdeviceptr)
836                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
837             copy_params[i].srcPitch = plane_params->pitch[i];
838           } else {
839             copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
840             copy_params[i].dstDevice = (CUdeviceptr)
841                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
842             copy_params[i].dstPitch = plane_params->pitch[i];
843           }
844         }
845         break;
846       }
847       case NVBUF_MEM_CUDA_PINNED:
848       {
849         for (i = 0; i < plane_params->num_planes; i++) {
850           if (is_src) {
851             copy_params[i].srcMemoryType = CU_MEMORYTYPE_HOST;
852             copy_params[i].srcHost =
853                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
854             copy_params[i].srcPitch = plane_params->pitch[i];
855           } else {
856             copy_params[i].dstMemoryType = CU_MEMORYTYPE_HOST;
857             copy_params[i].dstHost =
858                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
859             copy_params[i].dstPitch = plane_params->pitch[i];
860           }
861         }
862         break;
863       }
864       case NVBUF_MEM_CUDA_UNIFIED:
865       {
866         for (i = 0; i < plane_params->num_planes; i++) {
867           if (is_src) {
868             copy_params[i].srcMemoryType = CU_MEMORYTYPE_UNIFIED;
869             copy_params[i].srcDevice = (CUdeviceptr)
870                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
871             copy_params[i].srcPitch = plane_params->pitch[i];
872           } else {
873             copy_params[i].dstMemoryType = CU_MEMORYTYPE_UNIFIED;
874             copy_params[i].dstDevice = (CUdeviceptr)
875                 ((guint8 *) surface_params->dataPtr + plane_params->offset[i]);
876             copy_params[i].dstPitch = plane_params->pitch[i];
877           }
878         }
879         break;
880       }
881       default:
882         GST_ERROR ("Unexpected NVMM memory type %d", surface->memType);
883         goto error;
884     }
885
886     for (i = 0; i < plane_params->num_planes; i++) {
887       gsize width_in_bytes, height;
888
889       width_in_bytes = plane_params->width[i] * plane_params->bytesPerPix[i];
890       height = plane_params->height[i];
891
892       if (copy_params[i].WidthInBytes == 0 ||
893           width_in_bytes < copy_params[i].WidthInBytes) {
894         copy_params[i].WidthInBytes = width_in_bytes;
895       }
896
897       if (copy_params[i].Height == 0 || height < copy_params[i].Height) {
898         copy_params[i].Height = height;
899       }
900     }
901   } else
902 #endif
903   {
904     GstMapFlags map_flags;
905
906     if (is_src)
907       map_flags = GST_MAP_READ;
908     else
909       map_flags = GST_MAP_WRITE;
910
911     if (copy_type == GST_CUDA_BUFFER_COPY_CUDA)
912       map_flags |= GST_MAP_CUDA;
913
914     if (!gst_video_frame_map (frame, info, buf, map_flags)) {
915       GST_ERROR ("Failed to map buffer");
916       goto error;
917     }
918
919     for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (frame); i++) {
920       gsize width_in_bytes, height;
921
922       if (is_src) {
923         if (copy_type == GST_CUDA_BUFFER_COPY_CUDA) {
924           copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
925           copy_params[i].srcDevice =
926               (CUdeviceptr) GST_VIDEO_FRAME_PLANE_DATA (frame, i);
927         } else {
928           copy_params[i].srcMemoryType = CU_MEMORYTYPE_HOST;
929           copy_params[i].srcHost = GST_VIDEO_FRAME_PLANE_DATA (frame, i);
930         }
931         copy_params[i].srcPitch = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i);
932       } else {
933         if (copy_type == GST_CUDA_BUFFER_COPY_CUDA) {
934           copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
935           copy_params[i].dstDevice =
936               (CUdeviceptr) GST_VIDEO_FRAME_PLANE_DATA (frame, i);
937         } else {
938           copy_params[i].dstMemoryType = CU_MEMORYTYPE_HOST;
939           copy_params[i].dstHost = GST_VIDEO_FRAME_PLANE_DATA (frame, i);
940         }
941         copy_params[i].dstPitch = GST_VIDEO_FRAME_PLANE_STRIDE (frame, i);
942       }
943
944       width_in_bytes = GST_VIDEO_FRAME_COMP_WIDTH (frame, i) *
945           GST_VIDEO_FRAME_COMP_PSTRIDE (frame, i);
946       height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, i);
947
948       if (copy_params[i].WidthInBytes == 0 ||
949           width_in_bytes < copy_params[i].WidthInBytes) {
950         copy_params[i].WidthInBytes = width_in_bytes;
951       }
952
953       if (copy_params[i].Height == 0 || height < copy_params[i].Height) {
954         copy_params[i].Height = height;
955       }
956     }
957   }
958
959   return TRUE;
960
961 error:
962   if (buffer_mapped) {
963     gst_buffer_unmap (buf, map_info);
964     memset (map_info, 0, sizeof (GstMapInfo));
965   }
966
967   return FALSE;
968 }
969
970 static void
971 unmap_buffer_or_frame (GstBuffer * buf, GstVideoFrame * frame,
972     GstMapInfo * map_info)
973 {
974   if (frame->buffer)
975     gst_video_frame_unmap (frame);
976
977   if (map_info->data)
978     gst_buffer_unmap (buf, map_info);
979 }
980
981 static gboolean
982 gst_cuda_buffer_copy_internal (GstBuffer * dst_buf,
983     GstCudaBufferCopyType dst_type, const GstVideoInfo * dst_info,
984     GstBuffer * src_buf, GstCudaBufferCopyType src_type,
985     const GstVideoInfo * src_info, GstCudaContext * context, CUstream stream)
986 {
987   GstVideoFrame dst_frame, src_frame;
988   gboolean ret = FALSE;
989   GstMapInfo dst_map, src_map;
990   guint i;
991   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
992
993   memset (copy_params, 0, sizeof (copy_params));
994   memset (&dst_frame, 0, sizeof (GstVideoFrame));
995   memset (&src_frame, 0, sizeof (GstVideoFrame));
996   memset (&dst_map, 0, sizeof (GstMapInfo));
997   memset (&src_map, 0, sizeof (GstMapInfo));
998
999   if (!map_buffer_and_fill_copy2d (dst_buf, dst_info,
1000           dst_type, &dst_frame, &dst_map, FALSE, copy_params)) {
1001     GST_ERROR_OBJECT (context, "Failed to map output buffer");
1002     return FALSE;
1003   }
1004
1005   if (!map_buffer_and_fill_copy2d (src_buf, src_info,
1006           src_type, &src_frame, &src_map, TRUE, copy_params)) {
1007     GST_ERROR_OBJECT (context, "Failed to map input buffer");
1008     unmap_buffer_or_frame (dst_buf, &dst_frame, &dst_map);
1009     return FALSE;
1010   }
1011
1012   if (!gst_cuda_context_push (context)) {
1013     GST_ERROR_OBJECT (context, "Failed to push our context");
1014     goto unmap_and_out;
1015   }
1016
1017   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (dst_info); i++) {
1018     ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
1019     if (!ret) {
1020       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
1021       break;
1022     }
1023   }
1024
1025   gst_cuda_result (CuStreamSynchronize (stream));
1026   gst_cuda_context_pop (NULL);
1027
1028 unmap_and_out:
1029   unmap_buffer_or_frame (dst_buf, &src_frame, &src_map);
1030   unmap_buffer_or_frame (src_buf, &dst_frame, &dst_map);
1031
1032   return ret;
1033 }
1034
1035 #ifdef HAVE_NVCODEC_GST_GL
1036 static gboolean
1037 ensure_gl_interop (void)
1038 {
1039   guint device_count = 0;
1040   CUdevice device_list[1] = { 0, };
1041   CUresult cuda_ret;
1042
1043   cuda_ret = CuGLGetDevices (&device_count,
1044       device_list, 1, CU_GL_DEVICE_LIST_ALL);
1045
1046   if (cuda_ret != CUDA_SUCCESS || device_count == 0)
1047     return FALSE;
1048
1049   return TRUE;
1050 }
1051
1052 typedef struct _GLCopyData
1053 {
1054   GstBuffer *src_buf;
1055   const GstVideoInfo *src_info;
1056   GstBuffer *dst_buf;
1057   const GstVideoInfo *dst_info;
1058
1059   gboolean pbo_to_cuda;
1060   GstCudaBufferCopyType copy_type;
1061   GstCudaContext *context;
1062   CUstream stream;
1063   gboolean ret;
1064 } GLCopyData;
1065
1066 static GstCudaGraphicsResource *
1067 ensure_cuda_gl_graphics_resource (GstCudaContext * context, GstMemory * mem)
1068 {
1069   GQuark quark;
1070   GstCudaGraphicsResource *ret = NULL;
1071
1072   if (!gst_is_gl_memory_pbo (mem)) {
1073     GST_WARNING_OBJECT (context, "memory is not GL PBO memory, %s",
1074         mem->allocator->mem_type);
1075     return NULL;
1076   }
1077
1078   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
1079   ret = (GstCudaGraphicsResource *)
1080       gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
1081
1082   if (!ret) {
1083     GstGLMemoryPBO *pbo;
1084     GstGLBuffer *buf;
1085     GstMapInfo info;
1086
1087     ret = gst_cuda_graphics_resource_new (context,
1088         GST_OBJECT (GST_GL_BASE_MEMORY_CAST (mem)->context),
1089         GST_CUDA_GRAPHICS_RESOURCE_GL_BUFFER);
1090
1091     if (!gst_memory_map (mem, &info, (GstMapFlags) (GST_MAP_READ | GST_MAP_GL))) {
1092       GST_ERROR_OBJECT (context, "Failed to map gl memory");
1093       gst_cuda_graphics_resource_free (ret);
1094       return NULL;
1095     }
1096
1097     pbo = (GstGLMemoryPBO *) mem;
1098     buf = pbo->pbo;
1099
1100     if (!gst_cuda_graphics_resource_register_gl_buffer (ret,
1101             buf->id, CU_GRAPHICS_REGISTER_FLAGS_NONE)) {
1102       GST_ERROR_OBJECT (context, "Failed to register gl buffer");
1103       gst_memory_unmap (mem, &info);
1104       gst_cuda_graphics_resource_free (ret);
1105
1106       return NULL;
1107     }
1108
1109     gst_memory_unmap (mem, &info);
1110
1111     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, ret,
1112         (GDestroyNotify) gst_cuda_graphics_resource_free);
1113   }
1114
1115   return ret;
1116 }
1117
1118 static void
1119 gl_copy_thread_func (GstGLContext * gl_context, GLCopyData * data)
1120 {
1121   GstCudaGraphicsResource *resources[GST_VIDEO_MAX_PLANES];
1122   guint num_resources;
1123   GstBuffer *gl_buf, *cuda_buf;
1124   GstVideoFrame cuda_frame;
1125   GstMapInfo cuda_map_info;
1126   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
1127   guint i;
1128   GstCudaContext *context = data->context;
1129   CUstream stream = data->stream;
1130
1131   memset (copy_params, 0, sizeof (copy_params));
1132   memset (&cuda_frame, 0, sizeof (GstVideoFrame));
1133   memset (&cuda_map_info, 0, sizeof (GstMapInfo));
1134
1135   data->ret = FALSE;
1136
1137   /* Incompatible gl context */
1138   if (!ensure_gl_interop ())
1139     return;
1140
1141   if (data->pbo_to_cuda) {
1142     gl_buf = data->src_buf;
1143     cuda_buf = data->dst_buf;
1144
1145     if (!map_buffer_and_fill_copy2d (cuda_buf,
1146             data->dst_info, data->copy_type, &cuda_frame, &cuda_map_info,
1147             FALSE, copy_params)) {
1148       GST_ERROR_OBJECT (context, "Failed to map output CUDA buffer");
1149       return;
1150     }
1151   } else {
1152     gl_buf = data->dst_buf;
1153     cuda_buf = data->src_buf;
1154
1155     if (!map_buffer_and_fill_copy2d (cuda_buf,
1156             data->src_info, data->copy_type, &cuda_frame, &cuda_map_info,
1157             TRUE, copy_params)) {
1158       GST_ERROR_OBJECT (context, "Failed to map input CUDA buffer");
1159       return;
1160     }
1161   }
1162
1163   num_resources = gst_buffer_n_memory (gl_buf);
1164   g_assert (num_resources >= GST_VIDEO_INFO_N_PLANES (data->src_info));
1165
1166   if (!gst_cuda_context_push (context)) {
1167     GST_ERROR_OBJECT (context, "Failed to push context");
1168     unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1169     return;
1170   }
1171
1172   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->src_info); i++) {
1173     GstMemory *mem = gst_buffer_peek_memory (gl_buf, i);
1174     GstGLMemoryPBO *pbo;
1175
1176     resources[i] = ensure_cuda_gl_graphics_resource (context, mem);
1177     if (!resources[i])
1178       goto out;
1179
1180     pbo = (GstGLMemoryPBO *) mem;
1181     if (!data->pbo_to_cuda) {
1182       /* Need PBO -> texture */
1183       GST_MINI_OBJECT_FLAG_SET (mem, GST_GL_BASE_MEMORY_TRANSFER_NEED_UPLOAD);
1184
1185       /* PBO -> sysmem */
1186       GST_MINI_OBJECT_FLAG_SET (pbo->pbo,
1187           GST_GL_BASE_MEMORY_TRANSFER_NEED_DOWNLOAD);
1188     } else {
1189       /* get the texture into the PBO */
1190       gst_gl_memory_pbo_upload_transfer (pbo);
1191       gst_gl_memory_pbo_download_transfer (pbo);
1192     }
1193   }
1194
1195   for (i = 0; i < GST_VIDEO_INFO_N_PLANES (data->src_info); i++) {
1196     CUgraphicsResource cuda_resource;
1197     CUdeviceptr dev_ptr;
1198     size_t size;
1199     gboolean copy_ret;
1200     gsize width_in_bytes, height;
1201
1202     if (data->pbo_to_cuda) {
1203       cuda_resource =
1204           gst_cuda_graphics_resource_map (resources[i], stream,
1205           CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
1206     } else {
1207       cuda_resource =
1208           gst_cuda_graphics_resource_map (resources[i], stream,
1209           CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1210     }
1211
1212     if (!cuda_resource) {
1213       GST_ERROR_OBJECT (context, "Failed to map graphics resource %d", i);
1214       goto out;
1215     }
1216
1217     if (!gst_cuda_result (CuGraphicsResourceGetMappedPointer (&dev_ptr, &size,
1218                 cuda_resource))) {
1219       gst_cuda_graphics_resource_unmap (resources[i], stream);
1220       GST_ERROR_OBJECT (context, "Failed to get mapped pointer");
1221       goto out;
1222     }
1223
1224     if (data->pbo_to_cuda) {
1225       copy_params[i].srcMemoryType = CU_MEMORYTYPE_DEVICE;
1226       copy_params[i].srcDevice = dev_ptr;
1227       copy_params[i].srcPitch = GST_VIDEO_INFO_PLANE_STRIDE (data->src_info, i);
1228
1229       width_in_bytes = GST_VIDEO_INFO_COMP_WIDTH (data->src_info, i) *
1230           GST_VIDEO_INFO_COMP_PSTRIDE (data->src_info, i);
1231       height = GST_VIDEO_INFO_COMP_HEIGHT (data->src_info, i);
1232     } else {
1233       copy_params[i].dstMemoryType = CU_MEMORYTYPE_DEVICE;
1234       copy_params[i].dstDevice = dev_ptr;
1235       copy_params[i].dstPitch = GST_VIDEO_INFO_PLANE_STRIDE (data->dst_info, i);
1236
1237       width_in_bytes = GST_VIDEO_INFO_COMP_WIDTH (data->dst_info, i) *
1238           GST_VIDEO_INFO_COMP_PSTRIDE (data->dst_info, i);
1239       height = GST_VIDEO_INFO_COMP_HEIGHT (data->dst_info, i);
1240     }
1241
1242     if (width_in_bytes < copy_params[i].WidthInBytes)
1243       copy_params[i].WidthInBytes = width_in_bytes;
1244
1245     if (height < copy_params[i].Height)
1246       copy_params[i].Height = height;
1247
1248     copy_ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
1249     gst_cuda_graphics_resource_unmap (resources[i], stream);
1250
1251     if (!copy_ret) {
1252       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
1253       goto out;
1254     }
1255   }
1256
1257   data->ret = TRUE;
1258
1259 out:
1260   gst_cuda_result (CuStreamSynchronize (stream));
1261   gst_cuda_context_pop (NULL);
1262   unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1263 }
1264
1265 static gboolean
1266 cuda_copy_gl_interop (GstBuffer * dst_buf, const GstVideoInfo * dst_info,
1267     GstBuffer * src_buf, const GstVideoInfo * src_info,
1268     GstGLContext * gl_context, GstCudaContext * context, CUstream stream,
1269     gboolean pbo_to_cuda, GstCudaBufferCopyType copy_type)
1270 {
1271   GLCopyData data;
1272
1273   g_assert (copy_type == GST_CUDA_BUFFER_COPY_CUDA ||
1274       copy_type == GST_CUDA_BUFFER_COPY_NVMM);
1275
1276   data.src_buf = src_buf;
1277   data.src_info = src_info;
1278   data.dst_buf = dst_buf;
1279   data.dst_info = dst_info;
1280   data.pbo_to_cuda = pbo_to_cuda;
1281   data.copy_type = copy_type;
1282   data.context = context;
1283   data.stream = stream;
1284   data.ret = FALSE;
1285
1286   gst_gl_context_thread_add (gl_context,
1287       (GstGLContextThreadFunc) gl_copy_thread_func, &data);
1288
1289   return data.ret;
1290 }
1291 #endif
1292
1293 #ifdef GST_CUDA_HAS_D3D
1294 static gboolean
1295 ensure_d3d11_interop (GstCudaContext * context, GstD3D11Device * device)
1296 {
1297   guint device_count = 0;
1298   guint cuda_device_id;
1299   CUdevice device_list[1] = { 0, };
1300   CUresult cuda_ret;
1301
1302   g_object_get (context, "cuda-device-id", &cuda_device_id, NULL);
1303
1304   cuda_ret = CuD3D11GetDevices (&device_count,
1305       device_list, 1, gst_d3d11_device_get_device_handle (device),
1306       CU_D3D11_DEVICE_LIST_ALL);
1307
1308   if (cuda_ret != CUDA_SUCCESS || device_count == 0)
1309     return FALSE;
1310
1311   if (device_list[0] != (CUdevice) cuda_device_id)
1312     return FALSE;
1313
1314   return TRUE;
1315 }
1316
1317 static GstCudaGraphicsResource *
1318 ensure_cuda_d3d11_graphics_resource (GstCudaContext * context, GstMemory * mem)
1319 {
1320   GQuark quark;
1321   GstCudaGraphicsResource *ret = NULL;
1322
1323   if (!gst_is_d3d11_memory (mem)) {
1324     GST_WARNING_OBJECT (context, "memory is not D3D11 memory, %s",
1325         mem->allocator->mem_type);
1326     return NULL;
1327   }
1328
1329   quark = gst_cuda_quark_from_id (GST_CUDA_QUARK_GRAPHICS_RESOURCE);
1330   ret = (GstCudaGraphicsResource *)
1331       gst_mini_object_get_qdata (GST_MINI_OBJECT (mem), quark);
1332
1333   if (!ret) {
1334     ret = gst_cuda_graphics_resource_new (context,
1335         GST_OBJECT (GST_D3D11_MEMORY_CAST (mem)->device),
1336         GST_CUDA_GRAPHICS_RESOURCE_D3D11_RESOURCE);
1337
1338     if (!gst_cuda_graphics_resource_register_d3d11_resource (ret,
1339             gst_d3d11_memory_get_resource_handle (GST_D3D11_MEMORY_CAST (mem)),
1340             CU_GRAPHICS_REGISTER_FLAGS_SURFACE_LOAD_STORE)) {
1341       GST_ERROR_OBJECT (context, "failed to register d3d11 resource");
1342       gst_cuda_graphics_resource_free (ret);
1343
1344       return NULL;
1345     }
1346
1347     gst_mini_object_set_qdata (GST_MINI_OBJECT (mem), quark, ret,
1348         (GDestroyNotify) gst_cuda_graphics_resource_free);
1349   }
1350
1351   return ret;
1352 }
1353
1354 static gboolean
1355 cuda_copy_d3d11_interop (GstBuffer * dst_buf, const GstVideoInfo * dst_info,
1356     GstBuffer * src_buf, const GstVideoInfo * src_info, GstD3D11Device * device,
1357     GstCudaContext * context, CUstream stream, gboolean d3d11_to_cuda)
1358 {
1359   GstCudaGraphicsResource *resources[GST_VIDEO_MAX_PLANES];
1360   D3D11_TEXTURE2D_DESC desc[GST_VIDEO_MAX_PLANES];
1361   guint num_resources;
1362   GstBuffer *d3d11_buf, *cuda_buf;
1363   GstVideoFrame d3d11_frame, cuda_frame;
1364   GstMapInfo cuda_map_info;
1365   CUDA_MEMCPY2D copy_params[GST_VIDEO_MAX_PLANES];
1366   guint i;
1367   gboolean ret = FALSE;
1368
1369   memset (copy_params, 0, sizeof (copy_params));
1370   memset (&cuda_frame, 0, sizeof (GstVideoFrame));
1371   memset (&cuda_map_info, 0, sizeof (GstMapInfo));
1372
1373   /* Incompatible d3d11 device */
1374   if (!ensure_d3d11_interop (context, device))
1375     return FALSE;
1376
1377   if (d3d11_to_cuda) {
1378     d3d11_buf = src_buf;
1379     cuda_buf = dst_buf;
1380     if (!gst_video_frame_map (&d3d11_frame, src_info, d3d11_buf,
1381             GST_MAP_READ | GST_MAP_D3D11)) {
1382       GST_ERROR_OBJECT (context, "Failed to map input D3D11 buffer");
1383       return FALSE;
1384     }
1385     if (!map_buffer_and_fill_copy2d (cuda_buf,
1386             dst_info, GST_CUDA_BUFFER_COPY_CUDA, &cuda_frame, &cuda_map_info,
1387             FALSE, copy_params)) {
1388       GST_ERROR_OBJECT (context, "Failed to map output CUDA buffer");
1389       gst_video_frame_unmap (&d3d11_frame);
1390       return FALSE;
1391     }
1392   } else {
1393     d3d11_buf = dst_buf;
1394     cuda_buf = src_buf;
1395     if (!gst_video_frame_map (&d3d11_frame, dst_info, d3d11_buf,
1396             GST_MAP_WRITE | GST_MAP_D3D11)) {
1397       GST_ERROR_OBJECT (context, "Failed to map output D3D11 buffer");
1398       return FALSE;
1399     }
1400     if (!map_buffer_and_fill_copy2d (cuda_buf,
1401             src_info, GST_CUDA_BUFFER_COPY_CUDA, &cuda_frame, &cuda_map_info,
1402             TRUE, copy_params)) {
1403       GST_ERROR_OBJECT (context, "Failed to map input CUDA buffer");
1404       gst_video_frame_unmap (&d3d11_frame);
1405       return FALSE;
1406     }
1407   }
1408
1409   num_resources = gst_buffer_n_memory (d3d11_buf);
1410   g_assert (num_resources >= GST_VIDEO_FRAME_N_PLANES (&d3d11_frame));
1411
1412   if (!gst_cuda_context_push (context)) {
1413     GST_ERROR_OBJECT (context, "Failed to push context");
1414     gst_video_frame_unmap (&d3d11_frame);
1415     unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1416     return FALSE;
1417   }
1418
1419   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&d3d11_frame); i++) {
1420     GstMemory *mem = gst_buffer_peek_memory (d3d11_buf, i);
1421
1422     resources[i] = ensure_cuda_d3d11_graphics_resource (context, mem);
1423     if (!resources[i]
1424         || !gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (mem),
1425             &desc[i]))
1426       goto out;
1427   }
1428
1429   for (i = 0; i < GST_VIDEO_FRAME_N_PLANES (&d3d11_frame); i++) {
1430     CUgraphicsResource cuda_resource;
1431     CUarray d3d11_array;
1432     gboolean copy_ret;
1433
1434     if (d3d11_to_cuda) {
1435       cuda_resource =
1436           gst_cuda_graphics_resource_map (resources[i], stream,
1437           CU_GRAPHICS_MAP_RESOURCE_FLAGS_READ_ONLY);
1438     } else {
1439       cuda_resource =
1440           gst_cuda_graphics_resource_map (resources[i], stream,
1441           CU_GRAPHICS_MAP_RESOURCE_FLAGS_WRITE_DISCARD);
1442     }
1443
1444     if (!cuda_resource) {
1445       GST_ERROR_OBJECT (context, "Failed to map graphics resource %d", i);
1446       goto out;
1447     }
1448
1449     if (!gst_cuda_result (CuGraphicsSubResourceGetMappedArray (&d3d11_array,
1450                 cuda_resource, 0, 0))) {
1451       gst_cuda_graphics_resource_unmap (resources[i], stream);
1452       GST_ERROR_OBJECT (context, "Failed to get mapped array");
1453       goto out;
1454     }
1455
1456     if (d3d11_to_cuda) {
1457       copy_params[i].srcMemoryType = CU_MEMORYTYPE_ARRAY;
1458       copy_params[i].srcArray = d3d11_array;
1459       copy_params[i].srcPitch =
1460           desc[i].Width * GST_VIDEO_FRAME_COMP_PSTRIDE (&d3d11_frame, i);
1461     } else {
1462       copy_params[i].dstMemoryType = CU_MEMORYTYPE_ARRAY;
1463       copy_params[i].dstArray = d3d11_array;
1464       copy_params[i].dstPitch =
1465           desc[i].Width * GST_VIDEO_FRAME_COMP_PSTRIDE (&d3d11_frame, i);
1466     }
1467
1468     copy_ret = gst_cuda_result (CuMemcpy2DAsync (&copy_params[i], stream));
1469     gst_cuda_graphics_resource_unmap (resources[i], stream);
1470
1471     if (!copy_ret) {
1472       GST_ERROR_OBJECT (context, "Failed to copy plane %d", i);
1473       goto out;
1474     }
1475   }
1476
1477   ret = TRUE;
1478
1479 out:
1480   gst_cuda_result (CuStreamSynchronize (stream));
1481   gst_cuda_context_pop (NULL);
1482   gst_video_frame_unmap (&d3d11_frame);
1483   unmap_buffer_or_frame (cuda_buf, &cuda_frame, &cuda_map_info);
1484
1485   return ret;
1486 }
1487 #endif
1488
1489 gboolean
1490 gst_cuda_buffer_copy (GstBuffer * dst, GstCudaBufferCopyType dst_type,
1491     const GstVideoInfo * dst_info, GstBuffer * src,
1492     GstCudaBufferCopyType src_type, const GstVideoInfo * src_info,
1493     GstCudaContext * context, CUstream stream)
1494 {
1495   gboolean use_copy_2d = FALSE;
1496   GstMemory *dst_mem, *src_mem;
1497 #ifdef GST_CUDA_HAS_D3D
1498   D3D11_TEXTURE2D_DESC desc;
1499 #endif
1500   GstCudaContext *cuda_context;
1501
1502   g_return_val_if_fail (GST_IS_BUFFER (dst), FALSE);
1503   g_return_val_if_fail (dst_info != NULL, FALSE);
1504   g_return_val_if_fail (GST_IS_BUFFER (src), FALSE);
1505   g_return_val_if_fail (src_info != NULL, FALSE);
1506   g_return_val_if_fail (GST_IS_CUDA_CONTEXT (context), FALSE);
1507
1508   _init_debug ();
1509
1510   if (dst_type == GST_CUDA_BUFFER_COPY_NVMM &&
1511       src_type == GST_CUDA_BUFFER_COPY_NVMM) {
1512     GST_ERROR_OBJECT (context, "Not supported copy NVMM -> NVMM");
1513     return FALSE;
1514   }
1515
1516   if (GST_VIDEO_INFO_FORMAT (dst_info) != GST_VIDEO_INFO_FORMAT (src_info)) {
1517     GST_ERROR_OBJECT (context,
1518         "Copy between different format is not supported");
1519     return FALSE;
1520   }
1521
1522   if (dst_type == GST_CUDA_BUFFER_COPY_CUDA ||
1523       dst_type == GST_CUDA_BUFFER_COPY_NVMM ||
1524       src_type == GST_CUDA_BUFFER_COPY_CUDA ||
1525       src_type == GST_CUDA_BUFFER_COPY_NVMM) {
1526     use_copy_2d = TRUE;
1527   }
1528
1529   if (!use_copy_2d) {
1530     GST_TRACE_OBJECT (context, "Not a device memory, use system memory copy");
1531     return gst_cuda_buffer_fallback_copy (dst, dst_info, src, src_info);
1532   }
1533
1534   dst_mem = gst_buffer_peek_memory (dst, 0);
1535   src_mem = gst_buffer_peek_memory (src, 0);
1536
1537 #ifdef HAVE_NVCODEC_GST_GL
1538   if (src_type == GST_CUDA_BUFFER_COPY_GL && gst_is_gl_memory_pbo (src_mem)) {
1539     GstGLMemory *gl_mem = (GstGLMemory *) src_mem;
1540     GstGLContext *gl_context = gl_mem->mem.context;
1541     GstCudaContext *cuda_context = context;
1542
1543     if (dst_type == GST_CUDA_BUFFER_COPY_CUDA && gst_is_cuda_memory (dst_mem))
1544       cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1545
1546     GST_TRACE_OBJECT (context, "GL -> %s",
1547         gst_cuda_buffer_copy_type_to_string (dst_type));
1548
1549     return cuda_copy_gl_interop (dst, dst_info, src, src_info, gl_context,
1550         cuda_context, stream, TRUE, dst_type);
1551   }
1552
1553   if (dst_type == GST_CUDA_BUFFER_COPY_GL && gst_is_gl_memory_pbo (dst_mem)) {
1554     GstGLMemory *gl_mem = (GstGLMemory *) dst_mem;
1555     GstGLContext *gl_context = gl_mem->mem.context;
1556     GstCudaContext *cuda_context = context;
1557
1558     if (src_type == GST_CUDA_BUFFER_COPY_CUDA && gst_is_cuda_memory (src_mem))
1559       cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1560
1561     GST_TRACE_OBJECT (context, "%s -> GL",
1562         gst_cuda_buffer_copy_type_to_string (src_type));
1563
1564     return cuda_copy_gl_interop (dst, dst_info, src, src_info, gl_context,
1565         cuda_context, stream, FALSE, src_type);
1566   }
1567 #endif
1568
1569 #ifdef GST_CUDA_HAS_D3D
1570   if (src_type == GST_CUDA_BUFFER_COPY_D3D11 && gst_is_d3d11_memory (src_mem) &&
1571       gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (src_mem), &desc)
1572       && desc.Usage == D3D11_USAGE_DEFAULT && gst_is_cuda_memory (dst_mem)) {
1573     GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (src_mem);
1574     GstD3D11Device *device = dmem->device;
1575     GstCudaContext *cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1576     gboolean ret;
1577
1578     GST_TRACE_OBJECT (context, "D3D11 -> CUDA");
1579
1580     gst_d3d11_device_lock (device);
1581     ret = cuda_copy_d3d11_interop (dst, dst_info, src, src_info, device,
1582         cuda_context, stream, TRUE);
1583     gst_d3d11_device_unlock (device);
1584
1585     return ret;
1586   }
1587
1588   if (dst_type == GST_CUDA_BUFFER_COPY_D3D11 && gst_is_d3d11_memory (dst_mem) &&
1589       gst_d3d11_memory_get_texture_desc (GST_D3D11_MEMORY_CAST (dst_mem), &desc)
1590       && desc.Usage == D3D11_USAGE_DEFAULT && gst_is_cuda_memory (src_mem)) {
1591     GstD3D11Memory *dmem = GST_D3D11_MEMORY_CAST (dst_mem);
1592     GstD3D11Device *device = dmem->device;
1593     GstCudaContext *cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1594     gboolean ret;
1595
1596     GST_TRACE_OBJECT (context, "CUDA -> D3D11");
1597
1598     gst_d3d11_device_lock (device);
1599     ret = cuda_copy_d3d11_interop (dst, dst_info, src, src_info, device,
1600         cuda_context, stream, FALSE);
1601     gst_d3d11_device_unlock (device);
1602
1603     return ret;
1604   }
1605 #endif
1606
1607   if (gst_is_cuda_memory (dst_mem)) {
1608     cuda_context = GST_CUDA_MEMORY_CAST (dst_mem)->context;
1609   } else if (gst_is_cuda_memory (src_mem)) {
1610     cuda_context = GST_CUDA_MEMORY_CAST (src_mem)->context;
1611   } else {
1612     cuda_context = context;
1613   }
1614
1615   GST_TRACE_OBJECT (context, "%s -> %s",
1616       gst_cuda_buffer_copy_type_to_string (src_type),
1617       gst_cuda_buffer_copy_type_to_string (dst_type));
1618
1619   return gst_cuda_buffer_copy_internal (dst, dst_type, dst_info,
1620       src, src_type, src_info, cuda_context, stream);
1621 }