public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/ffmpeg] rawhide: Fix Firefox vulkan direct-export rendering issue
@ 2026-07-30 13:21 Dominik 'Rathann' Mierzejewski
0 siblings, 0 replies; only message in thread
From: Dominik 'Rathann' Mierzejewski @ 2026-07-30 13:21 UTC (permalink / raw)
To: git-commits
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
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-07-30 13:21 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-30 13:21 [rpms/ffmpeg] rawhide: Fix Firefox vulkan direct-export rendering issue Dominik 'Rathann' Mierzejewski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox