Commit 56dbd4cd37 for ffmpeg

commit 56dbd4cd377bc6ac2a59a9d62a3a93875e2297b3
Author: Niklas Haas <git@haasn.dev>
Date:   Tue Oct 20 01:17:21 2026 +0200

    swscale/vulkan/ops: implement SWS_OP_PACK and SWS_OP_UNPACK properly

    These were hacked into the code as swizzles relying on the _UINT_PACK32 view
    of the image to do the actual (un)packing, which only covered the 2:10:10:10
    formats, and silently produced garbage on drivers lacking storage support
    for those view formats (e.g. ANV).

    Implement these operations properly, the way they were intended, by doing
    the actual documented bit-packing operation instead whatever hacky
    assumptions the swizzle was making.

    Fixes fate-sws-unscaled-spirv on Intel.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Signed-off-by: Niklas Haas <git@haasn.dev>

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 71769d65d9..87c622c861 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -534,6 +534,23 @@ static void define_shader_consts(SwsContext *sws, const SwsOpList *ops,
                 }
             }
             break;
+        case SWS_OP_PACK:
+        case SWS_OP_UNPACK: {
+            uint64_t mask[4];
+            int shift[4], tmp[4];
+            ff_sws_pack_op_decode(op, mask, shift);
+            for (int i = 0; i < 4; i++)
+                tmp[i] = spi_OpConstantUInt(spi, u32_type, shift[i]);
+            id->const_ids[id->nb_const_ids++] =
+                spi_OpConstantComposite(spi, id->u32vec4_type,
+                                        tmp[0], tmp[1], tmp[2], tmp[3]);
+            for (int i = 0; i < 4; i++)
+                tmp[i] = spi_OpConstantUInt(spi, u32_type, mask[i]);
+            id->const_ids[id->nb_const_ids++] =
+                spi_OpConstantComposite(spi, id->u32vec4_type,
+                                        tmp[0], tmp[1], tmp[2], tmp[3]);
+            break;
+        }
         case SWS_OP_LSHIFT:
         case SWS_OP_RSHIFT: {
             int tmp = spi_OpConstantUInt(spi, u32_type, op->shift.amount);
@@ -886,10 +903,26 @@ static int read_filtered(SPICtx *spi, SPIRVIDs *id, const SwsOpList *ops,
                                     acc_s[0], acc_s[1], acc_s[2], acc_s[3]);
 }

+/* Sub-packed formats are accessed as a single raw word per pixel, which is
+ * then (un)packed manually. */
+static VkFormat packed_word_fmt(const SwsOp *op)
+{
+    if (!op || op->rw.mode != SWS_RW_PACKED || op->rw.elems != 1)
+        return VK_FORMAT_UNDEFINED;
+
+    switch (op->type) {
+    case SWS_PIXEL_U8:  return VK_FORMAT_R8_UINT;
+    case SWS_PIXEL_U16: return VK_FORMAT_R16_UINT;
+    case SWS_PIXEL_U32: return VK_FORMAT_R32_UINT;
+    default:            return VK_FORMAT_UNDEFINED;
+    }
+}
+
 static int get_view_fmts(VkFormat view_fmts[AV_NUM_DATA_POINTERS],
                          enum AVPixelFormat fmt, const SwsOp *op)
 {
     const VkFormat *vk_fmts = av_vkfmt_from_pixfmt(fmt);
+    const VkFormat word_fmt = packed_word_fmt(op);
     enum FFVkShaderRepFormat rep = FF_VK_REP_NATIVE;
     if (op)
         rep = op->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : FF_VK_REP_UINT;
@@ -897,7 +930,7 @@ static int get_view_fmts(VkFormat view_fmts[AV_NUM_DATA_POINTERS],
         return AVERROR(ENOTSUP);

     for (int i = 0; i < av_pix_fmt_count_planes(fmt); i++) {
-        view_fmts[i] = ff_vk_map_fmt_to_rep(vk_fmts[i], rep);
+        view_fmts[i] = word_fmt ? word_fmt : ff_vk_map_fmt_to_rep(vk_fmts[i], rep);
         if (view_fmts[i] == VK_FORMAT_UNDEFINED)
             return AVERROR(ENOTSUP);
     }
@@ -1270,18 +1303,27 @@ static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, FFVulkanOpsCtx *s,
             nb_const_ids += 5*5 + 1;
             break;
         }
-        case SWS_OP_UNPACK:
-            if (ops->src.format == AV_PIX_FMT_X2BGR10)
-                data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 2, 1, 0);
-            else
-                data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 0, 1, 2);
+        case SWS_OP_UNPACK: {
+            int shift = id->const_ids[nb_const_ids++];
+            int mask  = id->const_ids[nb_const_ids++];
+            data = spi_OpVectorShuffle(spi, type_v, data, data, 0, 0, 0, 0);
+            data = spi_OpShiftRightLogical(spi, type_v, data, shift);
+            data = spi_OpBitwiseAnd(spi, type_v, data, mask);
             break;
-        case SWS_OP_PACK:
-            if (ops->dst.format == AV_PIX_FMT_X2BGR10)
-                data = spi_OpVectorShuffle(spi, type_v, data, data, 3, 2, 1, 0);
-            else
-                data = spi_OpVectorShuffle(spi, type_v, data, data, 1, 2, 3, 0);
+        }
+        case SWS_OP_PACK: {
+            int shift = id->const_ids[nb_const_ids++];
+            int mask  = id->const_ids[nb_const_ids++];
+            data = spi_OpBitwiseAnd(spi, type_v, data, mask);
+            data = spi_OpShiftLeftLogical(spi, type_v, data, shift);
+            int val = spi_OpCompositeExtract(spi, type_s, data, 0);
+            for (int i = 1; i < 4; i++) {
+                int tmp = spi_OpCompositeExtract(spi, type_s, data, i);
+                val = spi_OpBitwiseOr(spi, type_s, val, tmp);
+            }
+            data = spi_OpCompositeConstruct(spi, type_v, val, val, val, val);
             break;
+        }
         default:
             return AVERROR(ENOTSUP);
         }