PR #23821 opened by philipl URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23821 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23821.patch
Remove the static supported_formats[] allowlist from hwcontext_cuda. cuda_frames_get_constraints() now iterates all registered pixel formats and includes every non-hwaccel format. cuda_frames_init() validates with av_pix_fmt_desc_get() instead of checking against the allowlist. Also add a roundtrip test that uploads a deterministic byte pattern to CUDA, downloads it back, and verifies the data match. This tests all non-hwaccel pixel formats. The test is gated on CONFIG_CUDA. >From 1885faa1a93f30634513dfdbe33f5fd4ea7314d6 Mon Sep 17 00:00:00 2001 From: Philip Langdale <[email protected]> Date: Wed, 15 Jul 2026 12:56:26 -0700 Subject: [PATCH] avutil/hwcontext_cuda: remove format allowlist, accept any pixel format Remove the static supported_formats[] allowlist from hwcontext_cuda. cuda_frames_get_constraints() now iterates all registered pixel formats and includes every non-hwaccel format. cuda_frames_init() validates with av_pix_fmt_desc_get() instead of checking against the allowlist. Also add a roundtrip test that uploads a deterministic byte pattern to CUDA, downloads it back, and verifies the data match. This tests all non-hwaccel pixel formats. The test is gated on CONFIG_CUDA. --- libavutil/Makefile | 1 + libavutil/hwcontext_cuda.c | 78 ++++++-------- libavutil/tests/.gitignore | 1 + libavutil/tests/hwcontext_cuda.c | 172 +++++++++++++++++++++++++++++++ tests/fate/hw.mak | 5 + 5 files changed, 210 insertions(+), 47 deletions(-) create mode 100644 libavutil/tests/hwcontext_cuda.c diff --git a/libavutil/Makefile b/libavutil/Makefile index c3cadf5c2f..9cb3108b38 100644 --- a/libavutil/Makefile +++ b/libavutil/Makefile @@ -325,6 +325,7 @@ TESTPROGS = adler32 \ xtea \ tea \ +TESTPROGS-$(CONFIG_CUDA) += hwcontext_cuda TESTPROGS-$(HAVE_THREADS) += cpu_init TESTPROGS-$(HAVE_LZO1X_999_COMPRESS) += lzo diff --git a/libavutil/hwcontext_cuda.c b/libavutil/hwcontext_cuda.c index a171a9e139..cad7069eda 100644 --- a/libavutil/hwcontext_cuda.c +++ b/libavutil/hwcontext_cuda.c @@ -40,50 +40,13 @@ typedef struct CUDADeviceContext { AVCUDADeviceContextInternal internal; } CUDADeviceContext; -static const enum AVPixelFormat supported_formats[] = { - AV_PIX_FMT_NV12, - AV_PIX_FMT_NV16, - AV_PIX_FMT_YUV420P, - AV_PIX_FMT_YUVA420P, - AV_PIX_FMT_YUV444P, - 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_YUV422P, - AV_PIX_FMT_YUV420P10, - AV_PIX_FMT_YUV422P10, - AV_PIX_FMT_YUV444P10, - AV_PIX_FMT_YUV444P10MSB, - AV_PIX_FMT_YUV444P12MSB, - AV_PIX_FMT_YUV444P16, - AV_PIX_FMT_0RGB32, - AV_PIX_FMT_0BGR32, - AV_PIX_FMT_RGB32, - AV_PIX_FMT_BGR32, -#if CONFIG_VULKAN - AV_PIX_FMT_VULKAN, -#endif -}; - #define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x) static int cuda_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints) { - int i; - - 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; + int n = 0; constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); if (!constraints->valid_hw_formats) @@ -92,6 +55,32 @@ static int cuda_frames_get_constraints(AVHWDeviceContext *ctx, constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA; constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; + for (int i = 0; i < AV_PIX_FMT_NB; i++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i); + if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + n++; + } + +#if CONFIG_VULKAN + n++; +#endif + + constraints->valid_sw_formats = av_malloc_array(n + 1, + sizeof(*constraints->valid_sw_formats)); + if (!constraints->valid_sw_formats) + return AVERROR(ENOMEM); + + n = 0; + for (int i = 0; i < AV_PIX_FMT_NB; i++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i); + if (desc && !(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + constraints->valid_sw_formats[n++] = i; + } +#if CONFIG_VULKAN + constraints->valid_sw_formats[n++] = AV_PIX_FMT_VULKAN; +#endif + constraints->valid_sw_formats[n] = AV_PIX_FMT_NONE; + return 0; } @@ -148,16 +137,11 @@ static int cuda_frames_init(AVHWFramesContext *ctx) AVCUDADeviceContext *hwctx = device_ctx->hwctx; CUDAFramesContext *priv = ctx->hwctx; CudaFunctions *cu = hwctx->internal->cuda_dl; - int err, i; + int err; - for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { - if (ctx->sw_format == supported_formats[i]) - break; - } - if (i == FF_ARRAY_ELEMS(supported_formats)) { - av_log(ctx, AV_LOG_ERROR, "Pixel format '%s' is not supported\n", - av_get_pix_fmt_name(ctx->sw_format)); - return AVERROR(ENOSYS); + if (!av_pix_fmt_desc_get(ctx->sw_format)) { + av_log(ctx, AV_LOG_ERROR, "Invalid pixel format\n"); + return AVERROR(EINVAL); } err = CHECK_CU(cu->cuDeviceGetAttribute(&priv->tex_alignment, diff --git a/libavutil/tests/.gitignore b/libavutil/tests/.gitignore index 04043494ec..0f43921ed6 100644 --- a/libavutil/tests/.gitignore +++ b/libavutil/tests/.gitignore @@ -31,6 +31,7 @@ /hash /hdr_dynamic_vivid_metadata /hmac +/hwcontext_cuda /hwdevice /imgutils /integer diff --git a/libavutil/tests/hwcontext_cuda.c b/libavutil/tests/hwcontext_cuda.c new file mode 100644 index 0000000000..db9274b8a2 --- /dev/null +++ b/libavutil/tests/hwcontext_cuda.c @@ -0,0 +1,172 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <stdio.h> +#include <string.h> + +#include "libavutil/hwcontext.h" +#include "libavutil/imgutils.h" +#include "libavutil/mem.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" + +static int test_format(AVBufferRef *device_ref, enum AVPixelFormat fmt) +{ + AVBufferRef *frames_ref = NULL; + AVHWFramesContext *hwfc; + AVFrame *sw_frame = NULL, *hw_frame = NULL, *download = NULL; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + int ret; + + frames_ref = av_hwframe_ctx_alloc(device_ref); + if (!frames_ref) { + ret = AVERROR(ENOMEM); + goto fail; + } + + hwfc = (AVHWFramesContext *)frames_ref->data; + hwfc->format = AV_PIX_FMT_CUDA; + hwfc->sw_format = fmt; + hwfc->width = 64; + hwfc->height = 64; + + ret = av_hwframe_ctx_init(frames_ref); + if (ret < 0) + goto fail; + + sw_frame = av_frame_alloc(); + hw_frame = av_frame_alloc(); + download = av_frame_alloc(); + if (!sw_frame || !hw_frame || !download) { + ret = AVERROR(ENOMEM); + goto fail; + } + + sw_frame->format = fmt; + sw_frame->width = 64; + sw_frame->height = 64; + ret = av_frame_get_buffer(sw_frame, 0); + if (ret < 0) + goto fail; + + { + int linesizes[4]; + av_image_fill_linesizes(linesizes, fmt, 64); + + for (int i = 0; i < FF_ARRAY_ELEMS(sw_frame->data) && sw_frame->data[i]; i++) { + int shift = (i == 1 || i == 2) && desc ? desc->log2_chroma_h : 0; + int h = AV_CEIL_RSHIFT(64, shift); + + for (int y = 0; y < h; y++) + for (int x = 0; x < linesizes[i]; x++) + sw_frame->data[i][y * sw_frame->linesize[i] + x] = + (uint8_t)(x + y * 3 + i * 17); + } + } + + hw_frame->hw_frames_ctx = av_buffer_ref(frames_ref); + if (!hw_frame->hw_frames_ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = av_hwframe_get_buffer(frames_ref, hw_frame, 0); + if (ret < 0) + goto fail; + + ret = av_hwframe_transfer_data(hw_frame, sw_frame, 0); + if (ret < 0) + goto fail; + + download->format = fmt; + download->width = 64; + download->height = 64; + ret = av_frame_get_buffer(download, 0); + if (ret < 0) + goto fail; + + ret = av_hwframe_transfer_data(download, hw_frame, 0); + if (ret < 0) + goto fail; + + { + int linesizes[4]; + av_image_fill_linesizes(linesizes, fmt, 64); + + for (int i = 0; i < FF_ARRAY_ELEMS(download->data) && download->data[i]; i++) { + int shift = (i == 1 || i == 2) && desc ? desc->log2_chroma_h : 0; + int h = AV_CEIL_RSHIFT(64, shift); + + for (int y = 0; y < h; y++) { + int off = y * FFMIN(sw_frame->linesize[i], download->linesize[i]); + if (memcmp(sw_frame->data[i] + off, + download->data[i] + off, + linesizes[i])) { + printf("fail: %-16s plane %d row %d mismatch\n", + av_get_pix_fmt_name(fmt), i, y); + ret = AVERROR(EINVAL); + goto fail; + } + } + } + } + +fail: + av_frame_free(&sw_frame); + av_frame_free(&hw_frame); + av_frame_free(&download); + av_buffer_unref(&frames_ref); + return ret; +} + +int main(void) +{ + AVBufferRef *device_ref = NULL; + enum AVPixelFormat fmt; + int ret, failures = 0, total = 0; + + ret = av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_CUDA, NULL, NULL, 0); + if (ret < 0) { + printf("No CUDA device available.\n"); + return 1; + } + + for (fmt = 0; fmt < AV_PIX_FMT_NB; fmt++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt); + if (!desc || (desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + continue; + + total++; + ret = test_format(device_ref, fmt); + if (ret < 0) { + printf("fail: %-16s %s\n", + av_get_pix_fmt_name(fmt), + av_err2str(ret)); + failures++; + } + } + + av_buffer_unref(&device_ref); + + if (failures) + printf("%d / %d tests failed.\n", failures, total); + else + printf("%d tests passed.\n", total); + + return !!failures; +} diff --git a/tests/fate/hw.mak b/tests/fate/hw.mak index 0f126892fd..ea8bdf4dc4 100644 --- a/tests/fate/hw.mak +++ b/tests/fate/hw.mak @@ -3,4 +3,9 @@ fate-hwdevice: libavutil/tests/hwdevice$(EXESUF) fate-hwdevice: CMD = run libavutil/tests/hwdevice$(EXESUF) fate-hwdevice: CMP = null +FATE_HW-$(CONFIG_CUDA) += fate-hwcontext-cuda +fate-hwcontext-cuda: libavutil/tests/hwcontext_cuda$(EXESUF) +fate-hwcontext-cuda: CMD = run libavutil/tests/hwcontext_cuda$(EXESUF) +fate-hwcontext-cuda: CMP = null + FATE_HW-$(CONFIG_AVUTIL) += $(FATE_HWCONTEXT) -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
