public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Dominik 'Rathann' Mierzejewski <dominik@greysector.net>
To: git-commits@fedoraproject.org
Subject: [rpms/ffmpeg] rawhide: Fix Firefox vulkan direct-export rendering issue
Date: Thu, 30 Jul 2026 13:21:56 GMT	[thread overview]
Message-ID: <178541771601.1.18187437852910736297.rpms-ffmpeg-b59068d371bc@fedoraproject.org> (raw)

            A new commit has been pushed.

            Repo   : rpms/ffmpeg
            Branch : rawhide
            Commit : b59068d371bc8503f129fbaf0cc5166d88deba20
            Author : Dominik 'Rathann' Mierzejewski <dominik@greysector.net>
            Date   : 2026-07-29T20:25:03+02:00
            Stats  : +542/-0 in 2 file(s)
            URL    : https://src.fedoraproject.org/rpms/ffmpeg/c/b59068d371bc8503f129fbaf0cc5166d88deba20?branch=rawhide

            Log:
            Fix Firefox vulkan direct-export rendering issue

See: https://bugzilla.mozilla.org/show_bug.cgi?id=2048591
     https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/23932

---
diff --git a/ffmpeg-vulkan-direct-export.patch b/ffmpeg-vulkan-direct-export.patch
new file mode 100644
index 0000000..71b26b8
--- /dev/null
+++ b/ffmpeg-vulkan-direct-export.patch
@@ -0,0 +1,539 @@
+From 25e187f8494966377a4b9d077260ce7b501a911c Mon Sep 17 00:00:00 2001
+From: Tymur Boiko <tboiko@nvidia.com>
+Date: Tue, 27 Jan 2026 18:16:33 +0100
+Subject: [PATCH] vulkan: fix DRM map, decode barriers, and video frame setup
+ for modifier output
+
+When mapping Vulkan Video frames to DMA-BUF, synchronize using an exportable
+binary semaphore and sync_fd where supported. Submit a lightweight exec that
+waits on each plane's timeline semaphore at the current value, signals a
+SYNC_FD-exportable binary semaphore, then export with vkGetSemaphoreFdKHR.
+Store that binary semaphore in AVVkFrameInternal and reuse it across maps
+instead of creating and destroying each time: for
+VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT, copy transference means a
+successful vkGetSemaphoreFdKHR unsignals the semaphore like a wait, so it can
+be signaled again on the next map submit. If export is unavailable, fall back
+to vkWaitSemaphores.
+
+Moved drm_sync_sem destroy to vulkan_free_internal
+
+Export dma-buf fds with GetMemoryFdKHR for each populated f->mem[i], iterating
+up to the sw_format plane count instead of stopping at the image count, so
+multi-memory bindings are not skipped. Describe DRM layers using
+max(sw planes, image count) and query subresource layout with the correct
+aspect and image index when one VkImage backs multiple planes. Reference the
+source hw_frames_ctx on the mapped frame and close dma-buf fds on failure paths.
+
+For DMA-BUF-capable pools, honor VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT
+from format export queries when binding memory. With DRM modifiers and a
+video profile in create_pnext, preserve caller usage and image flags instead of
+overwriting them from generic supported_usage probing; use the modifier list
+create info when probing export flags for modifier tiling.
+
+Include VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR from the output frames
+context's usage together with DST (fixes
+VUID-VkVideoBeginCodingInfoKHR-slotIndex-07245) instead of adding DPB usage
+only when !is_current.
+
+In ff_vk_decode_add_slice, pass VkVideoProfileListInfoKHR (from the output
+frames context's create_pnext) as the pNext argument to
+ff_vk_get_pooled_buffer instead of the full create_pnext chain. In
+ff_vk_frame_params, set tiling to OPTIMAL only when it is not already
+DRM_FORMAT_MODIFIER_EXT. In ff_vk_decode_init, when the output pool's
+create_pnext includes VkImageDrmFormatModifierListCreateInfoEXT, initialize the
+DPB pool with that modifier-list pNext and DRM_FORMAT_MODIFIER_EXT tiling;
+otherwise use VkVideoProfileListInfoKHR and OPTIMAL as before. When
+VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR is unset, the output
+and DPB pools cannot use different layouts or tiling, so the DPB pool must
+match the output pool.
+
+Also fix av_hwframe_map ioctl sync_fd export, multi-planar semaphore handling,
+and related failure-path cleanup.
+
+Signed-off-by: Tymur Boiko <tboiko@nvidia.com>
+---
+ libavcodec/vulkan_decode.c   |  36 +++++--
+ libavutil/hwcontext_vulkan.c | 194 ++++++++++++++++++++++++++++++-----
+ 2 files changed, 201 insertions(+), 29 deletions(-)
+
+diff --git a/libavcodec/vulkan_decode.c b/libavcodec/vulkan_decode.c
+index 9ab8d45aa9..afca62b304 100644
+--- a/libavcodec/vulkan_decode.c
++++ b/libavcodec/vulkan_decode.c
+@@ -233,7 +233,8 @@ int ff_vk_decode_prepare_frame(FFVulkanDecodeContext *dec, AVFrame *pic,
+                                 (AVVkFrame *)pic->data[0],
+                                 hwfc->format[0],
+                                 VK_IMAGE_USAGE_VIDEO_DECODE_DST_BIT_KHR |
+-                                (!is_current ? VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR : 0));
++                                (hwfc->usage & VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR));
++                                // the above fixes VUID-VkVideoBeginCodingInfoKHR-slotIndex-07245
+         if (err < 0)
+             return err;
+ 
+@@ -335,12 +336,21 @@ int ff_vk_decode_add_slice(AVCodecContext *avctx, FFVulkanDecodePicture *vp,
+          * easier, and gives us ample headroom. */
+         buf_size = 2 << av_log2(buf_size);
+ 
++        /* When the frames context uses DRM modifier tiling,
++         * hwctx->create_pnext contains VkImageDrmFormatModifierListCreateInfoEXT,
++         * which per spec does not extend VkBufferCreateInfo, so we need to find
++         * the VkVideoProfileListInfoKHR structure within it. */
++        void *buf_pnext = ctx->s.hwfc->create_pnext;
++        if (ctx->s.hwfc->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT)
++            buf_pnext = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
++                                                  VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
++
+         err = ff_vk_get_pooled_buffer(&ctx->s, &ctx->buf_pool, &new_ref,
+                                       DECODER_IS_SDR(avctx->codec_id) ?
+                                       (VK_BUFFER_USAGE_STORAGE_BUFFER_BIT |
+                                        VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT) :
+                                       VK_BUFFER_USAGE_VIDEO_DECODE_SRC_BIT_KHR,
+-                                      ctx->s.hwfc->create_pnext, buf_size,
++                                      buf_pnext, buf_size,
+                                       VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
+                                       (DECODER_IS_SDR(avctx->codec_id) ?
+                                        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT : 0x0));
+@@ -1217,7 +1227,9 @@ int ff_vk_frame_params(AVCodecContext *avctx, AVBufferRef *hw_frames_ctx)
+         }
+     }
+ 
+-    hwfc->tiling = VK_IMAGE_TILING_OPTIMAL;
++    if (hwfc->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT)
++        hwfc->tiling = VK_IMAGE_TILING_OPTIMAL;
++
+     hwfc->usage  = VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+                    VK_IMAGE_USAGE_STORAGE_BIT      |
+                    VK_IMAGE_USAGE_SAMPLED_BIT;
+@@ -1382,10 +1394,22 @@ int ff_vk_decode_init(AVCodecContext *avctx)
+         dpb_frames->height    = s->frames->height;
+ 
+         dpb_hwfc = dpb_frames->hwctx;
+-        dpb_hwfc->create_pnext = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
+-                                                           VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
++        void *profile_list = (void *)ff_vk_find_struct(ctx->s.hwfc->create_pnext,
++                                                       VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR);
++        /* Reference (DPB) images use the same tiling and pNext chain as output.
++         * If VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_DISTINCT_BIT_KHR is 0, the
++         * driver does not support separate output and DPB with different layouts/tiling. */
++        void *drm_create_pnext = ff_vk_find_struct(ctx->s.hwfc->create_pnext,
++                                                   VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_LIST_CREATE_INFO_EXT);
++        if (drm_create_pnext) {
++            dpb_hwfc->create_pnext = drm_create_pnext;
++            dpb_hwfc->tiling       = VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT;
++            av_assert2(ff_vk_find_struct(drm_create_pnext, VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR));
++        } else {
++            dpb_hwfc->create_pnext = profile_list;
++            dpb_hwfc->tiling       = VK_IMAGE_TILING_OPTIMAL;
++        }
+         dpb_hwfc->format[0]    = s->hwfc->format[0];
+-        dpb_hwfc->tiling       = VK_IMAGE_TILING_OPTIMAL;
+         dpb_hwfc->usage        = VK_IMAGE_USAGE_VIDEO_DECODE_DPB_BIT_KHR;
+ 
+         if (ctx->common.layered_dpb)
+diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
+index bdada78c2b..888cc39530 100644
+--- a/libavutil/hwcontext_vulkan.c
++++ b/libavutil/hwcontext_vulkan.c
+@@ -190,11 +190,18 @@ typedef struct VulkanFramesPriv {
+ 
+     /* Properties for DRM modifier for each plane in the image */
+     VkDrmFormatModifierPropertiesEXT drm_format_modifier_properties[5];
++
++    /* Set when physical device reports DEDICATED_ONLY for DMA-BUF export (try_export_flags) */
++    int export_requires_dedicated;
+ } VulkanFramesPriv;
+ 
+ typedef struct AVVkFrameInternal {
+     pthread_mutex_t update_mutex;
+ 
++    /* Binary semaphore for SYNC_FD export at DRM map time. Created once lazily,
++     * re-signaled each time via a submit in vulkan_map_to_drm. */
++    VkSemaphore drm_sync_sem;
++
+ #if CONFIG_CUDA
+     /* Importing external memory into cuda is really expensive so we keep the
+      * memory imported all the time */
+@@ -2369,7 +2376,7 @@ static int alloc_mem(AVHWDeviceContext *ctx, VkMemoryRequirements *req,
+     return 0;
+ }
+ 
+-static void vulkan_free_internal(AVVkFrame *f)
++static void vulkan_free_internal(VulkanDevicePriv *p, AVVkFrame *f)
+ {
+     av_unused AVVkFrameInternal *internal = f->internal;
+ 
+@@ -2401,6 +2408,10 @@ static void vulkan_free_internal(AVVkFrame *f)
+     }
+ #endif
+ 
++    if (internal->drm_sync_sem != VK_NULL_HANDLE)
++        p->vkctx.vkfn.DestroySemaphore(p->p.act_dev, internal->drm_sync_sem,
++                                        p->p.alloc);
++
+     pthread_mutex_destroy(&internal->update_mutex);
+     av_freep(&f->internal);
+ }
+@@ -2428,7 +2439,7 @@ static void vulkan_frame_free(AVHWFramesContext *hwfc, AVVkFrame *f)
+         vk->WaitSemaphores(hwctx->act_dev, &sem_wait, UINT64_MAX);
+     }
+ 
+-    vulkan_free_internal(f);
++    vulkan_free_internal(p, f);
+ 
+     for (int i = 0; i < nb_images; i++) {
+         vk->DestroyImage(hwctx->act_dev,     f->img[i], hwctx->alloc);
+@@ -2475,6 +2486,10 @@ static int alloc_bind_mem(AVHWFramesContext *hwfc, AVVkFrame *f,
+ 
+         vk->GetImageMemoryRequirements2(hwctx->act_dev, &req_desc, &req);
+ 
++        av_log(hwfc, AV_LOG_TRACE,
++               "plane %d: driver reports prefersDedicatedAllocation=%i requiresDedicatedAllocation=%i\n",
++               img_cnt, ded_req.prefersDedicatedAllocation, ded_req.requiresDedicatedAllocation);
++
+         if (f->tiling == VK_IMAGE_TILING_LINEAR)
+             req.memoryRequirements.size = FFALIGN(req.memoryRequirements.size,
+                                                   p->props.properties.limits.minMemoryMapAlignment);
+@@ -2482,6 +2497,8 @@ static int alloc_bind_mem(AVHWFramesContext *hwfc, AVVkFrame *f,
+         /* In case the implementation prefers/requires dedicated allocation */
+         use_ded_mem = ded_req.prefersDedicatedAllocation |
+                       ded_req.requiresDedicatedAllocation;
++        if (((VulkanFramesPriv *)hwfc->hwctx)->export_requires_dedicated)
++            use_ded_mem = 1;
+         if (use_ded_mem)
+             ded_alloc.image = f->img[img_cnt];
+ 
+@@ -2853,7 +2870,8 @@ static void try_export_flags(AVHWFramesContext *hwfc,
+         .type   = VK_IMAGE_TYPE_2D,
+         .tiling = hwctx->tiling,
+         .usage  = hwctx->usage,
+-        .flags  = VK_IMAGE_CREATE_ALIAS_BIT,
++        .flags  = (hwctx->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT && has_mods) ?
++                  (hwctx->img_flags) : (VkImageCreateFlags)(VK_IMAGE_CREATE_ALIAS_BIT),
+     };
+ 
+     nb_mods = has_mods ? drm_mod_info->drmFormatModifierCount : 1;
+@@ -2864,9 +2882,17 @@ static void try_export_flags(AVHWFramesContext *hwfc,
+         ret = vk->GetPhysicalDeviceImageFormatProperties2(dev_hwctx->phys_dev,
+                                                         &pinfo, &props);
+ 
++        av_log(hwfc, AV_LOG_VERBOSE, "GetPhysicalDeviceImageFormatProperties2: mod[%d]=0x%llx -> %s\n",
++               i, has_mods ? (unsigned long long)phy_dev_mod_info.drmFormatModifier : 0ULL,
++               ret == VK_SUCCESS ? "OK" : "FAIL");
+         if (ret == VK_SUCCESS) {
+             *iexp |= exp;
+             *comp_handle_types |= eprops.externalMemoryProperties.compatibleHandleTypes;
++            if (exp == VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT) {
++                VulkanFramesPriv *fp = hwfc->hwctx;
++                fp->export_requires_dedicated = !!(eprops.externalMemoryProperties.externalMemoryFeatures &
++                                                  VK_EXTERNAL_MEMORY_FEATURE_DEDICATED_ONLY_BIT);
++            }
+         }
+     }
+ }
+@@ -2894,7 +2920,8 @@ static AVBufferRef *vulkan_pool_alloc(void *opaque, size_t size)
+                              ? VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT
+                              : VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT);
+ #else
+-    if (p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_MEMORY)
++    if ((p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_MEMORY) &&
++        (hwctx->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT))
+         try_export_flags(hwfc, &eiinfo.handleTypes, &e,
+                          VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT);
+ 
+@@ -2913,8 +2940,10 @@ static AVBufferRef *vulkan_pool_alloc(void *opaque, size_t size)
+     err = create_frame(hwfc, &f, hwctx->tiling, hwctx->usage, hwctx->img_flags,
+                        hwctx->nb_layers,
+                        eiinfo.handleTypes ? &eiinfo : hwctx->create_pnext);
+-    if (err)
++    if (err) {
++        av_log(hwfc, AV_LOG_ERROR, "vulkan_pool_alloc failed: create_frame failed: %d\n", err);
+         return NULL;
++    }
+ 
+     err = alloc_bind_mem(hwfc, f, eminfo, sizeof(*eminfo));
+     if (err)
+@@ -2942,6 +2971,7 @@ static AVBufferRef *vulkan_pool_alloc(void *opaque, size_t size)
+     return avbuf;
+ 
+ fail:
++    av_log(hwfc, AV_LOG_ERROR, "vulkan_pool_alloc failed with error %d\n", err);
+     vulkan_frame_free(hwfc, f);
+     return NULL;
+ }
+@@ -3043,7 +3073,14 @@ static int vulkan_frames_init(AVHWFramesContext *hwfc)
+     }
+ 
+     /* Lone DPB images do not need additional flags. */
+-    if (!is_lone_dpb) {
++    /* With DRM modifier + video profile the caller has already chosen a valid
++     * usage/img_flags/chain; do not add usage or img_flags (supported_usage does
++     * not consider the actual modifier or video profile). */
++    int drm_mod_with_video = (hwctx->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT &&
++                              ff_vk_find_struct(hwctx->create_pnext,
++                                                VK_STRUCTURE_TYPE_VIDEO_PROFILE_LIST_INFO_KHR));
++
++    if (!is_lone_dpb && !drm_mod_with_video) {
+         /* Image usage flags */
+         hwctx->usage |= supported_usage & (VK_IMAGE_USAGE_TRANSFER_DST_BIT |
+                                            VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+@@ -3968,7 +4005,7 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
+     return 0;
+ 
+ fail:
+-    vulkan_free_internal(dst_f);
++    vulkan_free_internal(p, dst_f);
+     return err;
+ }
+ 
+@@ -3979,6 +4016,7 @@ static int vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
+     CUcontext dummy;
+     AVVkFrame *dst_f;
+     AVVkFrameInternal *dst_int;
++    VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
+     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);
+@@ -4057,7 +4095,7 @@ static int vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
+ 
+ fail:
+     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+-    vulkan_free_internal(dst_f);
++    vulkan_free_internal(p, dst_f);
+     av_buffer_unref(&dst->buf[0]);
+     return err;
+ }
+@@ -4127,6 +4165,72 @@ static VkImageAspectFlags plane_index_to_aspect(int plane) {
+     return VK_IMAGE_ASPECT_MEMORY_PLANE_0_BIT_EXT;
+ }
+ 
++#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
++static int vulkan_drm_export_sync_fd(AVHWFramesContext *hwfc, AVVkFrame *f,
++                                     VulkanFramesPriv *fp, int nb_sems)
++{
++    int sync_fd = -1;
++    VkResult ret;
++    VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
++    AVVulkanDeviceContext *hwctx = &p->p;
++    FFVulkanFunctions *vk = &p->vkctx.vkfn;
++
++    if (f->internal->drm_sync_sem == VK_NULL_HANDLE) {
++        VkExportSemaphoreCreateInfo exp_info = {
++            .sType       = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
++            .handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
++        };
++        VkSemaphoreTypeCreateInfo type_info = {
++            .sType         = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
++            .pNext         = &exp_info,
++            .semaphoreType = VK_SEMAPHORE_TYPE_BINARY,
++        };
++        VkSemaphoreCreateInfo sem_create = {
++            .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
++            .pNext = &type_info,
++        };
++        ret = vk->CreateSemaphore(hwctx->act_dev, &sem_create, hwctx->alloc,
++                                  &f->internal->drm_sync_sem);
++        if (ret != VK_SUCCESS) {
++            av_log(hwctx, AV_LOG_ERROR, "Failed to create DRM export semaphore: %s\n",
++                   ff_vk_ret2str(ret));
++            return AVERROR_EXTERNAL;
++        }
++    }
++
++    /* Submit a lightweight exec that waits on the timeline semaphore
++     * (true last operation on the frame) and signals the binary semaphore,
++     * so any Vulkan frame can get a SYNC_FD regardless of origin. */
++    FFVkExecContext *exec = ff_vk_exec_get(&p->vkctx, &fp->compute_exec);
++    if (ff_vk_exec_start(&p->vkctx, exec) >= 0) {
++        for (int i = 0; i < nb_sems; i++)
++            ff_vk_exec_add_dep_wait_sem(&p->vkctx, exec, f->sem[i],
++                                        f->sem_value[i],
++                                        VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT);
++        ff_vk_exec_add_dep_bool_sem(&p->vkctx, exec, &f->internal->drm_sync_sem, 1,
++                                    VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, 0);
++        if (ff_vk_exec_submit(&p->vkctx, exec) >= 0) {
++            VkSemaphoreGetFdInfoKHR get_fd_info = {
++                .sType      = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
++                .semaphore  = f->internal->drm_sync_sem,
++                .handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_SYNC_FD_BIT,
++            };
++            ret = vk->GetSemaphoreFdKHR(hwctx->act_dev, &get_fd_info, &sync_fd);
++            if (ret != VK_SUCCESS) {
++                av_log(hwctx, AV_LOG_WARNING,
++                       "Failed to get sync fd from DRM map export semaphore: %s\n",
++                       ff_vk_ret2str(ret));
++                sync_fd = -1;
++            }
++        } else {
++            ff_vk_exec_discard_deps(&p->vkctx, exec);
++        }
++    }
++
++    return sync_fd;
++}
++#endif
++
+ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+                              const AVFrame *src, int flags)
+ {
+@@ -4137,15 +4241,14 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+     AVVulkanDeviceContext *hwctx = &p->p;
+     FFVulkanFunctions *vk = &p->vkctx.vkfn;
+     VulkanFramesPriv *fp = hwfc->hwctx;
++    const int planes = av_pix_fmt_count_planes(hwfc->sw_format);
+     const int nb_images = ff_vk_count_images(f);
+     VkImageDrmFormatModifierPropertiesEXT drm_mod = {
+         .sType = VK_STRUCTURE_TYPE_IMAGE_DRM_FORMAT_MODIFIER_PROPERTIES_EXT,
+     };
+-    VkSemaphoreWaitInfo wait_info = {
+-        .sType          = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
+-        .flags          = 0x0,
+-        .semaphoreCount = nb_images,
+-    };
++    const int nb_sems = nb_images;
++
++    int sync_fd = -1;
+ 
+     AVDRMFrameDescriptor *drm_desc = av_mallocz(sizeof(*drm_desc));
+     if (!drm_desc)
+@@ -4155,11 +4258,28 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+     if (err < 0)
+         goto end;
+ 
+-    /* Wait for the operation to finish so we can cleanly export it. */
+-    wait_info.pSemaphores = f->sem;
+-    wait_info.pValues     = f->sem_value;
++#ifdef DMA_BUF_IOCTL_EXPORT_SYNC_FILE
++    if ((p->vkctx.extensions & FF_VK_EXT_EXTERNAL_FD_SEM) &&
++        f->tiling == VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT &&
++        vk->GetSemaphoreFdKHR && vk->CreateSemaphore) {
++        err = vulkan_drm_export_sync_fd(hwfc, f, fp, nb_sems);
++        if (err < 0)
++            goto end;
++        sync_fd = err;
++        err = 0;
++    }
++#endif
+ 
+-    vk->WaitSemaphores(hwctx->act_dev, &wait_info, UINT64_MAX);
++    if (sync_fd < 0) {
++        VkSemaphoreWaitInfo wait_info = {
++            .sType          = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
++            .flags          = 0x0,
++            .semaphoreCount = nb_sems,
++            .pSemaphores    = f->sem,
++            .pValues        = f->sem_value,
++        };
++        vk->WaitSemaphores(hwctx->act_dev, &wait_info, UINT64_MAX);
++    }
+ 
+     err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, &vulkan_unmap_to_drm, drm_desc);
+     if (err < 0)
+@@ -4173,7 +4293,7 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+         goto end;
+     }
+ 
+-    for (int i = 0; (i < nb_images) && (f->mem[i]); i++) {
++    for (int i = 0; (i < planes) && (f->mem[i]); i++) {
+         VkMemoryGetFdInfoKHR export_info = {
+             .sType      = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
+             .memory     = f->mem[i],
+@@ -4188,12 +4308,30 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+             goto end;
+         }
+ 
++#if HAVE_LINUX_DMA_BUF_H && defined(DMA_BUF_IOCTL_IMPORT_SYNC_FILE)
++        if (sync_fd >= 0) {
++            int dup_fd = dup(sync_fd);
++            if (dup_fd >= 0) {
++                struct dma_buf_import_sync_file import_info = {
++                    .flags = DMA_BUF_SYNC_WRITE,
++                    .fd = dup_fd,
++                };
++                if (ioctl(drm_desc->objects[i].fd, DMA_BUF_IOCTL_IMPORT_SYNC_FILE, &import_info) < 0)
++                    av_log(hwfc, AV_LOG_WARNING, "DMA_BUF_IOCTL_IMPORT_SYNC_FILE failed: %s\n", av_err2str(AVERROR(errno)));
++                close(dup_fd);
++            } else {
++                av_log(hwfc, AV_LOG_WARNING, "dup(sync_fd) failed: %s\n", av_err2str(AVERROR(errno)));
++            }
++        }
++#endif
++
+         drm_desc->nb_objects++;
+         drm_desc->objects[i].size = f->size[i];
+         drm_desc->objects[i].format_modifier = drm_mod.drmFormatModifier;
+     }
+ 
+-    drm_desc->nb_layers = nb_images;
++    /* NV12 has 2 planes but 1 image/semaphore */
++    drm_desc->nb_layers = FFMAX(planes, nb_images);
+     for (int i = 0; i < drm_desc->nb_layers; i++) {
+         VkFormat plane_vkfmt = av_vkfmt_from_pixfmt(hwfc->sw_format)[i];
+ 
+@@ -4208,13 +4346,14 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+ 
+         for (int j = 0; j < drm_desc->layers[i].nb_planes; j++) {
+             VkSubresourceLayout layout;
++            int aspect_plane = (nb_images == 1) ? i : j;
+             VkImageSubresource sub = {
+-                .aspectMask = plane_index_to_aspect(j),
++                .aspectMask = plane_index_to_aspect(aspect_plane),
+             };
+ 
+             drm_desc->layers[i].planes[j].object_index = FFMIN(i, drm_desc->nb_objects - 1);
+ 
+-            vk->GetImageSubresourceLayout(hwctx->act_dev, f->img[i], &sub, &layout);
++            vk->GetImageSubresourceLayout(hwctx->act_dev, f->img[FFMIN(i, nb_images - 1)], &sub, &layout);
+             drm_desc->layers[i].planes[j].offset = layout.offset;
+             drm_desc->layers[i].planes[j].pitch  = layout.rowPitch;
+         }
+@@ -4234,13 +4373,21 @@ static int vulkan_map_to_drm(AVHWFramesContext *hwfc, AVFrame *dst,
+     dst->width   = src->width;
+     dst->height  = src->height;
+     dst->data[0] = (uint8_t *)drm_desc;
++    dst->hw_frames_ctx = av_buffer_ref(src->hw_frames_ctx);
++
++    if (sync_fd >= 0)
++        close(sync_fd);
+ 
+     av_log(hwfc, AV_LOG_VERBOSE, "Mapped AVVkFrame to a DRM object!\n");
+ 
+     return 0;
+ 
+ end:
++    for (int i = 0; i < drm_desc->nb_objects; i++)
++        close(drm_desc->objects[i].fd);
+     av_free(drm_desc);
++    if (sync_fd >= 0)
++        close(sync_fd);
+     return err;
+ }
+ 
+@@ -4720,7 +4867,7 @@ end:
+ static int vulkan_transfer_data_to(AVHWFramesContext *hwfc, AVFrame *dst,
+                                    const AVFrame *src)
+ {
+-    av_unused VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
++    VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
+ 
+     switch (src->format) {
+ #if CONFIG_CUDA
+@@ -4750,6 +4897,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
+     CUcontext dummy;
+     AVVkFrame *dst_f;
+     AVVkFrameInternal *dst_int;
++    VulkanDevicePriv *p = hwfc->device_ctx->hwctx;
+     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);
+@@ -4830,7 +4978,7 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
+ 
+ fail:
+     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+-    vulkan_free_internal(dst_f);
++    vulkan_free_internal(p, dst_f);
+     av_buffer_unref(&dst->buf[0]);
+     return err;
+ }
+-- 
+2.25.1
+

diff --git a/ffmpeg.spec b/ffmpeg.spec
index 88da3d8..653c151 100644
--- a/ffmpeg.spec
+++ b/ffmpeg.spec
@@ -129,6 +129,9 @@ Patch2:         ffmpeg-allow-fdk-aac-free.patch
 # Backport fix for CVE-2026-30998
 Patch10:        https://git.ffmpeg.org/gitweb/ffmpeg.git/patch/18b83f2d0a0f9bcbafb0001a2911327c4b8df056#/ffmpeg-CVE-2026-30998.patch
 
+# Add upstream commit to address firefox vulkan direct-export rendering issue
+Patch11:         https://git.ffmpeg.org/gitweb/ffmpeg.git/patch/25e187f8494966377a4b9d077260ce7b501a911c#/ffmpeg-vulkan-direct-export.patch
+
 # Add first_dts getter to libavformat for Chromium
 # See: https://bugzilla.redhat.com/show_bug.cgi?id=2240127
 # Reference: https://crbug.com/1306560

                 reply	other threads:[~2026-07-30 13:21 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178541771601.1.18187437852910736297.rpms-ffmpeg-b59068d371bc@fedoraproject.org \
    --to=dominik@greysector.net \
    --cc=git-commits@fedoraproject.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox