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]
