PR #25228 opened by Niklas Haas (haasn)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25228
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25228.patch


>From caf03574a0a00b53c6271995050b9efe525fa102 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Tue, 20 Oct 2026 00:31:38 +0200
Subject: [PATCH 1/5] avutil/vulkan: export ff_vk_map_fmt_to_rep()

Signed-off-by: Niklas Haas <[email protected]>
---
 libavutil/vulkan.c | 6 +++---
 libavutil/vulkan.h | 6 ++++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index 3987c33080a..645fdc42099 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -1996,7 +1996,7 @@ FFVkImageViews *ff_vk_imageviews_alloc(FFVulkanContext 
*s, int nb_views)
     return iv;
 }
 
-static VkFormat map_fmt_to_rep(VkFormat fmt, enum FFVkShaderRepFormat rep_fmt)
+VkFormat ff_vk_map_fmt_to_rep(VkFormat fmt, enum FFVkShaderRepFormat rep_fmt)
 {
 #define REPS_FMT(fmt) \
     [FF_VK_REP_NATIVE] = fmt ## _UINT, \
@@ -2118,7 +2118,7 @@ int ff_vk_create_imageview(FFVulkanContext *s,
         .pNext      = &view_usage_info,
         .image      = vkf->img[FFMIN(plane, nb_images - 1)],
         .viewType   = VK_IMAGE_VIEW_TYPE_2D,
-        .format     = map_fmt_to_rep(rep_fmts[plane], rep_fmt),
+        .format     = ff_vk_map_fmt_to_rep(rep_fmts[plane], rep_fmt),
         .components = ff_comp_identity_map,
         .subresourceRange = {
             .aspectMask = ff_vk_aspect_flag(f, plane),
@@ -2173,7 +2173,7 @@ int ff_vk_create_imageviews(FFVulkanContext *s, 
FFVkExecContext *e,
             .pNext      = &view_usage_info,
             .image      = vkf->img[FFMIN(i, nb_images - 1)],
             .viewType   = VK_IMAGE_VIEW_TYPE_2D,
-            .format     = map_fmt_to_rep(rep_fmts[i], rep_fmt),
+            .format     = ff_vk_map_fmt_to_rep(rep_fmts[i], rep_fmt),
             .components = ff_comp_identity_map,
             .subresourceRange = {
                 .aspectMask = ff_vk_aspect_flag(f, i),
diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h
index 30850c07d92..951052e59e6 100644
--- a/libavutil/vulkan.h
+++ b/libavutil/vulkan.h
@@ -448,6 +448,12 @@ enum FFVkShaderRepFormat {
 const char *ff_vk_shader_rep_fmt(enum AVPixelFormat pix_fmt,
                                  enum FFVkShaderRepFormat rep_fmt);
 
+/**
+ * Returns the view format for the given representation of fmt, or
+ * VK_FORMAT_UNDEFINED if there is none.
+ */
+VkFormat ff_vk_map_fmt_to_rep(VkFormat fmt, enum FFVkShaderRepFormat rep_fmt);
+
 /**
  * Loads props/mprops/driver_props
  */
-- 
2.54.0


>From 473a87ecd6037d32ee7fb33bd8fece9101cd2244 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Tue, 20 Oct 2026 00:31:51 +0200
Subject: [PATCH 2/5] avutil/vulkan: add ff_vk_create_imageviews_fmt()

Like ff_vk_create_imageviews(), but with an explicit view format per plane.

Signed-off-by: Niklas Haas <[email protected]>
---
 libavutil/vulkan.c | 39 +++++++++++++++++++++++++++------------
 libavutil/vulkan.h |  9 +++++++++
 2 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c
index 645fdc42099..6d5038a5848 100644
--- a/libavutil/vulkan.c
+++ b/libavutil/vulkan.c
@@ -2146,16 +2146,16 @@ int ff_vk_create_imageview(FFVulkanContext *s,
     return 0;
 }
 
-int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
-                            VkImageView views[AV_NUM_DATA_POINTERS],
-                            AVFrame *f, enum FFVkShaderRepFormat rep_fmt)
+int ff_vk_create_imageviews_fmt(FFVulkanContext *s, FFVkExecContext *e,
+                                VkImageView views[AV_NUM_DATA_POINTERS],
+                                AVFrame *f,
+                                const VkFormat fmts[AV_NUM_DATA_POINTERS])
 {
     int err = 0;
     VkResult ret;
     FFVulkanFunctions *vk = &s->vkfn;
     AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
     AVVulkanFramesContext *vkfc = hwfc->hwctx;
-    const VkFormat *rep_fmts = av_vkfmt_from_pixfmt(hwfc->sw_format);
     AVVkFrame *vkf = (AVVkFrame *)f->data[0];
     const int nb_images = ff_vk_count_images(vkf);
     const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
@@ -2173,7 +2173,7 @@ int ff_vk_create_imageviews(FFVulkanContext *s, 
FFVkExecContext *e,
             .pNext      = &view_usage_info,
             .image      = vkf->img[FFMIN(i, nb_images - 1)],
             .viewType   = VK_IMAGE_VIEW_TYPE_2D,
-            .format     = ff_vk_map_fmt_to_rep(rep_fmts[i], rep_fmt),
+            .format     = fmts[i],
             .components = ff_comp_identity_map,
             .subresourceRange = {
                 .aspectMask = ff_vk_aspect_flag(f, i),
@@ -2181,13 +2181,6 @@ int ff_vk_create_imageviews(FFVulkanContext *s, 
FFVkExecContext *e,
                 .layerCount = 1,
             },
         };
-        if (view_create_info.format == VK_FORMAT_UNDEFINED) {
-            av_log(s, AV_LOG_ERROR, "Unable to find a compatible 
representation "
-                                    "of format %i and mode %i\n",
-                   rep_fmts[i], rep_fmt);
-            err = AVERROR(EINVAL);
-            goto fail;
-        }
 
         ret = vk->CreateImageView(s->hwctx->act_dev, &view_create_info,
                                   s->hwctx->alloc, &tmp_views[i]);
@@ -2215,6 +2208,28 @@ fail:
     return err;
 }
 
+int ff_vk_create_imageviews(FFVulkanContext *s, FFVkExecContext *e,
+                            VkImageView views[AV_NUM_DATA_POINTERS],
+                            AVFrame *f, enum FFVkShaderRepFormat rep_fmt)
+{
+    AVHWFramesContext *hwfc = (AVHWFramesContext *)f->hw_frames_ctx->data;
+    const VkFormat *rep_fmts = av_vkfmt_from_pixfmt(hwfc->sw_format);
+    const int nb_planes = av_pix_fmt_count_planes(hwfc->sw_format);
+    VkFormat fmts[AV_NUM_DATA_POINTERS] = { 0 };
+
+    for (int i = 0; i < nb_planes; i++) {
+        fmts[i] = ff_vk_map_fmt_to_rep(rep_fmts[i], rep_fmt);
+        if (fmts[i] == VK_FORMAT_UNDEFINED) {
+            av_log(s, AV_LOG_ERROR, "Unable to find a compatible 
representation "
+                                    "of format %i and mode %i\n",
+                   rep_fmts[i], rep_fmt);
+            return AVERROR(EINVAL);
+        }
+    }
+
+    return ff_vk_create_imageviews_fmt(s, e, views, f, fmts);
+}
+
 void ff_vk_frame_barrier(FFVulkanContext *s, FFVkExecContext *e,
                          AVFrame *pic, VkImageMemoryBarrier2 *bar, int *nb_bar,
                          VkPipelineStageFlags2 src_stage,
diff --git a/libavutil/vulkan.h b/libavutil/vulkan.h
index 951052e59e6..1b92e6a4b2e 100644
--- a/libavutil/vulkan.h
+++ b/libavutil/vulkan.h
@@ -582,6 +582,15 @@ int ff_vk_create_imageviews(FFVulkanContext *s, 
FFVkExecContext *e,
                             VkImageView views[AV_NUM_DATA_POINTERS],
                             AVFrame *f, enum FFVkShaderRepFormat rep_fmt);
 
+/**
+ * Same as ff_vk_create_imageviews(), but with an explicit view format for
+ * each plane, which must be compatible with the format of the image.
+ */
+int ff_vk_create_imageviews_fmt(FFVulkanContext *s, FFVkExecContext *e,
+                                VkImageView views[AV_NUM_DATA_POINTERS],
+                                AVFrame *f,
+                                const VkFormat fmts[AV_NUM_DATA_POINTERS]);
+
 #define ff_vk_buf_barrier(dst, vkb, s_stage, s_access, s_access2,              
\
                           d_stage, d_access, d_access2, offs, bsz)             
\
     do {                                                                       
\
-- 
2.54.0


>From 1ebc8685da67b69c478b6a71c873b0b1ab96a2da Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Tue, 20 Oct 2026 00:31:56 +0200
Subject: [PATCH 3/5] swscale/vulkan/spvasm: add OpBitwiseOr

Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/vulkan/spvasm.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/libswscale/vulkan/spvasm.h b/libswscale/vulkan/spvasm.h
index b6712929878..93729be07de 100644
--- a/libswscale/vulkan/spvasm.h
+++ b/libswscale/vulkan/spvasm.h
@@ -342,6 +342,7 @@ OP_RES2SRC(OpSLessThanEqual, 179)
 OP_RES2SRC(OpShiftRightLogical, 194)
 OP_RES2SRC(OpShiftLeftLogical, 196)
 
+OP_RES2SRC(OpBitwiseOr, 197)
 OP_RES2SRC(OpBitwiseAnd, 199)
 OP_1SRC(OpReturnValue, 254)
 
-- 
2.54.0


>From ada304ddfca59b51d49a935019676498f75c539d Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Tue, 20 Oct 2026 01:17:05 +0200
Subject: [PATCH 4/5] swscale/vulkan/ops: resolve image view formats at compile
 time

Unsupported formats now fail at compile time instead of being silently
ignored in process().

Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/vulkan/ops.c | 42 ++++++++++++++++++++++++++++++++---------
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 3f8c34ecda3..71769d65d92 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -86,8 +86,8 @@ typedef struct VulkanPriv {
     FFVulkanShader shd;
     FFVkBuffer data_bufs[MAX_DATA_BUFS];
     int nb_data_bufs;
-    enum FFVkShaderRepFormat src_rep;
-    enum FFVkShaderRepFormat dst_rep;
+    VkFormat src_view_fmt[AV_NUM_DATA_POINTERS];
+    VkFormat dst_view_fmt[AV_NUM_DATA_POINTERS];
     int interlaced;
 } VulkanPriv;
 
@@ -110,8 +110,10 @@ static void process(const SwsFrame *dst, const SwsFrame 
*src, int y, int h,
 
     VkImageView src_views[AV_NUM_DATA_POINTERS];
     VkImageView dst_views[AV_NUM_DATA_POINTERS];
-    ff_vk_create_imageviews(&p->s->vkctx, ec, src_views, src_f, p->src_rep);
-    ff_vk_create_imageviews(&p->s->vkctx, ec, dst_views, dst_f, p->dst_rep);
+    ff_vk_create_imageviews_fmt(&p->s->vkctx, ec, src_views, src_f,
+                                p->src_view_fmt);
+    ff_vk_create_imageviews_fmt(&p->s->vkctx, ec, dst_views, dst_f,
+                                p->dst_view_fmt);
 
     ff_vk_shader_update_img_array(&p->s->vkctx, ec, &p->shd, src_f, src_views,
                                   0, 0, VK_IMAGE_LAYOUT_GENERAL, 
VK_NULL_HANDLE);
@@ -884,6 +886,25 @@ static int read_filtered(SPICtx *spi, SPIRVIDs *id, const 
SwsOpList *ops,
                                     acc_s[0], acc_s[1], acc_s[2], acc_s[3]);
 }
 
+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);
+    enum FFVkShaderRepFormat rep = FF_VK_REP_NATIVE;
+    if (op)
+        rep = op->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : FF_VK_REP_UINT;
+    if (!vk_fmts)
+        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);
+        if (view_fmts[i] == VK_FORMAT_UNDEFINED)
+            return AVERROR(ENOTSUP);
+    }
+
+    return 0;
+}
+
 /* Plane indices refer to actual frame planes, so the image handle arrays
  * have to cover the highest plane referenced, not just the plane count. */
 static int rw_op_img_count(const SwsOp *op, const uint8_t *planes)
@@ -916,12 +937,15 @@ static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, 
FFVulkanOpsCtx *s,
     /* Image ops, to determine types */
     const SwsOp *op_w = ff_sws_op_list_output(ops);
     int out_img_count = rw_op_img_count(op_w, ops->plane_dst);
-    p->dst_rep = op_w->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : 
FF_VK_REP_UINT;
-
     const SwsOp *op_r = ff_sws_op_list_input(ops);
     int in_img_count = op_r ? rw_op_img_count(op_r, ops->plane_src) : 0;
-    if (op_r)
-        p->src_rep = op_r->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : 
FF_VK_REP_UINT;
+
+    int err = get_view_fmts(p->dst_view_fmt, ops->dst.format, op_w);
+    if (err < 0)
+        return err;
+    err = get_view_fmts(p->src_view_fmt, ops->src.format, op_r);
+    if (err < 0)
+        return err;
 
     FFVulkanDescriptorSetBinding desc_set[MAX_DATA_BUFS] = {
         {
@@ -938,7 +962,7 @@ static int add_ops_spirv(SwsContext *sws, VulkanPriv *p, 
FFVulkanOpsCtx *s,
     ff_vk_shader_add_descriptor_set(&s->vkctx, shd, desc_set, 2, 0);
 
     /* Create dither buffers */
-    int err = create_bufs(s, p, ops);
+    err = create_bufs(s, p, ops);
     if (err < 0)
         return err;
 
-- 
2.54.0


>From 8383d7ac2b6e7c795c477d19c86801d9ef51d120 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Tue, 20 Oct 2026 01:17:21 +0200
Subject: [PATCH 5/5] 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 <[email protected]>
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/vulkan/ops.c | 64 ++++++++++++++++++++++++++++++++++-------
 1 file changed, 53 insertions(+), 11 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 71769d65d92..87c622c8619 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);
         }
-- 
2.54.0

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

Reply via email to