This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit ef3dcf4ea6139f2b8cad29e67b48a39f094fa3bc Author: Timo Rothenpieler <[email protected]> AuthorDate: Mon Mar 16 16:11:32 2026 +0100 Commit: Timo Rothenpieler <[email protected]> CommitDate: Tue Jul 14 19:45:02 2026 +0000 avutil/hwcontext_cuda: add CUarray surface pool support Extend the internal CUDA frames context (CUDAFramesContext) to manage block-linear CUarray surfaces alongside the existing pitched-linear allocations. When the frames context format is AV_PIX_FMT_CUARRAY, the caller (e.g. NVDEC) can fill cuarray_desc and cuarray_num_surfaces before calling av_hwframe_ctx_init(), which then allocates the CUarray surfaces. Also add cuda_transfer_data_from_cuarray() to support hwdownload from CUarray frames to host memory via cuMemcpy2DAsync. Co-authored-by: Diego de Souza <[email protected]> Signed-off-by: Diego de Souza <[email protected]> --- doc/APIchanges | 3 + libavutil/hwcontext_cuda.c | 409 ++++++++++++++++++++++++++++++++---- libavutil/hwcontext_cuda.h | 74 ++++++- libavutil/hwcontext_cuda_internal.h | 21 ++ libavutil/version.h | 2 +- 5 files changed, 470 insertions(+), 39 deletions(-) diff --git a/doc/APIchanges b/doc/APIchanges index b24ea9d8f2..a3a978c43b 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23. API changes, most recent first: +2026-07-14 - xxxxxxxxxx - lavu 61.5.100 - hwcontext_cuda.h + Add AVCUDAFramesContext and AVCUDAArrayFrameDescriptor. + 2026-07-14 - xxxxxxxxxx - lavu 61.5.100 - pixfmt.h Add AV_PIX_FMT_CUARRAY. diff --git a/libavutil/hwcontext_cuda.c b/libavutil/hwcontext_cuda.c index a171a9e139..61fbf52885 100644 --- a/libavutil/hwcontext_cuda.c +++ b/libavutil/hwcontext_cuda.c @@ -31,8 +31,12 @@ #include "imgutils.h" typedef struct CUDAFramesContext { + AVCUDAFramesContext p; + int shift_width, shift_height; int tex_alignment; + + int cuarray_num_surfaces_used; } CUDAFramesContext; typedef struct CUDADeviceContext { @@ -46,12 +50,16 @@ static const enum AVPixelFormat supported_formats[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVA420P, AV_PIX_FMT_YUV444P, + AV_PIX_FMT_NV24, AV_PIX_FMT_P010, AV_PIX_FMT_P012, AV_PIX_FMT_P016, AV_PIX_FMT_P210, AV_PIX_FMT_P212, AV_PIX_FMT_P216, + AV_PIX_FMT_P410, + AV_PIX_FMT_P412, + AV_PIX_FMT_P416, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV420P10, AV_PIX_FMT_YUV422P10, @@ -70,27 +78,46 @@ static const enum AVPixelFormat supported_formats[] = { #define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x) +static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt); +static unsigned int cuda_array_numchannels_for_pix_fmt(enum AVPixelFormat fmt); + static int cuda_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints) { - int i; + const AVCUDAHWConfig *config = hwconfig; + enum AVPixelFormat req_fmt = config ? config->hw_format : AV_PIX_FMT_NONE; + int i, nb_sw_formats = 0; constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1, sizeof(*constraints->valid_sw_formats)); if (!constraints->valid_sw_formats) return AVERROR(ENOMEM); - for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) - constraints->valid_sw_formats[i] = supported_formats[i]; - constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = AV_PIX_FMT_NONE; + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { + if (req_fmt == AV_PIX_FMT_CUARRAY && + !cuda_array_format_for_pix_fmt(supported_formats[i])) + continue; + constraints->valid_sw_formats[nb_sw_formats++] = supported_formats[i]; + } + constraints->valid_sw_formats[nb_sw_formats] = AV_PIX_FMT_NONE; - constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); - if (!constraints->valid_hw_formats) - return AVERROR(ENOMEM); + if (req_fmt == AV_PIX_FMT_CUDA || req_fmt == AV_PIX_FMT_CUARRAY) { + constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); + if (!constraints->valid_hw_formats) + return AVERROR(ENOMEM); + + constraints->valid_hw_formats[0] = req_fmt; + constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; + } else { + constraints->valid_hw_formats = av_malloc_array(3, sizeof(*constraints->valid_hw_formats)); + if (!constraints->valid_hw_formats) + return AVERROR(ENOMEM); - constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA; - constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; + constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA; + constraints->valid_hw_formats[1] = AV_PIX_FMT_CUARRAY; + constraints->valid_hw_formats[2] = AV_PIX_FMT_NONE; + } return 0; } @@ -106,7 +133,13 @@ static void cuda_buffer_free(void *opaque, uint8_t *data) CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); - CHECK_CU(cu->cuMemFree((CUdeviceptr)data)); + if (ctx->format == AV_PIX_FMT_CUARRAY) { + AVCUDAArrayFrameDescriptor *desc = (AVCUDAArrayFrameDescriptor*)data; + CHECK_CU(cu->cuArrayDestroy(desc->array)); + av_free(desc); + } else { + CHECK_CU(cu->cuMemFree((CUdeviceptr)data)); + } CHECK_CU(cu->cuCtxPopCurrent(&dummy)); } @@ -114,6 +147,7 @@ static void cuda_buffer_free(void *opaque, uint8_t *data) static AVBufferRef *cuda_pool_alloc(void *opaque, size_t size) { AVHWFramesContext *ctx = opaque; + CUDAFramesContext *priv = ctx->hwctx; AVHWDeviceContext *device_ctx = ctx->device_ctx; AVCUDADeviceContext *hwctx = device_ctx->hwctx; CudaFunctions *cu = hwctx->internal->cuda_dl; @@ -127,21 +161,122 @@ static AVBufferRef *cuda_pool_alloc(void *opaque, size_t size) if (err < 0) return NULL; + if (ctx->format == AV_PIX_FMT_CUARRAY) { + AVCUDAArrayFrameDescriptor *desc = av_mallocz(sizeof(*desc)); + if (!desc) + goto done; + + if (priv->p.cuarray_num_surfaces > 0) { + if (priv->cuarray_num_surfaces_used >= priv->p.cuarray_num_surfaces) { + av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n"); + av_free(desc); + goto done; + } + desc->index = priv->cuarray_num_surfaces_used++; + desc->array = priv->p.cuarray_surfaces[desc->index]; + } else { + err = CHECK_CU(cu->cuArray3DCreate(&desc->array, &priv->p.cuarray_desc)); + if (err < 0) { + av_free(desc); + goto done; + } + } + + ret = av_buffer_create((uint8_t*)desc, sizeof(*desc), cuda_buffer_free, ctx, 0); + if (!ret) { + // It is okay (and necessary) to free a pool array here, + // since cuarray_num_surfaces_used is already incremented. + CHECK_CU(cu->cuArrayDestroy(desc->array)); + av_free(desc); + goto done; + } + + goto done; + } + err = CHECK_CU(cu->cuMemAlloc(&data, size)); if (err < 0) - goto fail; + goto done; ret = av_buffer_create((uint8_t*)data, size, cuda_buffer_free, ctx, 0); if (!ret) { CHECK_CU(cu->cuMemFree(data)); - goto fail; + goto done; } -fail: + // Common exit: reached on both success (ret holds the buffer) and + // failure (ret == NULL); restores the CUDA context and returns ret. +done: CHECK_CU(cu->cuCtxPopCurrent(&dummy)); return ret; } +static void cuda_frames_uninit(AVHWFramesContext *ctx) +{ + AVHWDeviceContext *device_ctx = ctx->device_ctx; + AVCUDADeviceContext *hwctx = device_ctx->hwctx; + CUDAFramesContext *priv = ctx->hwctx; + CudaFunctions *cu = hwctx->internal->cuda_dl; + + if (priv->p.cuarray_surfaces) { + CUcontext dummy; + CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); + + // Make sure we don't free surfaces that have been adopted by the pool already + for (int i = priv->cuarray_num_surfaces_used; i < priv->p.cuarray_num_surfaces; i++) + if (priv->p.cuarray_surfaces[i]) + CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i])); + + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + av_freep(&priv->p.cuarray_surfaces); + priv->p.cuarray_num_surfaces = 0; + } +} + +static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt) +{ + switch (fmt) { + case AV_PIX_FMT_NV12: return CU_AD_FORMAT_NV12; + case AV_PIX_FMT_P010: + case AV_PIX_FMT_P012: + case AV_PIX_FMT_P016: return CU_AD_FORMAT_P016; + case AV_PIX_FMT_NV16: return CU_AD_FORMAT_NV16; + case AV_PIX_FMT_P210: + case AV_PIX_FMT_P212: + case AV_PIX_FMT_P216: return CU_AD_FORMAT_P216; + case AV_PIX_FMT_NV24: return CU_AD_FORMAT_YUV444_8BIT_SEMIPLANAR; + case AV_PIX_FMT_P410: + case AV_PIX_FMT_P412: + case AV_PIX_FMT_P416: return CU_AD_FORMAT_YUV444_16BIT_SEMIPLANAR; + case AV_PIX_FMT_YUV420P: return CU_AD_FORMAT_UINT8_PLANAR_420; + case AV_PIX_FMT_YUV422P: return CU_AD_FORMAT_UINT8_PLANAR_422; + case AV_PIX_FMT_YUV444P: return CU_AD_FORMAT_UINT8_PLANAR_444; + case AV_PIX_FMT_YUV420P10: return CU_AD_FORMAT_UINT16_PLANAR_420; + case AV_PIX_FMT_YUV422P10: return CU_AD_FORMAT_UINT16_PLANAR_422; + case AV_PIX_FMT_YUV444P10: + case AV_PIX_FMT_YUV444P10MSB: + case AV_PIX_FMT_YUV444P12MSB: + case AV_PIX_FMT_YUV444P16: return CU_AD_FORMAT_UINT16_PLANAR_444; + case AV_PIX_FMT_0RGB32: + case AV_PIX_FMT_0BGR32: + case AV_PIX_FMT_RGB32: + case AV_PIX_FMT_BGR32: return CU_AD_FORMAT_UNSIGNED_INT8; + default: return 0; + } +} + +static unsigned int cuda_array_numchannels_for_pix_fmt(enum AVPixelFormat fmt) +{ + switch (fmt) { + case AV_PIX_FMT_0RGB32: + case AV_PIX_FMT_0BGR32: + case AV_PIX_FMT_RGB32: + case AV_PIX_FMT_BGR32: return 4; + default: return 3; + } +} + static int cuda_frames_init(AVHWFramesContext *ctx) { AVHWDeviceContext *device_ctx = ctx->device_ctx; @@ -150,6 +285,8 @@ static int cuda_frames_init(AVHWFramesContext *ctx) CudaFunctions *cu = hwctx->internal->cuda_dl; int err, i; + CUcontext dummy; + for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { if (ctx->sw_format == supported_formats[i]) break; @@ -160,6 +297,11 @@ static int cuda_frames_init(AVHWFramesContext *ctx) return AVERROR(ENOSYS); } + if (ctx->format == AV_PIX_FMT_CUARRAY && !cu->cuArrayGetPlane) { + av_log(ctx, AV_LOG_ERROR, "cuArrayGetPlane not available, update your driver\n"); + return AVERROR(ENOSYS); + } + err = CHECK_CU(cu->cuDeviceGetAttribute(&priv->tex_alignment, 14 /* CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT */, hwctx->internal->cuda_device)); @@ -176,47 +318,179 @@ static int cuda_frames_init(AVHWFramesContext *ctx) av_pix_fmt_get_chroma_sub_sample(ctx->sw_format, &priv->shift_width, &priv->shift_height); + if (ctx->format == AV_PIX_FMT_CUARRAY) { + if (!priv->p.cuarray_desc.Width) + priv->p.cuarray_desc.Width = ctx->width; + if (!priv->p.cuarray_desc.Height) + priv->p.cuarray_desc.Height = ctx->height; + if (!priv->p.cuarray_desc.NumChannels) + priv->p.cuarray_desc.NumChannels = cuda_array_numchannels_for_pix_fmt(ctx->sw_format); + + if (priv->p.cuarray_desc.Depth) { + av_log(ctx, AV_LOG_ERROR, "CUarrays with non-zero depth are not supported.\n"); + return AVERROR(EINVAL); + } + + if (!priv->p.cuarray_desc.Format) + priv->p.cuarray_desc.Format = cuda_array_format_for_pix_fmt(ctx->sw_format); + if (!priv->p.cuarray_desc.Format) { + av_log(ctx, AV_LOG_ERROR, "Invalid CUarray pixel format\n"); + return AVERROR_BUG; + } + + priv->p.cuarray_desc.Flags |= CUDA_ARRAY3D_SURFACE_LDST | CUDA_ARRAY3D_VIDEO_ENCODE_DECODE; + } + + if (ctx->format == AV_PIX_FMT_CUARRAY && priv->p.cuarray_num_surfaces > 0) { + priv->p.cuarray_surfaces = av_calloc(priv->p.cuarray_num_surfaces, sizeof(*priv->p.cuarray_surfaces)); + if (!priv->p.cuarray_surfaces) + return AVERROR(ENOMEM); + + err = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); + if (err < 0) { + av_freep(&priv->p.cuarray_surfaces); + return err; + } + + for (i = 0; i < priv->p.cuarray_num_surfaces; i++) { + err = CHECK_CU(cu->cuArray3DCreate(&priv->p.cuarray_surfaces[i], &priv->p.cuarray_desc)); + if (err < 0) { + for (i = i - 1; i >= 0; i--) + CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i])); + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + + av_freep(&priv->p.cuarray_surfaces); + return err; + } + } + + err = CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + if (err < 0) + goto fail; + + av_log(ctx, AV_LOG_DEBUG, "allocated %d CUarray surfaces (%zux%zu)\n", + priv->p.cuarray_num_surfaces, priv->p.cuarray_desc.Width, priv->p.cuarray_desc.Height); + } + if (!ctx->pool) { int size = av_image_get_buffer_size(ctx->sw_format, ctx->width, ctx->height, priv->tex_alignment); - if (size < 0) - return size; + if (size < 0) { + err = size; + goto fail; + } ffhwframesctx(ctx)->pool_internal = av_buffer_pool_init2(size, ctx, cuda_pool_alloc, NULL); - if (!ffhwframesctx(ctx)->pool_internal) - return AVERROR(ENOMEM); + if (!ffhwframesctx(ctx)->pool_internal) { + err = AVERROR(ENOMEM); + goto fail; + } } return 0; + +fail: + cuda_frames_uninit(ctx); + return err; } static int cuda_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) { - CUDAFramesContext *priv = ctx->hwctx; + AVHWDeviceContext *device_ctx = ctx->device_ctx; + AVCUDADeviceContext *hwctx = device_ctx->hwctx; + CUDAFramesContext *priv = ctx->hwctx; + CudaFunctions *cu = hwctx->internal->cuda_dl; + + CUcontext dummy; int res; frame->buf[0] = av_buffer_pool_get(ctx->pool); if (!frame->buf[0]) return AVERROR(ENOMEM); - res = av_image_fill_arrays(frame->data, frame->linesize, frame->buf[0]->data, - ctx->sw_format, ctx->width, ctx->height, priv->tex_alignment); - if (res < 0) - return res; + if (ctx->format == AV_PIX_FMT_CUARRAY) { + AVCUDAArrayFrameDescriptor *desc = (AVCUDAArrayFrameDescriptor*)frame->buf[0]->data; + if (!desc) { + frame->format = ctx->format; + frame->width = ctx->width; + frame->height = ctx->height; + return 0; + } + frame->data[0] = (uint8_t*)desc->array; + frame->data[1] = (uint8_t*)desc->index; + + res = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); + if (res < 0) + return res; + + for (int i = 0; i < FF_ARRAY_ELEMS(frame->linesize); i++) { + CUDA_ARRAY3D_DESCRIPTOR plane_desc = { 0 }; + CUarray plane_array; + CUresult arr_plane_res = cu->cuArrayGetPlane(&plane_array, desc->array, i); + + if (arr_plane_res == CUDA_ERROR_INVALID_VALUE) { + if (i > 0) + break; + /* Non-planar format (e.g. UNSIGNED_INT8 x4 for packed RGB): + * cuArrayGetPlane is unsupported, query the array directly. */ + res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, desc->array)); + if (res < 0) + goto fail; + } else if (arr_plane_res != CUDA_SUCCESS) { + res = CHECK_CU(arr_plane_res); + goto fail; + } else { + res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, plane_array)); + if (res < 0) + goto fail; + } + + int elem_size = ff_cuda_cuarray_elem_size(plane_desc.Format); + if (elem_size <= 0) { + res = AVERROR_BUG; + goto fail; + } + + frame->linesize[i] = plane_desc.Width * plane_desc.NumChannels * elem_size; + + if (arr_plane_res == CUDA_ERROR_INVALID_VALUE) + break; + } - // YUV420P is a special case. - // Nvenc expects the U/V planes in swapped order from how ffmpeg expects them, also chroma is half-aligned - if (ctx->sw_format == AV_PIX_FMT_YUV420P) { - frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2; - frame->data[2] = frame->data[1]; - frame->data[1] = frame->data[2] + frame->linesize[2] * AV_CEIL_RSHIFT(ctx->height, 1); + res = CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + if (res < 0) + return res; + } else { + res = av_image_fill_arrays(frame->data, frame->linesize, frame->buf[0]->data, + ctx->sw_format, ctx->width, ctx->height, priv->tex_alignment); + if (res < 0) + return res; + + // YUV420P is a special case. + // Nvenc expects the U/V planes in swapped order from how ffmpeg expects them, also chroma is half-aligned + if (ctx->sw_format == AV_PIX_FMT_YUV420P) { + frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2; + frame->data[2] = frame->data[1]; + frame->data[1] = frame->data[2] + frame->linesize[2] * AV_CEIL_RSHIFT(ctx->height, 1); + } } - frame->format = AV_PIX_FMT_CUDA; + frame->format = ctx->format; frame->width = ctx->width; frame->height = ctx->height; return 0; + +fail: + CHECK_CU(cu->cuCtxPopCurrent(&dummy)); + return res; +} + +static enum AVPixelFormat cuda_frame_hw_format(const AVFrame *frame) +{ + if (!frame->hw_frames_ctx) + return AV_PIX_FMT_NONE; + return ((AVHWFramesContext *)frame->hw_frames_ctx->data)->format; } static int cuda_transfer_get_formats(AVHWFramesContext *ctx, @@ -248,15 +522,42 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, CUcontext dummy; int i, ret; - if ((src->hw_frames_ctx && ((AVHWFramesContext*)src->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA) || - (dst->hw_frames_ctx && ((AVHWFramesContext*)dst->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA)) - return AVERROR(ENOSYS); + { + enum AVPixelFormat src_fmt = cuda_frame_hw_format(src); + enum AVPixelFormat dst_fmt = cuda_frame_hw_format(dst); + if ((src_fmt != AV_PIX_FMT_NONE && + src_fmt != AV_PIX_FMT_CUDA && src_fmt != AV_PIX_FMT_CUARRAY) || + (dst_fmt != AV_PIX_FMT_NONE && + dst_fmt != AV_PIX_FMT_CUDA && dst_fmt != AV_PIX_FMT_CUARRAY)) + return AVERROR(ENOSYS); + } ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx)); if (ret < 0) return ret; - for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) { + /* + * Copy one plane per iteration, bounded by the AVFrame linesizes. src and + * dst always share the same sw_format (this path never converts), so they + * are either both multi-planar or both single-plane. + * + * For a CUARRAY side, cuArrayGetPlane() returns the sub-array for plane i + * of a multi-planar CUarray (NV12, P0xx, NV24, planar 4:4:4, ...). + * cuArrayGetPlane() returns CUDA_ERROR_INVALID_VALUE either when the array + * is not multi-planar or when the plane index exceeds its plane count: + * - i == 0: the array is packed (e.g. RGB), i.e. a single plane, so the + * whole array is used as that plane. + * - i > 0: the planes of a multi-planar array always have a non-zero + * linesize, so a valid plane is never skipped by the loop bound; + * getting INVALID_VALUE here means src/dst disagree on the plane count, + * which is an inconsistency and is treated as an error (handled by the + * generic cures != CUDA_SUCCESS branch) rather than silently copying a + * subset of the planes. + */ + for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->linesize[i]; i++) { + int src_is_nonplanar_cuarray = 0; + int dst_is_nonplanar_cuarray = 0; + CUDA_MEMCPY2D cpy = { .srcPitch = src->linesize[i], .dstPitch = dst->linesize[i], @@ -264,17 +565,47 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, .Height = AV_CEIL_RSHIFT(src->height, ((i == 0 || i == 3) ? 0 : priv->shift_height)), }; - if (src->hw_frames_ctx) { + if (src->format == AV_PIX_FMT_CUDA) { cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE; cpy.srcDevice = (CUdeviceptr)src->data[i]; + } else if (src->format == AV_PIX_FMT_CUARRAY) { + CUarray array; + CUresult cures = cu->cuArrayGetPlane(&array, (CUarray)src->data[0], i); + if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) { + /* Not a multi-planar array (packed format): the whole array is + * the single plane. */ + array = (CUarray)src->data[0]; + src_is_nonplanar_cuarray = 1; + } else if (cures != CUDA_SUCCESS) { + ret = CHECK_CU(cures); + goto exit; + } + + cpy.srcMemoryType = CU_MEMORYTYPE_ARRAY; + cpy.srcArray = array; } else { cpy.srcMemoryType = CU_MEMORYTYPE_HOST; cpy.srcHost = src->data[i]; } - if (dst->hw_frames_ctx) { + if (dst->format == AV_PIX_FMT_CUDA) { cpy.dstMemoryType = CU_MEMORYTYPE_DEVICE; cpy.dstDevice = (CUdeviceptr)dst->data[i]; + } else if (dst->format == AV_PIX_FMT_CUARRAY) { + CUarray array; + CUresult cures = cu->cuArrayGetPlane(&array, (CUarray)dst->data[0], i); + if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) { + /* Not a multi-planar array (packed format): the whole array is + * the single plane. */ + array = (CUarray)dst->data[0]; + dst_is_nonplanar_cuarray = 1; + } else if (cures != CUDA_SUCCESS) { + ret = CHECK_CU(cures); + goto exit; + } + + cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY; + cpy.dstArray = array; } else { cpy.dstMemoryType = CU_MEMORYTYPE_HOST; cpy.dstHost = dst->data[i]; @@ -283,6 +614,9 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream)); if (ret < 0) goto exit; + + if (src_is_nonplanar_cuarray || dst_is_nonplanar_cuarray) + break; } if (!dst->hw_frames_ctx) { @@ -294,7 +628,7 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, exit: CHECK_CU(cu->cuCtxPopCurrent(&dummy)); - return 0; + return ret; } static void cuda_device_uninit(AVHWDeviceContext *device_ctx) @@ -585,10 +919,11 @@ const HWContextType ff_hwcontext_type_cuda = { .device_uninit = cuda_device_uninit, .frames_get_constraints = cuda_frames_get_constraints, .frames_init = cuda_frames_init, + .frames_uninit = cuda_frames_uninit, .frames_get_buffer = cuda_get_buffer, .transfer_get_formats = cuda_transfer_get_formats, .transfer_data_to = cuda_transfer_data, .transfer_data_from = cuda_transfer_data, - .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, AV_PIX_FMT_NONE }, + .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, AV_PIX_FMT_CUARRAY, AV_PIX_FMT_NONE }, }; diff --git a/libavutil/hwcontext_cuda.h b/libavutil/hwcontext_cuda.h index cbad434fea..38e5369794 100644 --- a/libavutil/hwcontext_cuda.h +++ b/libavutil/hwcontext_cuda.h @@ -24,6 +24,8 @@ #include <cuda.h> #endif +#include <stdint.h> + #include "pixfmt.h" /** @@ -46,8 +48,78 @@ typedef struct AVCUDADeviceContext { } AVCUDADeviceContext; /** - * AVHWFramesContext.hwctx is currently not used + * CUDA frame descriptor for pool allocation of AV_PIX_FMT_CUARRAY frames. + * + * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs + * with the data pointer pointing at an object of this type describing the + * planes of the frame. + * + * This has no use outside of custom allocation, and AVFrame AVBufferRef do not + * necessarily point to an instance of this struct. + */ +typedef struct AVCUDAArrayFrameDescriptor { + /** + * The CUarray containing the frame data. + * + * Normally stored in AVFrame.data[0]. + */ + CUarray array; + + /** + * The index into AVCUDAFramesContext.cuarray_surfaces, or 0 if not applicable. + * + * Normally stored in AVFrame.data[1] (cast from intptr_t). + */ + intptr_t index; +} AVCUDAArrayFrameDescriptor; + +/** + * This struct is allocated as AVHWFramesContext.hwctx + */ +typedef struct AVCUDAFramesContext { + /** + * CUDA_ARRAY3D_DESCRIPTOR CUarrays will be initialized with. + * Mostly used to provide external Flags. + * + * Width, Height and Format only honored if != 0. + * Filled with default parameters from the FramesContext otherwise. + * + * Only applicable for AV_PIX_FMT_CUARRAY. + */ + CUDA_ARRAY3D_DESCRIPTOR cuarray_desc; + + /** + * If >0, pre-allocate a fixed pool of surfaces. + * The surfaces will be available via cuarray_surfaces after init. + * Size of the pool cannot be changed afterwards. + * + * Only applicable for AV_PIX_FMT_CUARRAY. + */ + int cuarray_num_surfaces; + + /** + * If cuarray_num_surfaces is >0, this contains the array of pre-allocated surfaces. + * + * Only applicable for AV_PIX_FMT_CUARRAY. + */ + CUarray *cuarray_surfaces; +} AVCUDAFramesContext; + +/** + * CUDA hardware pipeline configuration details. + * + * Passed to av_hwdevice_get_hwframe_constraints() to query + * per-hw-format constraints. When provided, valid_sw_formats + * will be filtered to only those compatible with the specified + * hw_format. */ +typedef struct AVCUDAHWConfig { + /** + * The hardware pixel format to query constraints for. + * Must be AV_PIX_FMT_CUDA or AV_PIX_FMT_CUARRAY. + */ + enum AVPixelFormat hw_format; +} AVCUDAHWConfig; /** * @defgroup hwcontext_cuda Device context creation flags diff --git a/libavutil/hwcontext_cuda_internal.h b/libavutil/hwcontext_cuda_internal.h index a989c45861..9ca6edf7d3 100644 --- a/libavutil/hwcontext_cuda_internal.h +++ b/libavutil/hwcontext_cuda_internal.h @@ -35,4 +35,25 @@ struct AVCUDADeviceContextInternal { int flags; }; +/** + * Return the element size in bytes for a CUarray_format, or 0 for unknown. + * + * Used to compute frame->linesize[] for CUarray frames (block-linear). + * Callers that need a fallback should treat a 0 return as an error. + */ +static inline int ff_cuda_cuarray_elem_size(CUarray_format fmt) +{ + switch (fmt) { + case CU_AD_FORMAT_UNSIGNED_INT8: + case CU_AD_FORMAT_SIGNED_INT8: return 1; + case CU_AD_FORMAT_UNSIGNED_INT16: + case CU_AD_FORMAT_SIGNED_INT16: + case CU_AD_FORMAT_HALF: return 2; + case CU_AD_FORMAT_UNSIGNED_INT32: + case CU_AD_FORMAT_SIGNED_INT32: + case CU_AD_FORMAT_FLOAT: return 4; + default: return 0; + } +} + #endif /* AVUTIL_HWCONTEXT_CUDA_INTERNAL_H */ diff --git a/libavutil/version.h b/libavutil/version.h index 7ccaa6df1b..d5bf20cf89 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 61 -#define LIBAVUTIL_VERSION_MINOR 4 +#define LIBAVUTIL_VERSION_MINOR 5 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
