This is an automated email from the git hooks/post-receive script.
Git pushed a commit to branch master
in repository ffmpeg.
The following commit(s) were added to refs/heads/master by this push:
new 2cf3f4d64d avutil/hwcontext_cuda: remove format allowlist, accept any
pixel format
2cf3f4d64d is described below
commit 2cf3f4d64de0efa5ccb4021f7245e93b041dbd9e
Author: Philip Langdale <[email protected]>
AuthorDate: Wed Jul 15 12:56:26 2026 -0700
Commit: philipl <[email protected]>
CommitDate: Wed Jul 22 21:42:40 2026 +0000
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, non-palette format. cuda_frames_init()
validates with av_pix_fmt_desc_get() instead of checking against the
allowlist.
Palette formats have a special-cased use of plane[1] for the palette itself
and it's not worth the hassle of supporting in an otherwise clean generic
copy loop.
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 | 87 +++++++---------
libavutil/tests/.gitignore | 1 +
libavutil/tests/hwcontext_cuda.c | 215 +++++++++++++++++++++++++++++++++++++++
tests/fate/hw.mak | 5 +
5 files changed, 258 insertions(+), 51 deletions(-)
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 a057c36b20..b38573b04b 100644
--- a/libavutil/hwcontext_cuda.c
+++ b/libavutil/hwcontext_cuda.c
@@ -44,37 +44,7 @@ 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_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,
- 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)
@@ -86,20 +56,7 @@ static int cuda_frames_get_constraints(AVHWDeviceContext
*ctx,
{
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++) {
- 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;
+ int n = 0;
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));
@@ -122,6 +79,31 @@ static int cuda_frames_get_constraints(AVHWDeviceContext
*ctx,
#endif
}
+ constraints->valid_sw_formats = av_malloc_array(AV_PIX_FMT_NB + 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++) {
+ if (req_fmt == AV_PIX_FMT_CUARRAY) {
+ if (cuda_array_format_for_pix_fmt(i))
+ constraints->valid_sw_formats[n++] = i;
+ } else {
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(i);
+ /* Palette formats carry their palette in a separate zero-linesize
+ * data[1] plane that the transfer path does not handle, so exclude
+ * them along with the hwaccel formats. */
+ if (desc && !(desc->flags & (AV_PIX_FMT_FLAG_HWACCEL |
AV_PIX_FMT_FLAG_PAL)))
+ constraints->valid_sw_formats[n++] = i;
+ }
+ }
+#if CONFIG_VULKAN
+ if (req_fmt != AV_PIX_FMT_CUARRAY)
+ constraints->valid_sw_formats[n++] = AV_PIX_FMT_VULKAN;
+#endif
+ constraints->valid_sw_formats[n] = AV_PIX_FMT_NONE;
+
return 0;
}
@@ -290,15 +272,18 @@ static int cuda_frames_init(AVHWFramesContext *ctx)
AVCUDADeviceContext *hwctx = device_ctx->hwctx;
CUDAFramesContext *priv = ctx->hwctx;
CudaFunctions *cu = hwctx->internal->cuda_dl;
+ const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(ctx->sw_format);
int err, i;
- for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
- if (ctx->sw_format == supported_formats[i])
- break;
+ if (!desc) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid pixel format\n");
+ return AVERROR(EINVAL);
}
- 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));
+
+ /* Palette formats keep their palette in a separate zero-linesize plane
+ * that the transfer path does not copy, so they are not supported. */
+ if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
+ av_log(ctx, AV_LOG_ERROR, "Palette formats are not supported\n");
return AVERROR(ENOSYS);
}
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..e4dec7ec05
--- /dev/null
+++ b/libavutil/tests/hwcontext_cuda.c
@@ -0,0 +1,215 @@
+/*
+ * 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/log.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;
+}
+
+/* Verify that an unsupported sw_format is refused at init time. Returns 0 when
+ * the format is correctly rejected, and an error when it is wrongly accepted.
*/
+static int test_rejected(AVBufferRef *device_ref, enum AVPixelFormat fmt)
+{
+ AVBufferRef *frames_ref = av_hwframe_ctx_alloc(device_ref);
+ AVHWFramesContext *hwfc;
+ int ret;
+
+ if (!frames_ref)
+ return AVERROR(ENOMEM);
+
+ hwfc = (AVHWFramesContext *)frames_ref->data;
+ hwfc->format = AV_PIX_FMT_CUDA;
+ hwfc->sw_format = fmt;
+ hwfc->width = 64;
+ hwfc->height = 64;
+
+ /* Init is expected to fail; silence the resulting error log. */
+ av_log_set_level(AV_LOG_QUIET);
+ ret = av_hwframe_ctx_init(frames_ref);
+ av_log_set_level(AV_LOG_INFO);
+
+ av_buffer_unref(&frames_ref);
+
+ return ret >= 0 ? AVERROR(EINVAL) : 0;
+}
+
+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) {
+ /* CONFIG_CUDA only requires the ffnvcodec headers and a dynamic
+ * loader; a FATE host that compiled CUDA support need not have an
+ * NVIDIA GPU or usable driver. Skip cleanly in that case so that
+ * make fate does not fail for an unavailable test environment. A real
+ * transfer mismatch below still returns a nonzero exit status. */
+ printf("No CUDA device available, skipping.\n");
+ return 0;
+ }
+
+ 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++;
+ /* Palette formats must be refused rather than silently dropping the
+ * palette across a transfer; every other format must round-trip. */
+ if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
+ if (test_rejected(device_ref, fmt) < 0) {
+ printf("fail: %-16s wrongly accepted\n",
+ av_get_pix_fmt_name(fmt));
+ failures++;
+ }
+ } else {
+ 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)
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]