Commit ecd3b68059 for ffmpeg

commit ecd3b68059df4461b51416d6a9dd30b062f7be62
Author: Emil J. Tywoniak <emil@tywoniak.eu>
Date:   Sun Oct 25 22:56:31 2026 +0100

    avcodec/vulkan_encode: flush and invalidate non-coherent packet buffers

    The packet buffer can be non-coherent (e.g. Intel non-LLC GPUs). The
    headers written by the CPU were never flushed before submission, and
    the readback flushed instead of invalidating. On Meteor Lake this
    corrupted h264_vulkan and hevc_vulkan output at random.

    Assisted-by: Claude Opus 5.5

diff --git a/libavcodec/vulkan_encode.c b/libavcodec/vulkan_encode.c
index 9d3c9b04c5..d654cf05d6 100644
--- a/libavcodec/vulkan_encode.c
+++ b/libavcodec/vulkan_encode.c
@@ -337,6 +337,13 @@ static int vulkan_encode_issue(AVCodecContext *avctx,
         }
     }

+    /* Flush the headers written above if needed */
+    if (encode_info.dstBufferOffset)
+        RET(ff_vk_flush_buffer(&ctx->s, sd_buf, 0,
+                               FFALIGN(encode_info.dstBufferOffset,
+                                       ctx->s.props.properties.limits.nonCoherentAtomSize),
+                               1));
+
     vp->slices_offset = encode_info.dstBufferOffset;

     /* Align buffer size to the nearest lower alignment requirement. */
@@ -492,6 +499,7 @@ static int vulkan_encode_output(AVCodecContext *avctx,
                                 FFHWBaseEncodePicture *base_pic, AVPacket *pkt)
 {
     VkResult ret;
+    int err, offs;
     FFVulkanEncodePicture *vp = base_pic->priv;
     FFVulkanEncodeContext *ctx = avctx->priv_data;
     FFHWBaseEncodeContext *base_ctx = &ctx->base;
@@ -520,32 +528,26 @@ static int vulkan_encode_output(AVCodecContext *avctx,
         return AVERROR_EXTERNAL;
     }

-    /* Invalidate buffer if needed */
-    if (!(sd_buf->flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) {
-        FFVulkanFunctions *vk = &ctx->s.vkfn;
-        VkMappedMemoryRange invalidate_buf;
-
-        int offs = vp->slices_offset;
-        /* If the non-coherent alignment is greater than the bitstream buffer
-         * offset's alignment, and the offs value is not aligned already,
-         * align it to the previous alignment point. */
-        if (ctx->s.props.properties.limits.nonCoherentAtomSize >
-            ctx->caps.minBitstreamBufferOffsetAlignment && offs &&
-            (FFALIGN(offs, ctx->s.props.properties.limits.nonCoherentAtomSize) != offs)) {
-            offs -= ctx->s.props.properties.limits.nonCoherentAtomSize;
-            offs = FFALIGN(FFMAX(offs, 0), ctx->s.props.properties.limits.nonCoherentAtomSize);
-        }
-
-        invalidate_buf = (VkMappedMemoryRange) {
-            .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
-            .memory = sd_buf->mem,
-            .offset = offs,
-            .size = VK_WHOLE_SIZE,
-        };
-
-        vk->FlushMappedMemoryRanges(ctx->s.hwctx->act_dev, 1, &invalidate_buf);
+    /* If the non-coherent alignment is greater than the bitstream buffer
+     * offset's alignment, and the offs value is not aligned already,
+     * align it to the previous alignment point. */
+    offs = vp->slices_offset;
+    if (ctx->s.props.properties.limits.nonCoherentAtomSize >
+        ctx->caps.minBitstreamBufferOffsetAlignment && offs &&
+        (FFALIGN(offs, ctx->s.props.properties.limits.nonCoherentAtomSize) != offs)) {
+        offs -= ctx->s.props.properties.limits.nonCoherentAtomSize;
+        offs = FFALIGN(FFMAX(offs, 0), ctx->s.props.properties.limits.nonCoherentAtomSize);
     }

+    /* Invalidate buffer if needed */
+    err = ff_vk_flush_buffer(&ctx->s, sd_buf, offs,
+                             FFALIGN(vp->slices_offset + query_data[0] +
+                                     query_data[1] - offs,
+                                     ctx->s.props.properties.limits.nonCoherentAtomSize),
+                             0);
+    if (err < 0)
+        return err;
+
     if (vp->non_independent_frame) {
         av_assert0(!ctx->prev_buf_ref);
         size_t prev_buf_size = vp->slices_offset + query_data[0] + query_data[1];