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]

Reply via email to