PR #25137 opened by t-boiko
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25137
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25137.patch

switch_layout() submits vkCmdPipelineBarrier2 on the compute queue. The
image was last written by vkCmdDecodeVideoKHR on the video queue, which
signals the frame timeline at VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR.
The layout transition is a write, and the cross-queue order is that
semaphore wait. VK_PIPELINE_STAGE_2_NONE names no stage, so the barrier
was outside the wait and raced VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR
(SYNC-HAZARD-WRITE-AFTER-WRITE). Wait at
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT so every command in the submission,
including the transition, happens after the signal.

## Summary

Wait on the frame timeline at VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT in 
switch_layout() before the export layout transition.

## Why this wait has to cover the barrier

1. vkCmdDecodeVideoKHR writes the image on the video queue and signals the 
image timeline at VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR. The signaled access 
is VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR.
2. prepare_frame(PREP_MODE_EXTERNAL_EXPORT) calls switch_layout(), which 
records vkCmdPipelineBarrier2 on compute_exec, a different queue. The barrier 
transitions the image, and a layout transition is a write.
3. Two queues share an image only through a semaphore. The wait's stageMask is 
the set of stages that are ordered after the signal. A command whose stage is 
outside that mask is not ordered after the decode write.
4. The wait stage was VK_PIPELINE_STAGE_2_NONE. That mask contains no stage, so 
the barrier was not in the wait. The transition write and the decode write had 
no dependency. Synchronization validation reports this as 
SYNC-HAZARD-WRITE-AFTER-WRITE on vkQueueSubmit2.
5. VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT puts every command in that submission 
inside the wait, including the barrier. The transition then happens after the 
decode signal.

The import submission in vulkan_map_from_drm_frame_sync keeps a NONE wait on 
the destination image timeline. Its producer dependency is the dma-buf 
sync-file semaphore, and that semaphore is already waited at ALL_COMMANDS. 
vulkan_drm_export_sync_fd already waits the frame timeline at ALL_COMMANDS 
before it signals the sync file.
Ordinary decode, hwdownload, and encode do not use this wait after a decode 
write. Decode waits the timeline at VIDEO_DECODE, hwdownload waits it at 
ALL_COMMANDS, and vulkan_encode waits the source frame at ALL_COMMANDS before 
vkCmdEncodeVideoKHR. Those paths do not report this hazard.

## Steps to reproduce
1. Enable validation layers (synchronization layer is required)
2. Run the following command:
    ./ffmpeg -init_hw_device vulkan=vk:0 -init_hw_device 
drm=drm0:/dev/dri/card0 \
      -filter_hw_device drm0 -hwaccel vulkan -hwaccel_output_format vulkan \
      -i artifacts/drmmod/clip.h264 -frames:v 2 -vf hwmap,format=drm_prime -f 
null -

Before the patch, validation reports SYNC-HAZARD-WRITE-AFTER-WRITE: 
vkCmdPipelineBarrier2 on one queue writes an image that vkCmdDecodeVideoKHR 
wrote on another queue, with no dependency on 
VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR. After the patch, that hazard is gone.


>From afe5bb1eda8d94e0b2580a0fd6d05f1455d96872 Mon Sep 17 00:00:00 2001
From: Tymur Boiko <[email protected]>
Date: Fri, 16 Oct 2026 22:01:25 +0200
Subject: [PATCH] lavu/vulkan: wait for the frame timeline before the export
 layout transition

switch_layout() submits vkCmdPipelineBarrier2 on the compute queue. The
image was last written by vkCmdDecodeVideoKHR on the video queue, which
signals the frame timeline at VK_PIPELINE_STAGE_2_VIDEO_DECODE_BIT_KHR.
The layout transition is a write, and the cross-queue order is that
semaphore wait. VK_PIPELINE_STAGE_2_NONE names no stage, so the barrier
was outside the wait and raced VK_ACCESS_2_VIDEO_DECODE_WRITE_BIT_KHR
(SYNC-HAZARD-WRITE-AFTER-WRITE). Wait at
VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT so every command in the submission,
including the transition, happens after the signal.
---
 libavutil/hwcontext_vulkan.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 63d2ab5c43..72e4df26c8 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -2644,8 +2644,10 @@ static int switch_layout(AVHWFramesContext *hwfc, 
FFVkExecPool *ectx,
     if (err < 0)
         return err;
 
+    /* NONE does not block any stage, so this queue's layout transition
+     * races a decode write submitted on the video queue. */
     err = ff_vk_exec_add_dep_frame(&p->vkctx, exec, &tmp_frame,
-                                   VK_PIPELINE_STAGE_2_NONE,
+                                   VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT,
                                    VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT);
     if (err < 0)
         return err;
-- 
2.54.0

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

Reply via email to