PR #23727 opened by philipl
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23727
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23727.patch

This set of changes fixes gaps and regressions in cuda vulkan interop. 
Particularly, it fixes transfer of packed formats, which were simply broken 
before, and properly counts the number of semaphores when a vulkan image is 
imported into cuda (it's the number of images, not the number of planes).

There are still some additional behaviours that break otherwise valid interop 
scenarios when using filters, but I'll create separate PRs for those.


>From b60d7ab4a0879a77e89bdf6fa65443b5662bd5ef Mon Sep 17 00:00:00 2001
From: Philip Langdale <[email protected]>
Date: Mon, 6 Jul 2026 09:24:21 -0700
Subject: [PATCH 1/3] avutil/hwcontext_vulkan: fix CUDA interop for packed
 formats

The current export_to_cuda logic only works for planar and semi-planar formats.
When presented with a single plane packed format, it will incorrectly
calculate the number of channels, resulting in failures later on when cuda
code tries to access the frame. Let's fix it.
---
 libavutil/hwcontext_vulkan.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 20b6ed46f8..02ad0cef65 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -3871,6 +3871,15 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
     CudaFunctions *cu = cu_internal->cuda_dl;
     CUarray_format cufmt = desc->comp[0].depth > 8 ? 
CU_AD_FORMAT_UNSIGNED_INT16 :
                                                      
CU_AD_FORMAT_UNSIGNED_INT8;
+    int elem_size;
+
+    switch (cufmt) {
+    case CU_AD_FORMAT_UNSIGNED_INT8:  elem_size = 1; break;
+    case CU_AD_FORMAT_UNSIGNED_INT16: elem_size = 2; break;
+    default:
+        av_log(ctx, AV_LOG_ERROR, "Unsupported CUDA array format %d!\n", 
cufmt);
+        return AVERROR(ENOSYS);
+    }
 
     dst_f = (AVVkFrame *)frame->data[0];
     dst_int = dst_f->internal;
@@ -3915,7 +3924,7 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
                 .arrayDesc = {
                     .Depth = 0,
                     .Format = cufmt,
-                    .NumChannels = 1 + ((planes == 2) && i),
+                    .NumChannels = desc->comp[i].step / elem_size,
                     .Flags = 0,
                 },
                 .numLevels = 1,
-- 
2.52.0


>From 29d95efd8ef35549eee25c8a9f955a9c3054a914 Mon Sep 17 00:00:00 2001
From: Philip Langdale <[email protected]>
Date: Mon, 6 Jul 2026 10:04:10 -0700
Subject: [PATCH 2/3] avutil/hwcontext_vulkan: CUDA interop semaphores are per
 image

The current semaphore logic dates back to a time where we did not have any
multiplane images, and it has not kept up. As a result, we currently try and
manipulate too many semaphores when dealing with multiplane images, leading to
errors and crashes. Let's fix it.
---
 libavutil/hwcontext_vulkan.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 02ad0cef65..b10b6cfa3c 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -3971,6 +3971,7 @@ static int 
vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
     VulkanFramesPriv *fp = hwfc->hwctx;
     const int planes = av_pix_fmt_count_planes(hwfc->sw_format);
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(hwfc->sw_format);
+    int nb_images;
 
     AVHWFramesContext *cuda_fc = (AVHWFramesContext*)src->hw_frames_ctx->data;
     AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx;
@@ -3981,6 +3982,7 @@ static int 
vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
     CUDA_EXTERNAL_SEMAPHORE_SIGNAL_PARAMS s_s_par[AV_NUM_DATA_POINTERS] = { 0 
};
 
     dst_f = (AVVkFrame *)dst->data[0];
+    nb_images = ff_vk_count_images(dst_f);
 
     err = prepare_frame(hwfc, &fp->upload_exec, dst_f, 
PREP_MODE_EXTERNAL_EXPORT);
     if (err < 0)
@@ -3998,13 +4000,13 @@ static int 
vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
 
     dst_int = dst_f->internal;
 
-    for (int i = 0; i < planes; i++) {
+    for (int i = 0; i < nb_images; i++) {
         s_w_par[i].params.fence.value = dst_f->sem_value[i] + 0;
         s_s_par[i].params.fence.value = dst_f->sem_value[i] + 1;
     }
 
     err = CHECK_CU(cu->cuWaitExternalSemaphoresAsync(dst_int->cu_sem, s_w_par,
-                                                     planes, 
cuda_dev->stream));
+                                                     nb_images, 
cuda_dev->stream));
     if (err < 0)
         goto fail;
 
