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 <ddesouza@nvidia.com>
Signed-off-by: Diego de Souza <ddesouza@nvidia.com>
This commit is contained in:
Timo Rothenpieler
2026-03-16 16:11:32 +01:00
parent c7546ce0ea
commit ef3dcf4ea6
5 changed files with 470 additions and 39 deletions

View File

@@ -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.

View File

@@ -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] = AV_PIX_FMT_CUDA;
constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
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_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;
// 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->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;
}
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 },
};

View File

@@ -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

View File

@@ -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 */

View File

@@ -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, \