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]

Reply via email to