]> git.sesse.net Git - ffmpeg/blob - libavutil/hwcontext_cuda.c
avutil/hwcontext_cuda: add YUVA420P pixel format
[ffmpeg] / libavutil / hwcontext_cuda.c
1 /*
2  * This file is part of FFmpeg.
3  *
4  * FFmpeg is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * FFmpeg 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  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with FFmpeg; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18
19 #include "buffer.h"
20 #include "common.h"
21 #include "hwcontext.h"
22 #include "hwcontext_internal.h"
23 #include "hwcontext_cuda_internal.h"
24 #if CONFIG_VULKAN
25 #include "hwcontext_vulkan.h"
26 #endif
27 #include "cuda_check.h"
28 #include "mem.h"
29 #include "pixdesc.h"
30 #include "pixfmt.h"
31 #include "imgutils.h"
32
33 #define CUDA_FRAME_ALIGNMENT 256
34
35 typedef struct CUDAFramesContext {
36     int shift_width, shift_height;
37 } CUDAFramesContext;
38
39 static const enum AVPixelFormat supported_formats[] = {
40     AV_PIX_FMT_NV12,
41     AV_PIX_FMT_YUV420P,
42     AV_PIX_FMT_YUVA420P,
43     AV_PIX_FMT_YUV444P,
44     AV_PIX_FMT_P010,
45     AV_PIX_FMT_P016,
46     AV_PIX_FMT_YUV444P16,
47     AV_PIX_FMT_0RGB32,
48     AV_PIX_FMT_0BGR32,
49 #if CONFIG_VULKAN
50     AV_PIX_FMT_VULKAN,
51 #endif
52 };
53
54 #define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x)
55
56 static int cuda_frames_get_constraints(AVHWDeviceContext *ctx,
57                                        const void *hwconfig,
58                                        AVHWFramesConstraints *constraints)
59 {
60     int i;
61
62     constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1,
63                                                     sizeof(*constraints->valid_sw_formats));
64     if (!constraints->valid_sw_formats)
65         return AVERROR(ENOMEM);
66
67     for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
68         constraints->valid_sw_formats[i] = supported_formats[i];
69     constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE;
70
71     constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
72     if (!constraints->valid_hw_formats)
73         return AVERROR(ENOMEM);
74
75     constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA;
76     constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
77
78     return 0;
79 }
80
81 static void cuda_buffer_free(void *opaque, uint8_t *data)
82 {
83     AVHWFramesContext        *ctx = opaque;
84     AVHWDeviceContext *device_ctx = ctx->device_ctx;
85     AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
86     CudaFunctions             *cu = hwctx->internal->cuda_dl;
87
88     CUcontext dummy;
89
90     CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
91
92     CHECK_CU(cu->cuMemFree((CUdeviceptr)data));
93
94     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
95 }
96
97 static AVBufferRef *cuda_pool_alloc(void *opaque, int size)
98 {
99     AVHWFramesContext        *ctx = opaque;
100     AVHWDeviceContext *device_ctx = ctx->device_ctx;
101     AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
102     CudaFunctions             *cu = hwctx->internal->cuda_dl;
103
104     AVBufferRef *ret = NULL;
105     CUcontext dummy = NULL;
106     CUdeviceptr data;
107     int err;
108
109     err = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
110     if (err < 0)
111         return NULL;
112
113     err = CHECK_CU(cu->cuMemAlloc(&data, size));
114     if (err < 0)
115         goto fail;
116
117     ret = av_buffer_create((uint8_t*)data, size, cuda_buffer_free, ctx, 0);
118     if (!ret) {
119         CHECK_CU(cu->cuMemFree(data));
120         goto fail;
121     }
122
123 fail:
124     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
125     return ret;
126 }
127
128 static int cuda_frames_init(AVHWFramesContext *ctx)
129 {
130     CUDAFramesContext *priv = ctx->internal->priv;
131     int i;
132
133     for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
134         if (ctx->sw_format == supported_formats[i])
135             break;
136     }
137     if (i == FF_ARRAY_ELEMS(supported_formats)) {
138         av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n",
139                av_get_pix_fmt_name(ctx->sw_format));
140         return AVERROR(ENOSYS);
141     }
142
143     av_pix_fmt_get_chroma_sub_sample(ctx->sw_format, &priv->shift_width, &priv->shift_height);
144
145     if (!ctx->pool) {
146         int size = av_image_get_buffer_size(ctx->sw_format, ctx->width, ctx->height, CUDA_FRAME_ALIGNMENT);
147         if (size < 0)
148             return size;
149
150         ctx->internal->pool_internal = av_buffer_pool_init2(size, ctx, cuda_pool_alloc, NULL);
151         if (!ctx->internal->pool_internal)
152             return AVERROR(ENOMEM);
153     }
154
155     return 0;
156 }
157
158 static int cuda_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
159 {
160     int res;
161
162     frame->buf[0] = av_buffer_pool_get(ctx->pool);
163     if (!frame->buf[0])
164         return AVERROR(ENOMEM);
165
166     res = av_image_fill_arrays(frame->data, frame->linesize, frame->buf[0]->data,
167                                ctx->sw_format, ctx->width, ctx->height, CUDA_FRAME_ALIGNMENT);
168     if (res < 0)
169         return res;
170
171     // YUV420P is a special case.
172     // Nvenc expects the U/V planes in swapped order from how ffmpeg expects them, also chroma is half-aligned
173     if (ctx->sw_format == AV_PIX_FMT_YUV420P) {
174         frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2;
175         frame->data[2]     = frame->data[1];
176         frame->data[1]     = frame->data[2] + frame->linesize[2] * ctx->height / 2;
177     }
178
179     frame->format = AV_PIX_FMT_CUDA;
180     frame->width  = ctx->width;
181     frame->height = ctx->height;
182
183     return 0;
184 }
185
186 static int cuda_transfer_get_formats(AVHWFramesContext *ctx,
187                                      enum AVHWFrameTransferDirection dir,
188                                      enum AVPixelFormat **formats)
189 {
190     enum AVPixelFormat *fmts;
191
192     fmts = av_malloc_array(2, sizeof(*fmts));
193     if (!fmts)
194         return AVERROR(ENOMEM);
195
196     fmts[0] = ctx->sw_format;
197     fmts[1] = AV_PIX_FMT_NONE;
198
199     *formats = fmts;
200
201     return 0;
202 }
203
204 static int cuda_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst,
205                                    const AVFrame *src)
206 {
207     CUDAFramesContext       *priv = ctx->internal->priv;
208     AVHWDeviceContext *device_ctx = ctx->device_ctx;
209     AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
210     CudaFunctions             *cu = hwctx->internal->cuda_dl;
211
212     CUcontext dummy;
213     int i, ret;
214
215     /* We don't support transfers to HW devices. */
216     if (dst->hw_frames_ctx)
217         return AVERROR(ENOSYS);
218
219     ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
220     if (ret < 0)
221         return ret;
222
223     for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) {
224         CUDA_MEMCPY2D cpy = {
225             .srcMemoryType = CU_MEMORYTYPE_DEVICE,
226             .dstMemoryType = CU_MEMORYTYPE_HOST,
227             .srcDevice     = (CUdeviceptr)src->data[i],
228             .dstHost       = dst->data[i],
229             .srcPitch      = src->linesize[i],
230             .dstPitch      = dst->linesize[i],
231             .WidthInBytes  = FFMIN(src->linesize[i], dst->linesize[i]),
232             .Height        = src->height >> (i ? priv->shift_height : 0),
233         };
234
235         ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream));
236         if (ret < 0)
237             goto exit;
238     }
239
240     ret = CHECK_CU(cu->cuStreamSynchronize(hwctx->stream));
241     if (ret < 0)
242         goto exit;
243
244 exit:
245     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
246
247     return 0;
248 }
249
250 static int cuda_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst,
251                                  const AVFrame *src)
252 {
253     CUDAFramesContext       *priv = ctx->internal->priv;
254     AVHWDeviceContext *device_ctx = ctx->device_ctx;
255     AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
256     CudaFunctions             *cu = hwctx->internal->cuda_dl;
257
258     CUcontext dummy;
259     int i, ret;
260
261     /* We don't support transfers from HW devices. */
262     if (src->hw_frames_ctx)
263         return AVERROR(ENOSYS);
264
265     ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
266     if (ret < 0)
267         return ret;
268
269     for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) {
270         CUDA_MEMCPY2D cpy = {
271             .srcMemoryType = CU_MEMORYTYPE_HOST,
272             .dstMemoryType = CU_MEMORYTYPE_DEVICE,
273             .srcHost       = src->data[i],
274             .dstDevice     = (CUdeviceptr)dst->data[i],
275             .srcPitch      = src->linesize[i],
276             .dstPitch      = dst->linesize[i],
277             .WidthInBytes  = FFMIN(src->linesize[i], dst->linesize[i]),
278             .Height        = src->height >> ((i == 0 || i == 3) ? 0 : priv->shift_height),
279         };
280
281         ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream));
282         if (ret < 0)
283             goto exit;
284     }
285
286 exit:
287     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
288
289     return 0;
290 }
291
292 static void cuda_device_uninit(AVHWDeviceContext *device_ctx)
293 {
294     AVCUDADeviceContext *hwctx = device_ctx->hwctx;
295
296     if (hwctx->internal) {
297         CudaFunctions *cu = hwctx->internal->cuda_dl;
298
299         if (hwctx->internal->is_allocated && hwctx->cuda_ctx) {
300             if (hwctx->internal->flags & AV_CUDA_USE_PRIMARY_CONTEXT)
301                 CHECK_CU(cu->cuDevicePrimaryCtxRelease(hwctx->internal->cuda_device));
302             else
303                 CHECK_CU(cu->cuCtxDestroy(hwctx->cuda_ctx));
304
305             hwctx->cuda_ctx = NULL;
306         }
307
308         cuda_free_functions(&hwctx->internal->cuda_dl);
309     }
310
311     av_freep(&hwctx->internal);
312 }
313
314 static int cuda_device_init(AVHWDeviceContext *ctx)
315 {
316     AVCUDADeviceContext *hwctx = ctx->hwctx;
317     int ret;
318
319     if (!hwctx->internal) {
320         hwctx->internal = av_mallocz(sizeof(*hwctx->internal));
321         if (!hwctx->internal)
322             return AVERROR(ENOMEM);
323     }
324
325     if (!hwctx->internal->cuda_dl) {
326         ret = cuda_load_functions(&hwctx->internal->cuda_dl, ctx);
327         if (ret < 0) {
328             av_log(ctx, AV_LOG_ERROR, "Could not dynamically load CUDA\n");
329             goto error;
330         }
331     }
332
333     return 0;
334
335 error:
336     cuda_device_uninit(ctx);
337     return ret;
338 }
339
340 static int cuda_context_init(AVHWDeviceContext *device_ctx, int flags) {
341     AVCUDADeviceContext *hwctx = device_ctx->hwctx;
342     CudaFunctions *cu;
343     CUcontext dummy;
344     int ret, dev_active = 0;
345     unsigned int dev_flags = 0;
346
347     const unsigned int desired_flags = CU_CTX_SCHED_BLOCKING_SYNC;
348
349     cu = hwctx->internal->cuda_dl;
350
351     hwctx->internal->flags = flags;
352
353     if (flags & AV_CUDA_USE_PRIMARY_CONTEXT) {
354         ret = CHECK_CU(cu->cuDevicePrimaryCtxGetState(hwctx->internal->cuda_device,
355                        &dev_flags, &dev_active));
356         if (ret < 0)
357             return ret;
358
359         if (dev_active && dev_flags != desired_flags) {
360             av_log(device_ctx, AV_LOG_ERROR, "Primary context already active with incompatible flags.\n");
361             return AVERROR(ENOTSUP);
362         } else if (dev_flags != desired_flags) {
363             ret = CHECK_CU(cu->cuDevicePrimaryCtxSetFlags(hwctx->internal->cuda_device,
364                            desired_flags));
365             if (ret < 0)
366                 return ret;
367         }
368
369         ret = CHECK_CU(cu->cuDevicePrimaryCtxRetain(&hwctx->cuda_ctx,
370                                                     hwctx->internal->cuda_device));
371         if (ret < 0)
372             return ret;
373     } else {
374         ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags,
375                                        hwctx->internal->cuda_device));
376         if (ret < 0)
377             return ret;
378
379         CHECK_CU(cu->cuCtxPopCurrent(&dummy));
380     }
381
382     hwctx->internal->is_allocated = 1;
383
384     // Setting stream to NULL will make functions automatically use the default CUstream
385     hwctx->stream = NULL;
386
387     return 0;
388 }
389
390 static int cuda_device_create(AVHWDeviceContext *device_ctx,
391                               const char *device,
392                               AVDictionary *opts, int flags)
393 {
394     AVCUDADeviceContext *hwctx = device_ctx->hwctx;
395     CudaFunctions *cu;
396     int ret, device_idx = 0;
397
398     if (device)
399         device_idx = strtol(device, NULL, 0);
400
401     if (cuda_device_init(device_ctx) < 0)
402         goto error;
403
404     cu = hwctx->internal->cuda_dl;
405
406     ret = CHECK_CU(cu->cuInit(0));
407     if (ret < 0)
408         goto error;
409
410     ret = CHECK_CU(cu->cuDeviceGet(&hwctx->internal->cuda_device, device_idx));
411     if (ret < 0)
412         goto error;
413
414     ret = cuda_context_init(device_ctx, flags);
415     if (ret < 0)
416         goto error;
417
418     return 0;
419
420 error:
421     cuda_device_uninit(device_ctx);
422     return AVERROR_UNKNOWN;
423 }
424
425 static int cuda_device_derive(AVHWDeviceContext *device_ctx,
426                               AVHWDeviceContext *src_ctx,
427                               int flags) {
428     AVCUDADeviceContext *hwctx = device_ctx->hwctx;
429     CudaFunctions *cu;
430     const char *src_uuid = NULL;
431     int ret, i, device_count;
432
433 #if CONFIG_VULKAN
434     VkPhysicalDeviceIDProperties vk_idp = {
435         .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES,
436     };
437 #endif
438
439     switch (src_ctx->type) {
440 #if CONFIG_VULKAN
441     case AV_HWDEVICE_TYPE_VULKAN: {
442         AVVulkanDeviceContext *vkctx = src_ctx->hwctx;
443         VkPhysicalDeviceProperties2 vk_dev_props = {
444             .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
445             .pNext = &vk_idp,
446         };
447         vkGetPhysicalDeviceProperties2(vkctx->phys_dev, &vk_dev_props);
448         src_uuid = vk_idp.deviceUUID;
449         break;
450     }
451 #endif
452     default:
453         return AVERROR(ENOSYS);
454     }
455
456     if (!src_uuid) {
457         av_log(device_ctx, AV_LOG_ERROR,
458                "Failed to get UUID of source device.\n");
459         goto error;
460     }
461
462     if (cuda_device_init(device_ctx) < 0)
463         goto error;
464
465     cu = hwctx->internal->cuda_dl;
466
467     ret = CHECK_CU(cu->cuInit(0));
468     if (ret < 0)
469         goto error;
470
471     ret = CHECK_CU(cu->cuDeviceGetCount(&device_count));
472     if (ret < 0)
473         goto error;
474
475     hwctx->internal->cuda_device = -1;
476     for (i = 0; i < device_count; i++) {
477         CUdevice dev;
478         CUuuid uuid;
479
480         ret = CHECK_CU(cu->cuDeviceGet(&dev, i));
481         if (ret < 0)
482             goto error;
483
484         ret = CHECK_CU(cu->cuDeviceGetUuid(&uuid, dev));
485         if (ret < 0)
486             goto error;
487
488         if (memcmp(src_uuid, uuid.bytes, sizeof (uuid.bytes)) == 0) {
489             hwctx->internal->cuda_device = dev;
490             break;
491         }
492     }
493
494     if (hwctx->internal->cuda_device == -1) {
495         av_log(device_ctx, AV_LOG_ERROR, "Could not derive CUDA device.\n");
496         goto error;
497     }
498
499     ret = cuda_context_init(device_ctx, flags);
500     if (ret < 0)
501         goto error;
502
503     return 0;
504
505 error:
506     cuda_device_uninit(device_ctx);
507     return AVERROR_UNKNOWN;
508 }
509
510 const HWContextType ff_hwcontext_type_cuda = {
511     .type                 = AV_HWDEVICE_TYPE_CUDA,
512     .name                 = "CUDA",
513
514     .device_hwctx_size    = sizeof(AVCUDADeviceContext),
515     .frames_priv_size     = sizeof(CUDAFramesContext),
516
517     .device_create        = cuda_device_create,
518     .device_derive        = cuda_device_derive,
519     .device_init          = cuda_device_init,
520     .device_uninit        = cuda_device_uninit,
521     .frames_get_constraints = cuda_frames_get_constraints,
522     .frames_init          = cuda_frames_init,
523     .frames_get_buffer    = cuda_get_buffer,
524     .transfer_get_formats = cuda_transfer_get_formats,
525     .transfer_data_to     = cuda_transfer_data_to,
526     .transfer_data_from   = cuda_transfer_data_from,
527
528     .pix_fmts             = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE },
529 };