@@ -4031,11 +4033,11 @@ static int 
vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
     }
 
     err = CHECK_CU(cu->cuSignalExternalSemaphoresAsync(dst_int->cu_sem, 
s_s_par,
-                                                       planes, 
cuda_dev->stream));
+                                                       nb_images, 
cuda_dev->stream));
     if (err < 0)
         goto fail;
 
-    for (int i = 0; i < planes; i++)
+    for (int i = 0; i < nb_images; i++)
         dst_f->sem_value[i]++;
 
     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
@@ -4886,7 +4888,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext 
*hwfc, AVFrame *dst,
 
     dst_int = dst_f->internal;
 
-    for (int i = 0; i < planes; i++) {
+    for (int i = 0; i < nb_images; i++) {
         s_w_par[i].params.fence.value = dst_f->sem_value[i] + 0;
         s_s_par[i].params.fence.value = dst_f->sem_value[i] + 1;
     }
@@ -4923,7 +4925,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext 
*hwfc, AVFrame *dst,
     if (err < 0)
         goto fail;
 
-    for (int i = 0; i < planes; i++)
+    for (int i = 0; i < nb_images; i++)
         dst_f->sem_value[i]++;
 
     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
-- 
2.52.0


>From 0c49447bae7d66fdfa4868a141e6014f94f41a20 Mon Sep 17 00:00:00 2001
From: Philip Langdale <[email protected]>
Date: Mon, 6 Jul 2026 12:36:02 -0700
Subject: [PATCH 3/3] avutil/hwcontext_vulkan: reject unsupported semi-planar
 CUDA imports

Cuda currently only supports packed and single-component planar formats, but
fails to import semi-planer (eg: NV12, P010). Even though there are now
semi-planar cuda array formats, which the latest nvdec can use, these formats
are not used when mapping Vulkan imports. Maybe they'll fix that some day.

But until then, let's explicitly detect the case and return a clear error
message for the user.

Exercising this error path revealed that vulkan_free_internal() frees
f->internal on a transfer error and then runs again when the frame is
destroyed, dereferencing the freed pointer; let's make it idempotent.
---
 libavutil/hwcontext_vulkan.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index b10b6cfa3c..6564bad852 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -2309,6 +2309,10 @@ static void vulkan_free_internal(VulkanDevicePriv *p, 
AVVkFrame *f)
 {
     av_unused AVVkFrameInternal *internal = f->internal;
 
+    // Make this function safe to call repeatedly
+    if (!internal)
+        return;
+
 #if CONFIG_CUDA
     if (internal->cuda_fc_ref) {
         AVHWFramesContext *cuda_fc = (AVHWFramesContext 
*)internal->cuda_fc_ref->data;
@@ -3906,6 +3910,18 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
 
         if (nb_images != planes) {
             for (int i = 0; i < planes; i++) {
+                /* Cuda now defines array formats for semi-planar, but these 
are
+                 * not currently supported for imported Vulkan images. */
+                if (desc->comp[i].step / elem_size > 1) {
+                    av_log(ctx, AV_LOG_ERROR,
+                           "Cannot map a multiplane Vulkan image (%d image(s) "
+                           "for %d plane(s)) to CUDA; create the Vulkan device 
"
+                           "with the disable_multiplane=1 option (one image 
per "
+                           "plane) for CUDA interop.\n", nb_images, planes);
+                    err = AVERROR(ENOSYS);
+                    goto fail;
+                }
+
                 VkImageSubresource subres = {
                     .aspectMask = i == 2 ? 
VK_IMAGE_ASPECT_MEMORY_PLANE_2_BIT_EXT :
                                   i == 1 ? 
VK_IMAGE_ASPECT_MEMORY_PLANE_1_BIT_EXT :
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to