From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 730F94EF4D for ; Thu, 19 Feb 2026 22:40:55 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'VIcboJ+MbHVxH4xXydYdyhb0I5cbKLz6lCgs5oGa8+4=', expected b'xt/CSDEdvmH5NTiSgADDTwpI9oCgHK3m0NnviGHkthw=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1771540815; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=VIcboJ+MbHVxH4xXydYdyhb0I5cbKLz6lCgs5oGa8+4=; b=n2siIaGToYiTvZRCCW2UvN5UN5YyiAnSzzFPt+ONwSrKS33e8UOS12NFTgy6uCbkBbgAv oORyw1CXRm2HdJR0vovjZu8e1UtDBzOjEzVeW8E3qaEFNtiFAwMGfp5xoMxq0BomOn+cqaS NhhF/FSY3nrvh7R8dC2X0gSwB5OADzr/i+su04AMVDBZLuBpzKCW5gb9R/DVq19krJAZlTy wZ69HedAwEUm4suODZFLK38QVkEktbmxJY553s3WnqY95r4SH1F0vHI/+fsXF9X82CYbfNq J+XXv6YuckMN1rZ8yRecPOnYx8elZGiWWudRJuuUn3tdCLY4xNMQeRS90D/Q== Received: from [172.18.0.3] (unknown [172.18.0.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id BF9AB6912EE; Fri, 20 Feb 2026 00:40:15 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1771540803; b=HRgqom20L8gZWJOYWrk7yNNROXiQBXhWqH1ula5iT0mtEGLupF+DKRn7uzaXOBJ13jbDS ltM5EcNvMlP65A8UxvBBAfGVgyd3dYmrioBXGlTRbDZOynfDiM5b4FHig86IhmwrY4Di45L 9GTUqL182S60KcuMMRpYDh/EYtn/wp2CXw6sfeBF/KTdwy0CY/Ebn2DtDnQkpe9h4eBnqDN 4omXK242H16BJ+9No/pNNpQ0ILQHjPNiG/Z2bfPlni/MN+MXWWIw/tHY98RPnLyb9wfpKFi Bps2av1p2yTGUhsIpxOFKRbPdRP+56wtEjvzbCQtowoeDIwvDa/OfVDRw/Aw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1771540803; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=jRV+wSHSNJSdvIt7HNohT5tdEzlVlOw+Olg/WMfbQjg=; b=Lq1kqcfX3BFGkQWRTXPPCTcpWKlXeHXtFcGu70wVMXKv2wEym8ShEUZH+MoKzFw0e+pjj kBDIeND7vwVK0hIrcrrx4ogxP6VnH00H31aeN2+35XD1Re/uKmxf4EG1Gk0yEwZsAeZanP8 pG24v5cP9VMzqoJiXuhAZzbDmhE4D3+Z7tXsI4RvJy9vv0XBCf9n+tmxCYMCSNeZxvNbpOB Hz8ON7ljbsIAHgMtSdguvpH4zcyq+G4FhYbLAVJFXw+G+YQphgRH1WC8J456OVv+Dum9gDe B8dQupKqRVmSozOM8VcCg3Dz7iSD8ekZXyYTkiWbT3Sff2zWv5Xd4E2mIfrg== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1771540795; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=xt/CSDEdvmH5NTiSgADDTwpI9oCgHK3m0NnviGHkthw=; b=u/er2dj0oLgbQi/P+QUQRzOhJlxOgnF2+2ATbW3+Goe5kO4JpA6fedFQLRhuSVLaBVIPJ EdIPnf8BUaCCBf+lTksmsE7fU++z+l5SNpxN9wGqUhDZf9gCvW7WhWYNw/NuOVdJkbO/V6g cThDBlbHYTJhBrS4c3cS7RD19deO03xXaQUl107kX69RkUpHluPCxTUfdmmPxK/GN7K7huy P3KcyoX698FkrqzcXYixkcS2M37imRmf+pRgp0L566uBTiwCMDTgk/5pfPMc3eTyISaCEL4 iAYYcw2JbLbYC+0KJs2AetAV9bnwAHCuN4XMebzXROPGs6iYOQGOA4n3+1bQ== MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Thu, 19 Feb 2026 22:39:55 -0000 Message-ID: <177154079595.25.11008091249156812316@29965ddac10e> Message-ID-Hash: Z67WNNQLKOBGZC6DJXXN23GXDN6N6AX6 X-Message-ID-Hash: Z67WNNQLKOBGZC6DJXXN23GXDN6N6AX6 X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PR] Convert a few filters to compile-time SPIR-V (PR #21810) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Lynne via ffmpeg-devel Cc: Lynne Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #21810 opened by Lynne URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21810 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21810.patch It only takes 10 minutes. >>From e730556dca8b33820b08969e9e89ffe9c5a0e483 Mon Sep 17 00:00:00 2001 From: Lynne Date: Tue, 10 Feb 2026 20:36:28 +0100 Subject: [PATCH 1/7] vf_flip_vulkan: convert to compile-time SPIR-V generation --- configure | 6 +- libavfilter/vf_flip_vulkan.c | 101 ++++++++---------------------- libavfilter/vulkan/Makefile | 1 + libavfilter/vulkan/flip.comp.glsl | 59 +++++++++++++++++ 4 files changed, 88 insertions(+), 79 deletions(-) create mode 100644 libavfilter/vulkan/flip.comp.glsl diff --git a/configure b/configure index b629173712..f2531aacf0 100755 --- a/configure +++ b/configure @@ -4113,7 +4113,7 @@ elbg_filter_deps="avcodec" eq_filter_deps="gpl" erosion_opencl_filter_deps="opencl" find_rect_filter_deps="avcodec avformat gpl" -flip_vulkan_filter_deps="vulkan spirv_library" +flip_vulkan_filter_deps="vulkan spirv_compiler" flite_filter_deps="libflite threads" framerate_filter_select="scene_sad" freezedetect_filter_select="scene_sad" @@ -4123,7 +4123,7 @@ frei0r_src_filter_deps="frei0r" fspp_filter_deps="gpl" fsync_filter_deps="avformat" gblur_vulkan_filter_deps="vulkan spirv_library" -hflip_vulkan_filter_deps="vulkan spirv_library" +hflip_vulkan_filter_deps="vulkan spirv_compiler" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" iccdetect_filter_deps="lcms2" @@ -4215,7 +4215,7 @@ transpose_vulkan_filter_deps="vulkan spirv_library" unsharp_opencl_filter_deps="opencl" uspp_filter_deps="gpl avcodec" vaguedenoiser_filter_deps="gpl" -vflip_vulkan_filter_deps="vulkan spirv_library" +vflip_vulkan_filter_deps="vulkan spirv_compiler" vidstabdetect_filter_deps="libvidstab" vidstabtransform_filter_deps="libvidstab" libvmaf_filter_deps="libvmaf" diff --git a/libavfilter/vf_flip_vulkan.c b/libavfilter/vf_flip_vulkan.c index 3e2aed0fda..72fba7d972 100644 --- a/libavfilter/vf_flip_vulkan.c +++ b/libavfilter/vf_flip_vulkan.c @@ -19,14 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/random_seed.h" #include "libavutil/opt.h" -#include "libavutil/vulkan_spirv.h" #include "vulkan_filter.h" #include "filters.h" #include "video.h" +extern const unsigned char ff_flip_comp_spv_data[]; +extern const unsigned int ff_flip_comp_spv_len; + enum FlipType { FLIP_VERTICAL, FLIP_HORIZONTAL, @@ -42,24 +43,13 @@ typedef struct FlipVulkanContext { FFVulkanShader shd; } FlipVulkanContext; -static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType type) +static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err = 0; - uint8_t *spv_data; - size_t spv_len; - void *spv_opaque = NULL; FlipVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); FFVulkanShader *shd = &s->shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc; - - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; - } s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); if (!s->qf) { @@ -69,77 +59,36 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in, enum FlipType } RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); - RET(ff_vk_shader_init(vkctx, &s->shd, "flip", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 32, 1, - 0)); - desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "input_image", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.input_format, FF_VK_REP_FLOAT), - .mem_quali = "readonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 32, 8, planes }, 0); + + ff_vk_shader_add_push_const(&s->shd, 0, sizeof(int), + VK_SHADER_STAGE_COMPUTE_BIT); + + const FFVulkanDescriptorSetBinding desc[] = { + { /* input_img */ + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, - { - .name = "output_image", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format, FF_VK_REP_FLOAT), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, + { /* output_img */ + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, }; + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0); - RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_image[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (type) - { - case FLIP_HORIZONTAL: - GLSLF(2, vec4 res = imageLoad(input_image[%i], ivec2(size.x - pos.x, pos.y)); ,i); - break; - case FLIP_VERTICAL: - GLSLF(2, vec4 res = imageLoad(input_image[%i], ivec2(pos.x, size.y - pos.y)); ,i); - break; - case FLIP_BOTH: - GLSLF(2, vec4 res = imageLoad(input_image[%i], ivec2(size.xy - pos.xy));, i); - break; - default: - GLSLF(2, vec4 res = imageLoad(input_image[%i], pos); ,i); - break; - } - GLSLF(2, imageStore(output_image[%i], pos, res); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(spv->compile_shader(vkctx, spv, shd, &spv_data, &spv_len, "main", - &spv_opaque)); - RET(ff_vk_shader_link(vkctx, shd, spv_data, spv_len, "main")); + RET(ff_vk_shader_link(vkctx, shd, + ff_flip_comp_spv_data, + ff_flip_comp_spv_len, "main")); RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd)); s->initialized = 1; fail: - if (spv_opaque) - spv->free_shader(spv, &spv_opaque); - if (spv) - spv->uninit(&spv); - return err; } @@ -171,10 +120,10 @@ static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) } if (!s->initialized) - RET(init_filter(ctx, in, type)); + RET(init_filter(ctx, in)); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - VK_NULL_HANDLE, NULL, 0)); + VK_NULL_HANDLE, &type, sizeof(int))); RET(av_frame_copy_props(out, in)); diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index dc15353e9e..bb9aa1baab 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -4,3 +4,4 @@ clean:: OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vulkan/avgblur.comp.spv.o OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vulkan/bwdif.comp.spv.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/debayer.comp.spv.o +OBJS-$(CONFIG_FLIP_VULKAN_FILTER) += vulkan/flip.comp.spv.o diff --git a/libavfilter/vulkan/flip.comp.glsl b/libavfilter/vulkan/flip.comp.glsl new file mode 100644 index 0000000000..f711586238 --- /dev/null +++ b/libavfilter/vulkan/flip.comp.glsl @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2021 Wu Jianhua + * Copyright (c) 2026 Lynne + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma shader_stage(compute) + +#extension GL_EXT_shader_image_load_formatted : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; + +layout (set = 0, binding = 0) uniform readonly image2D input_img[]; +layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; + +#define FLIP_VERTICAL 0 +#define FLIP_HORIZONTAL 1 +#define FLIP_BOTH 2 + +layout (push_constant, scalar) uniform pushConstants { + int flip; +}; + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(input_img[nonuniformEXT(gl_LocalInvocationID.z)]); + if (any(greaterThanEqual(pos, size))) + return; + + ivec2 dst; + switch (flip) { + case FLIP_HORIZONTAL: dst = ivec2(size.x - pos.x, pos.y); break; + case FLIP_VERTICAL: dst = ivec2(pos.x, size.y - pos.y); break; + case FLIP_BOTH: dst = ivec2(size.xy - pos.xy); break; + default: dst = pos; break; + } + + vec4 res = imageLoad(input_img[nonuniformEXT(gl_LocalInvocationID.z)], pos); + + imageStore(output_img[nonuniformEXT(gl_LocalInvocationID.z)], pos, res); +} -- 2.52.0 >>From 7a32f4c32a659aff70bc15662a75f62635ffe65f Mon Sep 17 00:00:00 2001 From: Lynne Date: Tue, 10 Feb 2026 20:51:38 +0100 Subject: [PATCH 2/7] vf_transpose_vulkan: convert to compile-time SPIR-V generation --- configure | 2 +- libavfilter/vf_transpose_vulkan.c | 95 +++++++------------------- libavfilter/vulkan/Makefile | 1 + libavfilter/vulkan/transpose.comp.glsl | 63 +++++++++++++++++ 4 files changed, 90 insertions(+), 71 deletions(-) create mode 100644 libavfilter/vulkan/transpose.comp.glsl diff --git a/configure b/configure index f2531aacf0..d1b5c89ae5 100755 --- a/configure +++ b/configure @@ -4211,7 +4211,7 @@ tonemap_opencl_filter_deps="opencl const_nan" transpose_opencl_filter_deps="opencl" transpose_vaapi_filter_deps="vaapi VAProcPipelineCaps_rotation_flags" transpose_vt_filter_deps="videotoolbox VTPixelRotationSessionCreate" -transpose_vulkan_filter_deps="vulkan spirv_library" +transpose_vulkan_filter_deps="vulkan spirv_compiler" unsharp_opencl_filter_deps="opencl" uspp_filter_deps="gpl avcodec" vaguedenoiser_filter_deps="gpl" diff --git a/libavfilter/vf_transpose_vulkan.c b/libavfilter/vf_transpose_vulkan.c index 3fe2d11cb2..2665ababfe 100644 --- a/libavfilter/vf_transpose_vulkan.c +++ b/libavfilter/vf_transpose_vulkan.c @@ -19,15 +19,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/random_seed.h" #include "libavutil/opt.h" -#include "libavutil/vulkan_spirv.h" #include "vulkan_filter.h" #include "filters.h" #include "transpose.h" #include "video.h" +extern const unsigned char ff_transpose_comp_spv_data[]; +extern const unsigned int ff_transpose_comp_spv_len; + typedef struct TransposeVulkanContext { FFVulkanContext vkctx; @@ -43,22 +44,9 @@ typedef struct TransposeVulkanContext { static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - uint8_t *spv_data; - size_t spv_len; - void *spv_opaque = NULL; TransposeVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - FFVulkanShader *shd = &s->shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc; - - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; - } s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); if (!s->qf) { @@ -68,70 +56,36 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) } RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); - RET(ff_vk_shader_init(vkctx, &s->shd, "transpose", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 1, 1, - 0)); - desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "input_images", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.input_format, FF_VK_REP_FLOAT), - .mem_quali = "readonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 32, 1, planes }, 0); + + ff_vk_shader_add_push_const(&s->shd, 0, sizeof(int), + VK_SHADER_STAGE_COMPUTE_BIT); + + const FFVulkanDescriptorSetBinding desc[] = { + { /* input_img */ + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, - { - .name = "output_images", - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format, FF_VK_REP_FLOAT), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, - .stages = VK_SHADER_STAGE_COMPUTE_BIT, + { /* output_img */ + .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, }; + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0); - RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - if (s->dir == TRANSPOSE_CCLOCK) - GLSLF(2, vec4 res = imageLoad(input_images[%i], ivec2(size.y - pos.y, pos.x)); ,i); - else if (s->dir == TRANSPOSE_CLOCK_FLIP || s->dir == TRANSPOSE_CLOCK) { - GLSLF(2, vec4 res = imageLoad(input_images[%i], ivec2(size.yx - pos.yx)); ,i); - if (s->dir == TRANSPOSE_CLOCK) - GLSLC(2, pos = ivec2(pos.x, size.y - pos.y); ); - } else - GLSLF(2, vec4 res = imageLoad(input_images[%i], pos.yx); ,i); - GLSLF(2, imageStore(output_images[%i], pos, res); ,i); - GLSLC(1, } ); - } - GLSLC(0, } ); - - RET(spv->compile_shader(vkctx, spv, shd, &spv_data, &spv_len, "main", - &spv_opaque)); - RET(ff_vk_shader_link(vkctx, shd, spv_data, spv_len, "main")); + RET(ff_vk_shader_link(vkctx, &s->shd, + ff_transpose_comp_spv_data, + ff_transpose_comp_spv_len, "main")); RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd)); s->initialized = 1; fail: - if (spv_opaque) - spv->free_shader(spv, &spv_opaque); - if (spv) - spv->uninit(&spv); - return err; } @@ -156,7 +110,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) RET(init_filter(ctx, in)); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - VK_NULL_HANDLE, NULL, 0)); + VK_NULL_HANDLE, &s->dir, sizeof(int))); RET(av_frame_copy_props(out, in)); @@ -223,7 +177,8 @@ static int config_props_output(AVFilterLink *outlink) } #define OFFSET(x) offsetof(TransposeVulkanContext, x) -#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM) +#define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM | \ + AV_OPT_FLAG_RUNTIME_PARAM) static const AVOption transpose_vulkan_options[] = { { "dir", "set transpose direction", OFFSET(dir), AV_OPT_TYPE_INT, { .i64 = TRANSPOSE_CCLOCK_FLIP }, 0, 7, FLAGS, .unit = "dir" }, diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index bb9aa1baab..e47655c8bd 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -5,3 +5,4 @@ OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vulkan/avgblur.comp.spv.o OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vulkan/bwdif.comp.spv.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/debayer.comp.spv.o OBJS-$(CONFIG_FLIP_VULKAN_FILTER) += vulkan/flip.comp.spv.o +OBJS-$(CONFIG_TRANSPOSE_VULKAN_FILTER) += vulkan/transpose.comp.spv.o diff --git a/libavfilter/vulkan/transpose.comp.glsl b/libavfilter/vulkan/transpose.comp.glsl new file mode 100644 index 0000000000..a8ab6b5843 --- /dev/null +++ b/libavfilter/vulkan/transpose.comp.glsl @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Wu Jianhua + * Copyright (c) 2026 Lynne + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma shader_stage(compute) + +#extension GL_EXT_shader_image_load_formatted : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; + +layout (set = 0, binding = 0) uniform readonly image2D input_img[]; +layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; + +#define TRANSPOSE_CCLOCK_FLIP 0 +#define TRANSPOSE_CLOCK 1 +#define TRANSPOSE_CCLOCK 2 +#define TRANSPOSE_CLOCK_FLIP 3 +#define TRANSPOSE_REVERSAL 4 +#define TRANSPOSE_HFLIP 5 +#define TRANSPOSE_VFLIP 6 + +layout (push_constant, scalar) uniform pushConstants { + int dir; +}; + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(input_img[nonuniformEXT(gl_LocalInvocationID.z)]); + if (any(greaterThanEqual(pos, size))) + return; + + ivec2 dst; + switch (dir) { + case TRANSPOSE_CCLOCK: dst = ivec2(size.y - pos.y, pos.x); break; + case TRANSPOSE_CLOCK: pos = ivec2(pos.x, size.y - pos.y); /* fall */ + case TRANSPOSE_CLOCK_FLIP: dst = ivec2(size.yx - pos.yx); break; + default: dst = pos.yx; break; + } + + vec4 res = imageLoad(input_img[nonuniformEXT(gl_LocalInvocationID.z)], pos); + + imageStore(output_img[nonuniformEXT(gl_LocalInvocationID.z)], pos, res); +} -- 2.52.0 >>From 8b15ff524fa0241965c7d3a638e235ea4b9e7c84 Mon Sep 17 00:00:00 2001 From: Lynne Date: Thu, 12 Feb 2026 11:33:07 +0100 Subject: [PATCH 3/7] vulkan_filter: add an argument for setting the Z workgroup count --- libavfilter/vf_avgblur_vulkan.c | 2 +- libavfilter/vf_blend_vulkan.c | 2 +- libavfilter/vf_bwdif_vulkan.c | 2 +- libavfilter/vf_chromaber_vulkan.c | 2 +- libavfilter/vf_flip_vulkan.c | 2 +- libavfilter/vf_gblur_vulkan.c | 2 +- libavfilter/vf_interlace_vulkan.c | 2 +- libavfilter/vf_overlay_vulkan.c | 2 +- libavfilter/vf_scale_vulkan.c | 2 +- libavfilter/vf_transpose_vulkan.c | 2 +- libavfilter/vf_xfade_vulkan.c | 2 +- libavfilter/vsrc_testsrc_vulkan.c | 4 ++-- libavfilter/vulkan_filter.c | 15 +++++++++------ libavfilter/vulkan_filter.h | 9 ++++++--- 14 files changed, 28 insertions(+), 22 deletions(-) diff --git a/libavfilter/vf_avgblur_vulkan.c b/libavfilter/vf_avgblur_vulkan.c index db0b1b8612..3716f62ccd 100644 --- a/libavfilter/vf_avgblur_vulkan.c +++ b/libavfilter/vf_avgblur_vulkan.c @@ -122,7 +122,7 @@ static int avgblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, VK_NULL_HANDLE, - &s->opts, sizeof(s->opts))); + 1, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) diff --git a/libavfilter/vf_blend_vulkan.c b/libavfilter/vf_blend_vulkan.c index 57cf3c696b..a6492a5c32 100644 --- a/libavfilter/vf_blend_vulkan.c +++ b/libavfilter/vf_blend_vulkan.c @@ -264,7 +264,7 @@ static int blend_frame(FFFrameSync *fs) RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out, (AVFrame *[]){ top, bottom }, 2, - VK_NULL_HANDLE, NULL, 0)); + VK_NULL_HANDLE, 1, NULL, 0)); return ff_filter_frame(outlink, out); diff --git a/libavfilter/vf_bwdif_vulkan.c b/libavfilter/vf_bwdif_vulkan.c index 5fefb3396d..ea4154daf5 100644 --- a/libavfilter/vf_bwdif_vulkan.c +++ b/libavfilter/vf_bwdif_vulkan.c @@ -115,7 +115,7 @@ static void bwdif_vulkan_filter_frame(AVFilterContext *ctx, AVFrame *dst, ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, dst, (AVFrame *[]){ y->prev, y->cur, y->next }, 3, - VK_NULL_HANDLE, ¶ms, sizeof(params)); + VK_NULL_HANDLE, 1, ¶ms, sizeof(params)); if (y->current_field == YADIF_FIELD_END) y->current_field = YADIF_FIELD_NORMAL; diff --git a/libavfilter/vf_chromaber_vulkan.c b/libavfilter/vf_chromaber_vulkan.c index 65b53afd64..6dd50c1bb4 100644 --- a/libavfilter/vf_chromaber_vulkan.c +++ b/libavfilter/vf_chromaber_vulkan.c @@ -188,7 +188,7 @@ static int chromaber_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) RET(init_filter(ctx, in)); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - s->sampler, &s->opts, sizeof(s->opts))); + s->sampler, 1, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) diff --git a/libavfilter/vf_flip_vulkan.c b/libavfilter/vf_flip_vulkan.c index 72fba7d972..b86f236d2a 100644 --- a/libavfilter/vf_flip_vulkan.c +++ b/libavfilter/vf_flip_vulkan.c @@ -123,7 +123,7 @@ static int filter_frame(AVFilterLink *link, AVFrame *in, enum FlipType type) RET(init_filter(ctx, in)); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - VK_NULL_HANDLE, &type, sizeof(int))); + VK_NULL_HANDLE, 1, &type, sizeof(int))); RET(av_frame_copy_props(out, in)); diff --git a/libavfilter/vf_gblur_vulkan.c b/libavfilter/vf_gblur_vulkan.c index 1b447e2754..8ade1c955d 100644 --- a/libavfilter/vf_gblur_vulkan.c +++ b/libavfilter/vf_gblur_vulkan.c @@ -316,7 +316,7 @@ static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) RET(ff_vk_filter_process_2pass(&s->vkctx, &s->e, (FFVulkanShader *[2]){ &s->shd_hor, &s->shd_ver }, - out, tmp, in, VK_NULL_HANDLE, NULL, 0)); + out, tmp, in, VK_NULL_HANDLE, 1, NULL, 0)); err = av_frame_copy_props(out, in); if (err < 0) diff --git a/libavfilter/vf_interlace_vulkan.c b/libavfilter/vf_interlace_vulkan.c index 7afb30c2d7..afa8a634f8 100644 --- a/libavfilter/vf_interlace_vulkan.c +++ b/libavfilter/vf_interlace_vulkan.c @@ -218,7 +218,7 @@ static int interlace_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out, (AVFrame *[]){ input_top, input_bot }, 2, - s->sampler, NULL, 0)); + s->sampler, 1, NULL, 0)); err = av_frame_copy_props(out, s->cur); if (err < 0) diff --git a/libavfilter/vf_overlay_vulkan.c b/libavfilter/vf_overlay_vulkan.c index 1f9eed8e08..e1a07e4bfd 100644 --- a/libavfilter/vf_overlay_vulkan.c +++ b/libavfilter/vf_overlay_vulkan.c @@ -240,7 +240,7 @@ static int overlay_vulkan_blend(FFFrameSync *fs) RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, out, (AVFrame *[]){ input_main, input_overlay }, 2, - VK_NULL_HANDLE, &s->opts, sizeof(s->opts))); + VK_NULL_HANDLE, 1, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, input_main); if (err < 0) diff --git a/libavfilter/vf_scale_vulkan.c b/libavfilter/vf_scale_vulkan.c index a1e4f59a72..e7e23826ed 100644 --- a/libavfilter/vf_scale_vulkan.c +++ b/libavfilter/vf_scale_vulkan.c @@ -372,7 +372,7 @@ static int scale_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) s->opts.crop_h = in->height - (in->crop_top + in->crop_bottom); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - s->sampler, &s->opts, sizeof(s->opts))); + s->sampler, 1, &s->opts, sizeof(s->opts))); err = av_frame_copy_props(out, in); if (err < 0) diff --git a/libavfilter/vf_transpose_vulkan.c b/libavfilter/vf_transpose_vulkan.c index 2665ababfe..e193bb6829 100644 --- a/libavfilter/vf_transpose_vulkan.c +++ b/libavfilter/vf_transpose_vulkan.c @@ -110,7 +110,7 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in) RET(init_filter(ctx, in)); RET(ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, out, in, - VK_NULL_HANDLE, &s->dir, sizeof(int))); + VK_NULL_HANDLE, 1, &s->dir, sizeof(int))); RET(av_frame_copy_props(out, in)); diff --git a/libavfilter/vf_xfade_vulkan.c b/libavfilter/vf_xfade_vulkan.c index 58e8797733..3aa96d62b0 100644 --- a/libavfilter/vf_xfade_vulkan.c +++ b/libavfilter/vf_xfade_vulkan.c @@ -447,7 +447,7 @@ static int xfade_frame(AVFilterContext *avctx, AVFrame *frame_a, AVFrame *frame_ 0.f, 1.f); RET(ff_vk_filter_process_Nin(&s->vkctx, &s->e, &s->shd, output, - (AVFrame *[]){ frame_a, frame_b }, 2, s->sampler, + (AVFrame *[]){ frame_a, frame_b }, 2, s->sampler, 1, &(XFadeParameters){ progress }, sizeof(XFadeParameters))); return ff_filter_frame(outlink, output); diff --git a/libavfilter/vsrc_testsrc_vulkan.c b/libavfilter/vsrc_testsrc_vulkan.c index cb3c787213..68cb8b9ef5 100644 --- a/libavfilter/vsrc_testsrc_vulkan.c +++ b/libavfilter/vsrc_testsrc_vulkan.c @@ -235,7 +235,7 @@ static int testsrc_vulkan_activate(AVFilterContext *ctx) return AVERROR(ENOMEM); err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, s->picref, NULL, - VK_NULL_HANDLE, &s->opts, sizeof(s->opts)); + VK_NULL_HANDLE, 1, &s->opts, sizeof(s->opts)); if (err < 0) return err; } @@ -254,7 +254,7 @@ static int testsrc_vulkan_activate(AVFilterContext *ctx) frame->sample_aspect_ratio = s->sar; if (!s->draw_once) { err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, frame, NULL, - VK_NULL_HANDLE, &s->opts, sizeof(s->opts)); + VK_NULL_HANDLE, 1, &s->opts, sizeof(s->opts)); if (err < 0) { av_frame_free(&frame); return err; diff --git a/libavfilter/vulkan_filter.c b/libavfilter/vulkan_filter.c index 44a4ce7242..2fc467c6d9 100644 --- a/libavfilter/vulkan_filter.c +++ b/libavfilter/vulkan_filter.c @@ -241,7 +241,8 @@ int ff_vk_filter_init(AVFilterContext *avctx) int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd, AVFrame *out_f, AVFrame *in_f, - VkSampler sampler, void *push_src, size_t push_size) + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size) { int err = 0; FFVulkanFunctions *vk = &vkctx->vkfn; @@ -304,7 +305,7 @@ int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, vk->CmdDispatch(exec->buf, FFALIGN(vkctx->output_width, shd->lg_size[0])/shd->lg_size[0], FFALIGN(vkctx->output_height, shd->lg_size[1])/shd->lg_size[1], - 1); + wgc_z); return ff_vk_exec_submit(vkctx, exec); fail: @@ -315,7 +316,8 @@ fail: int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd_list[2], AVFrame *out, AVFrame *tmp, AVFrame *in, - VkSampler sampler, void *push_src, size_t push_size) + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size) { int err = 0; FFVulkanFunctions *vk = &vkctx->vkfn; @@ -395,7 +397,7 @@ int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, vk->CmdDispatch(exec->buf, FFALIGN(vkctx->output_width, shd->lg_size[0])/shd->lg_size[0], FFALIGN(vkctx->output_height, shd->lg_size[1])/shd->lg_size[1], - 1); + wgc_z); } return ff_vk_exec_submit(vkctx, exec); @@ -407,7 +409,8 @@ fail: int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd, AVFrame *out, AVFrame *in[], int nb_in, - VkSampler sampler, void *push_src, size_t push_size) + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size) { int err = 0; FFVulkanFunctions *vk = &vkctx->vkfn; @@ -474,7 +477,7 @@ int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, vk->CmdDispatch(exec->buf, FFALIGN(vkctx->output_width, shd->lg_size[0])/shd->lg_size[0], FFALIGN(vkctx->output_height, shd->lg_size[1])/shd->lg_size[1], - 1); + wgc_z); return ff_vk_exec_submit(vkctx, exec); fail: diff --git a/libavfilter/vulkan_filter.h b/libavfilter/vulkan_filter.h index 6ed9c4de39..c54c8c56c4 100644 --- a/libavfilter/vulkan_filter.h +++ b/libavfilter/vulkan_filter.h @@ -44,7 +44,8 @@ int ff_vk_filter_init_context(AVFilterContext *avctx, FFVulkanContext *s, */ int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd, AVFrame *out_f, AVFrame *in_f, - VkSampler sampler, void *push_src, size_t push_size); + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size); /** * Submit a compute shader with a single in and single out with 2 stages. @@ -52,7 +53,8 @@ int ff_vk_filter_process_simple(FFVulkanContext *vkctx, FFVkExecPool *e, int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd_list[2], AVFrame *out, AVFrame *tmp, AVFrame *in, - VkSampler sampler, void *push_src, size_t push_size); + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size); /** * Up to 16 inputs, one output @@ -60,6 +62,7 @@ int ff_vk_filter_process_2pass(FFVulkanContext *vkctx, FFVkExecPool *e, int ff_vk_filter_process_Nin(FFVulkanContext *vkctx, FFVkExecPool *e, FFVulkanShader *shd, AVFrame *out, AVFrame *in[], int nb_in, - VkSampler sampler, void *push_src, size_t push_size); + VkSampler sampler, uint32_t wgc_z, + void *push_src, size_t push_size); #endif /* AVFILTER_VULKAN_FILTER_H */ -- 2.52.0 >>From 8e384c57d708ee5fdc0d2ded29aa1631f0e13810 Mon Sep 17 00:00:00 2001 From: Lynne Date: Thu, 12 Feb 2026 12:45:31 +0100 Subject: [PATCH 4/7] vulkan: add LUT permutation table for BGR0/BGRA Fixes filters. --- libavutil/vulkan.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libavutil/vulkan.c b/libavutil/vulkan.c index 5264abd7ad..334f913e28 100644 --- a/libavutil/vulkan.c +++ b/libavutil/vulkan.c @@ -1585,6 +1585,13 @@ void ff_vk_set_perm(enum AVPixelFormat pix_fmt, int lut[4], int inv) lut[2] = 1; lut[3] = 3; break; + case AV_PIX_FMT_BGRA: + case AV_PIX_FMT_BGR0: + lut[0] = 2; + lut[1] = 1; + lut[2] = 0; + lut[3] = 3; + break; default: lut[0] = 0; lut[1] = 1; -- 2.52.0 >>From f71d68e27d747a7cc83b9eea559ed3927900a1a5 Mon Sep 17 00:00:00 2001 From: Lynne Date: Thu, 12 Feb 2026 12:02:00 +0100 Subject: [PATCH 5/7] vsrc_testsrc_vulkan: convert to compile-time SPIR-V generation --- configure | 2 +- libavfilter/vsrc_testsrc_vulkan.c | 77 +++++++++--------------------- libavfilter/vulkan/Makefile | 1 + libavfilter/vulkan/color.comp.glsl | 45 +++++++++++++++++ 4 files changed, 70 insertions(+), 55 deletions(-) create mode 100644 libavfilter/vulkan/color.comp.glsl diff --git a/configure b/configure index d1b5c89ae5..58682c9c27 100755 --- a/configure +++ b/configure @@ -4084,7 +4084,7 @@ bwdif_cuda_filter_deps="ffnvcodec" bwdif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" bwdif_vulkan_filter_deps="vulkan spirv_compiler" chromaber_vulkan_filter_deps="vulkan spirv_library" -color_vulkan_filter_deps="vulkan spirv_library" +color_vulkan_filter_deps="vulkan spirv_compiler" colorkey_opencl_filter_deps="opencl" colormatrix_filter_deps="gpl" convolution_opencl_filter_deps="opencl" diff --git a/libavfilter/vsrc_testsrc_vulkan.c b/libavfilter/vsrc_testsrc_vulkan.c index 68cb8b9ef5..60cd285dd9 100644 --- a/libavfilter/vsrc_testsrc_vulkan.c +++ b/libavfilter/vsrc_testsrc_vulkan.c @@ -18,10 +18,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/random_seed.h" #include "libavutil/csp.h" #include "libavutil/opt.h" -#include "libavutil/vulkan_spirv.h" #include "vulkan_filter.h" #include "filters.h" #include "colorspace.h" @@ -31,8 +29,11 @@ enum TestSrcVulkanMode { TESTSRC_COLOR, }; +extern const unsigned char ff_color_comp_spv_data[]; +extern const unsigned int ff_color_comp_spv_len; + typedef struct TestSrcVulkanPushData { - float color_comp[4]; + float color_data[4][4]; } TestSrcVulkanPushData; typedef struct TestSrcVulkanContext { @@ -65,23 +66,11 @@ typedef struct TestSrcVulkanContext { static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode) { int err; - uint8_t *spv_data; - size_t spv_len; - void *spv_opaque = NULL; TestSrcVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - FFVulkanShader *shd = &s->shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc_set; const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(s->vkctx.output_format); - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; - } - s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); if (!s->qf) { av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n"); @@ -90,37 +79,22 @@ static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode } RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); - RET(ff_vk_shader_init(vkctx, &s->shd, "scale", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 32, 1, - 0)); - GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); - GLSLC(1, vec4 color_comp; ); - GLSLC(0, }; ); - GLSLC(0, ); + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 32, 32, 1 }, 0); ff_vk_shader_add_push_const(&s->shd, 0, sizeof(s->opts), VK_SHADER_STAGE_COMPUTE_BIT); - desc_set = (FFVulkanDescriptorSetBinding []) { - { - .name = "output_img", + const FFVulkanDescriptorSetBinding desc_set[] = { + { /* output_img */ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format, FF_VK_REP_FLOAT), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, }; + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 1, 0, 0); - RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc_set, 1, 0, 0)); - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); if (mode == TESTSRC_COLOR) { double rgb2yuv[3][3]; double rgbad[4]; @@ -167,38 +141,30 @@ static av_cold int init_filter(AVFilterContext *ctx, enum TestSrcVulkanMode mode if (desc->nb_components <= 2) yuvad[1] = yuvad[3]; + float color_comp[4]; for (int i = 0; i < 4; i++) - s->opts.color_comp[i] = yuvad[i]; + color_comp[i] = yuvad[i]; - GLSLC(1, vec4 r; ); - GLSLC(0, ); + int fmt_lut[4]; + ff_vk_set_perm(s->vkctx.output_format, fmt_lut, 0); for (int i = 0, c_off = 0; i < planes; i++) { for (int c = 0; c < desc->nb_components; c++) { if (desc->comp[c].plane == i) { int off = desc->comp[c].offset / (FFALIGN(desc->comp[c].depth, 8)/8); - GLSLF(1, r[%i] = color_comp[%i]; ,off, c_off++); + s->opts.color_data[i][off] = color_comp[fmt_lut[c_off++]]; } } - GLSLF(1, imageStore(output_img[%i], pos, r); ,i); - GLSLC(0, ); } - } - GLSLC(0, } ); - RET(spv->compile_shader(vkctx, spv, shd, &spv_data, &spv_len, "main", - &spv_opaque)); - RET(ff_vk_shader_link(vkctx, shd, spv_data, spv_len, "main")); + RET(ff_vk_shader_link(vkctx, &s->shd, ff_color_comp_spv_data, + ff_color_comp_spv_len, "main")); + } RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd)); s->initialized = 1; fail: - if (spv_opaque) - spv->free_shader(spv, &spv_opaque); - if (spv) - spv->uninit(&spv); - return err; } @@ -207,6 +173,7 @@ static int testsrc_vulkan_activate(AVFilterContext *ctx) int err; AVFilterLink *outlink = ctx->outputs[0]; TestSrcVulkanContext *s = ctx->priv; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); AVFrame *frame; if (!s->initialized) { @@ -235,7 +202,8 @@ static int testsrc_vulkan_activate(AVFilterContext *ctx) return AVERROR(ENOMEM); err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, s->picref, NULL, - VK_NULL_HANDLE, 1, &s->opts, sizeof(s->opts)); + VK_NULL_HANDLE, planes, + &s->opts, sizeof(s->opts)); if (err < 0) return err; } @@ -254,7 +222,8 @@ static int testsrc_vulkan_activate(AVFilterContext *ctx) frame->sample_aspect_ratio = s->sar; if (!s->draw_once) { err = ff_vk_filter_process_simple(&s->vkctx, &s->e, &s->shd, frame, NULL, - VK_NULL_HANDLE, 1, &s->opts, sizeof(s->opts)); + VK_NULL_HANDLE, planes, + &s->opts, sizeof(s->opts)); if (err < 0) { av_frame_free(&frame); return err; diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index e47655c8bd..f0d737733c 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -3,6 +3,7 @@ clean:: OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vulkan/avgblur.comp.spv.o OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vulkan/bwdif.comp.spv.o +OBJS-$(CONFIG_COLOR_VULKAN_FILTER) += vulkan/color.comp.spv.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/debayer.comp.spv.o OBJS-$(CONFIG_FLIP_VULKAN_FILTER) += vulkan/flip.comp.spv.o OBJS-$(CONFIG_TRANSPOSE_VULKAN_FILTER) += vulkan/transpose.comp.spv.o diff --git a/libavfilter/vulkan/color.comp.glsl b/libavfilter/vulkan/color.comp.glsl new file mode 100644 index 0000000000..9f23323f24 --- /dev/null +++ b/libavfilter/vulkan/color.comp.glsl @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2026 Lynne + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma shader_stage(compute) + +#extension GL_EXT_shader_image_load_formatted : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; + +layout (set = 0, binding = 0) uniform writeonly image2D output_img[]; + +layout (push_constant, scalar) uniform pushConstants { + mat4 color_data; +}; + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(output_img[nonuniformEXT(gl_WorkGroupID.z)]); + if (any(greaterThanEqual(pos, size))) + return; + + mat4 tmp = color_data; + vec4 res = tmp[gl_WorkGroupID.z]; + imageStore(output_img[nonuniformEXT(gl_WorkGroupID.z)], pos, res); +} -- 2.52.0 >>From 66f62f60f503261308b715b98879512a5bf54480 Mon Sep 17 00:00:00 2001 From: Lynne Date: Thu, 12 Feb 2026 15:07:12 +0100 Subject: [PATCH 6/7] vf_gblur_vulkan: port to compile-time SPIR-V generation --- configure | 2 +- libavfilter/vf_gblur_vulkan.c | 138 +++++++---------------------- libavfilter/vulkan/Makefile | 1 + libavfilter/vulkan/gblur.comp.glsl | 62 +++++++++++++ 4 files changed, 94 insertions(+), 109 deletions(-) create mode 100644 libavfilter/vulkan/gblur.comp.glsl diff --git a/configure b/configure index 58682c9c27..c124582c44 100755 --- a/configure +++ b/configure @@ -4122,7 +4122,7 @@ frei0r_filter_deps="frei0r" frei0r_src_filter_deps="frei0r" fspp_filter_deps="gpl" fsync_filter_deps="avformat" -gblur_vulkan_filter_deps="vulkan spirv_library" +gblur_vulkan_filter_deps="vulkan spirv_compiler" hflip_vulkan_filter_deps="vulkan spirv_compiler" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" diff --git a/libavfilter/vf_gblur_vulkan.c b/libavfilter/vf_gblur_vulkan.c index 8ade1c955d..18d65df8c9 100644 --- a/libavfilter/vf_gblur_vulkan.c +++ b/libavfilter/vf_gblur_vulkan.c @@ -20,24 +20,24 @@ */ #include "libavutil/mem.h" -#include "libavutil/random_seed.h" #include "libavutil/opt.h" -#include "libavutil/vulkan_spirv.h" #include "vulkan_filter.h" #include "filters.h" #include "video.h" -#define CGS 32 #define GBLUR_MAX_KERNEL_SIZE 127 +extern const unsigned char ff_gblur_comp_spv_data[]; +extern const unsigned int ff_gblur_comp_spv_len; + typedef struct GBlurVulkanContext { FFVulkanContext vkctx; int initialized; FFVkExecPool e; AVVulkanDeviceQueueFamily *qf; - VkSampler sampler; + FFVulkanShader shd_hor; FFVkBuffer params_hor; FFVulkanShader shd_ver; @@ -50,20 +50,6 @@ typedef struct GBlurVulkanContext { float sigmaV; } GBlurVulkanContext; -static const char gblur_func[] = { - C(0, void gblur(const ivec2 pos, const int index) ) - C(0, { ) - C(1, vec4 sum = imageLoad(input_images[index], pos) * kernel[0]; ) - C(0, ) - C(1, for(int i = 1; i < kernel.length(); i++) { ) - C(2, sum += imageLoad(input_images[index], pos + OFFSET) * kernel[i]; ) - C(2, sum += imageLoad(input_images[index], pos - OFFSET) * kernel[i]; ) - C(1, } ) - C(0, ) - C(1, imageStore(output_images[index], pos, sum); ) - C(0, } ) -}; - static inline float gaussian(float sigma, float x) { return 1.0 / (sqrt(2.0 * M_PI) * sigma) * @@ -124,50 +110,23 @@ static av_cold void init_gaussian_params(AVFilterContext *ctx) static int init_gblur_pipeline(GBlurVulkanContext *s, FFVulkanShader *shd, FFVkBuffer *params_buf, - int ksize, float sigma, FFVkSPIRVCompiler *spv) + int ksize, float sigma) { int err = 0; uint8_t *kernel_mapped; - uint8_t *spv_data; - size_t spv_len; - void *spv_opaque = NULL; - const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); + ff_vk_shader_add_push_const(shd, 0, sizeof(int), + VK_SHADER_STAGE_COMPUTE_BIT); - FFVulkanDescriptorSetBinding buf_desc = { - .name = "data", + const FFVulkanDescriptorSetBinding buf_desc = { .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, - .mem_quali = "readonly", - .mem_layout = "std430", .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .buf_content = "float kernel", - .buf_elems = ksize, }; + ff_vk_shader_add_descriptor_set(&s->vkctx, shd, &buf_desc, 1, 1, 0); - RET(ff_vk_shader_add_descriptor_set(&s->vkctx, shd, &buf_desc, 1, 1, 0)); - - GLSLD( gblur_func ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, const ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - for (int i = 0; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_images[%i]); ,i); - GLSLC(1, if (!IS_WITHIN(pos, size)) ); - GLSLC(2, return; ); - if (s->planes & (1 << i)) { - GLSLF(1, gblur(pos, %i); ,i); - } else { - GLSLF(1, vec4 res = imageLoad(input_images[%i], pos); ,i); - GLSLF(1, imageStore(output_images[%i], pos, res); ,i); - } - } - GLSLC(0, } ); - - RET(spv->compile_shader(&s->vkctx, spv, shd, &spv_data, &spv_len, "main", - &spv_opaque)); - RET(ff_vk_shader_link(&s->vkctx, shd, spv_data, spv_len, "main")); + RET(ff_vk_shader_link(&s->vkctx, shd, + ff_gblur_comp_spv_data, + ff_gblur_comp_spv_len, "main")); RET(ff_vk_shader_register_exec(&s->vkctx, &s->e, shd)); @@ -184,8 +143,6 @@ static int init_gblur_pipeline(GBlurVulkanContext *s, VK_FORMAT_UNDEFINED)); fail: - if (spv_opaque) - spv->free_shader(spv, &spv_opaque); return err; } @@ -196,16 +153,6 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); - FFVulkanShader *shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc; - - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; - } - s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); if (!s->qf) { av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n"); @@ -215,63 +162,36 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); - desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "input_images", + const FFVulkanDescriptorSetBinding desc[] = { + { /* input_img */ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.input_format, FF_VK_REP_FLOAT), - .mem_quali = "readonly", - .dimensions = 2, - .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, - { - .name = "output_images", + { /* output_img */ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format, FF_VK_REP_FLOAT), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, }; init_gaussian_params(ctx); - { - shd = &s->shd_hor; - RET(ff_vk_shader_init(vkctx, shd, "gblur_hor", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 1, 1, - 0)); + /* Horizontal */ + ff_vk_shader_load(&s->shd_hor, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 32, 1, 1 }, 0); + ff_vk_shader_add_descriptor_set(vkctx, &s->shd_hor, desc, 2, 0, 0); + RET(init_gblur_pipeline(s, &s->shd_hor, &s->params_hor, s->size, s->sigma)); - RET(ff_vk_shader_add_descriptor_set(vkctx, shd, desc, 2, 0, 0)); - - GLSLC(0, #define OFFSET (ivec2(i, 0.0))); - RET(init_gblur_pipeline(s, shd, &s->params_hor, s->size, s->sigma, spv)); - } - - { - shd = &s->shd_ver; - RET(ff_vk_shader_init(vkctx, shd, "gblur_hor", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 1, 32, 1, - 0)); - - RET(ff_vk_shader_add_descriptor_set(vkctx, shd, desc, 2, 0, 0)); - - GLSLC(0, #define OFFSET (ivec2(0.0, i))); - RET(init_gblur_pipeline(s, shd, &s->params_ver, s->sizeV, s->sigmaV, spv)); - } + /* Vertical */ + ff_vk_shader_load(&s->shd_ver, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 1, 32, 1 }, 0); + ff_vk_shader_add_descriptor_set(vkctx, &s->shd_ver, desc, 2, 0, 0); + RET(init_gblur_pipeline(s, &s->shd_ver, &s->params_ver, s->sizeV, s->sigmaV)); s->initialized = 1; fail: - if (spv) - spv->uninit(&spv); - return err; } @@ -298,6 +218,7 @@ static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) AVFilterContext *ctx = link->dst; GBlurVulkanContext *s = ctx->priv; AVFilterLink *outlink = ctx->outputs[0]; + const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); out = ff_get_video_buffer(outlink, outlink->w, outlink->h); if (!out) { @@ -316,7 +237,8 @@ static int gblur_vulkan_filter_frame(AVFilterLink *link, AVFrame *in) RET(ff_vk_filter_process_2pass(&s->vkctx, &s->e, (FFVulkanShader *[2]){ &s->shd_hor, &s->shd_ver }, - out, tmp, in, VK_NULL_HANDLE, 1, NULL, 0)); + out, tmp, in, VK_NULL_HANDLE, + planes, &s->planes, sizeof(int))); err = av_frame_copy_props(out, in); if (err < 0) diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index f0d737733c..47f3f3b75d 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -4,6 +4,7 @@ clean:: OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vulkan/avgblur.comp.spv.o OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vulkan/bwdif.comp.spv.o OBJS-$(CONFIG_COLOR_VULKAN_FILTER) += vulkan/color.comp.spv.o +OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vulkan/gblur.comp.spv.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/debayer.comp.spv.o OBJS-$(CONFIG_FLIP_VULKAN_FILTER) += vulkan/flip.comp.spv.o OBJS-$(CONFIG_TRANSPOSE_VULKAN_FILTER) += vulkan/transpose.comp.spv.o diff --git a/libavfilter/vulkan/gblur.comp.glsl b/libavfilter/vulkan/gblur.comp.glsl new file mode 100644 index 0000000000..77c337020e --- /dev/null +++ b/libavfilter/vulkan/gblur.comp.glsl @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2021-2022 Wu Jianhua + * Copyright (c) 2026 Lynne + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma shader_stage(compute) + +#extension GL_EXT_shader_image_load_formatted : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; + +layout (set = 0, binding = 0) uniform readonly image2D input_img[]; +layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; + +layout (set = 1, binding = 0, scalar) readonly buffer kernel_buf { + float kernel[]; +}; + +layout (push_constant, scalar) uniform pushConstants { + int planes; +}; + +#define P_IDX nonuniformEXT(gl_WorkGroupID.z) +void main() +{ + if (!bool(planes & (1 << gl_WorkGroupID.z))) + return; + + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(input_img[P_IDX]); + if (any(greaterThanEqual(pos, size))) + return; + + vec4 sum = imageLoad(input_img[P_IDX], pos) * kernel[0]; + + for(int i = 1; i < kernel.length(); i++) { + ivec2 offs = gl_WorkGroupSize.x > gl_WorkGroupSize.y ? ivec2(i, 0) : + ivec2(0, i); + sum += imageLoad(input_img[P_IDX], pos + offs) * kernel[i]; + sum += imageLoad(input_img[P_IDX], pos - offs) * kernel[i]; + } + + imageStore(output_img[P_IDX], pos, sum); +} -- 2.52.0 >>From b414c2a7b72cd458b90ca783297eadf07b69232b Mon Sep 17 00:00:00 2001 From: Lynne Date: Thu, 12 Feb 2026 21:57:02 +0100 Subject: [PATCH 7/7] chromaber_vulkan: switch to compile-time SPIR-V generation --- configure | 2 +- libavfilter/vf_chromaber_vulkan.c | 104 ++++--------------------- libavfilter/vulkan/Makefile | 1 + libavfilter/vulkan/chromaber.comp.glsl | 76 ++++++++++++++++++ 4 files changed, 94 insertions(+), 89 deletions(-) create mode 100644 libavfilter/vulkan/chromaber.comp.glsl diff --git a/configure b/configure index c124582c44..31a59730aa 100755 --- a/configure +++ b/configure @@ -4083,7 +4083,7 @@ bs2b_filter_deps="libbs2b" bwdif_cuda_filter_deps="ffnvcodec" bwdif_cuda_filter_deps_any="cuda_nvcc cuda_llvm" bwdif_vulkan_filter_deps="vulkan spirv_compiler" -chromaber_vulkan_filter_deps="vulkan spirv_library" +chromaber_vulkan_filter_deps="vulkan spirv_compiler" color_vulkan_filter_deps="vulkan spirv_compiler" colorkey_opencl_filter_deps="opencl" colormatrix_filter_deps="gpl" diff --git a/libavfilter/vf_chromaber_vulkan.c b/libavfilter/vf_chromaber_vulkan.c index 6dd50c1bb4..fa0bc8e300 100644 --- a/libavfilter/vf_chromaber_vulkan.c +++ b/libavfilter/vf_chromaber_vulkan.c @@ -20,12 +20,14 @@ #include "libavutil/random_seed.h" #include "libavutil/opt.h" -#include "libavutil/vulkan_spirv.h" #include "vulkan_filter.h" #include "filters.h" #include "video.h" +extern const unsigned char ff_chromaber_comp_spv_data[]; +extern const unsigned int ff_chromaber_comp_spv_len; + typedef struct ChromaticAberrationVulkanContext { FFVulkanContext vkctx; @@ -38,56 +40,22 @@ typedef struct ChromaticAberrationVulkanContext { /* Push constants / options */ struct { float dist[2]; + uint32_t single_plane; } opts; } ChromaticAberrationVulkanContext; -static const char distort_chroma_kernel[] = { - C(0, void distort_rgb(ivec2 size, ivec2 pos) ) - C(0, { ) - C(1, const vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; ) - C(1, const vec2 o = p * (dist - 1.0f); ) - C(0, ) - C(1, vec4 res; ) - C(1, res.r = texture(input_img[0], ((p - o)/2.0f) + 0.5f).r; ) - C(1, res.g = texture(input_img[0], ((p )/2.0f) + 0.5f).g; ) - C(1, res.b = texture(input_img[0], ((p + o)/2.0f) + 0.5f).b; ) - C(1, res.a = texture(input_img[0], ((p )/2.0f) + 0.5f).a; ) - C(1, imageStore(output_img[0], pos, res); ) - C(0, } ) - C(0, ) - C(0, void distort_chroma(int idx, ivec2 size, ivec2 pos) ) - C(0, { ) - C(1, vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; ) - C(1, float d = sqrt(p.x*p.x + p.y*p.y); ) - C(1, p *= d / (d*dist); ) - C(1, vec4 res = texture(input_img[idx], (p/2.0f) + 0.5f); ) - C(1, imageStore(output_img[idx], pos, res); ) - C(0, } ) -}; - static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { int err; - uint8_t *spv_data; - size_t spv_len; - void *spv_opaque = NULL; ChromaticAberrationVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; const int planes = av_pix_fmt_count_planes(s->vkctx.output_format); FFVulkanShader *shd = &s->shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc; /* Normalize options */ s->opts.dist[0] = (s->opts.dist[0] / 100.0f) + 1.0f; s->opts.dist[1] = (s->opts.dist[1] / 100.0f) + 1.0f; - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; - } - s->qf = ff_vk_qf_find(vkctx, VK_QUEUE_COMPUTE_BIT, 0); if (!s->qf) { av_log(ctx, AV_LOG_ERROR, "Device has no compute queues\n"); @@ -97,77 +65,37 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) RET(ff_vk_exec_pool_init(vkctx, s->qf, &s->e, s->qf->num*4, 0, 0, 0, NULL)); RET(ff_vk_init_sampler(vkctx, &s->sampler, 0, VK_FILTER_LINEAR)); - RET(ff_vk_shader_init(vkctx, &s->shd, "chromatic_abberation", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 32, 1, - 0)); - GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); - GLSLC(1, vec2 dist; ); - GLSLC(0, }; ); - GLSLC(0, ); + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, NULL, + (uint32_t []) { 32, 32, 1 }, 0); ff_vk_shader_add_push_const(&s->shd, 0, sizeof(s->opts), VK_SHADER_STAGE_COMPUTE_BIT); - desc = (FFVulkanDescriptorSetBinding []) { - { - .name = "input_img", + const FFVulkanDescriptorSetBinding desc[] = { + { /* input_img */ .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .dimensions = 2, - .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, - .samplers = DUP_SAMPLER(s->sampler), + .elems = planes, }, - { - .name = "output_img", + { /* output_img */ .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .mem_layout = ff_vk_shader_rep_fmt(s->vkctx.output_format, FF_VK_REP_FLOAT), - .mem_quali = "writeonly", - .dimensions = 2, - .elems = planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, + .elems = planes, }, }; + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0); - RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0)); - - GLSLD( distort_chroma_kernel ); - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - if (planes == 1) { - GLSLC(1, distort_rgb(imageSize(output_img[0]), pos); ); - } else { - GLSLC(1, ivec2 size = imageSize(output_img[0]); ); - GLSLC(1, vec2 npos = vec2(pos)/vec2(size); ); - GLSLC(1, vec4 res = texture(input_img[0], npos); ); - GLSLC(1, imageStore(output_img[0], pos, res); ); - for (int i = 1; i < planes; i++) { - GLSLC(0, ); - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (!IS_WITHIN(pos, size)) ); - GLSLC(2, return; ); - GLSLF(1, distort_chroma(%i, size, pos); ,i); - } - } - GLSLC(0, } ); - - RET(spv->compile_shader(vkctx, spv, shd, &spv_data, &spv_len, "main", - &spv_opaque)); - RET(ff_vk_shader_link(vkctx, shd, spv_data, spv_len, "main")); + RET(ff_vk_shader_link(vkctx, shd, + ff_chromaber_comp_spv_data, + ff_chromaber_comp_spv_len, "main")); RET(ff_vk_shader_register_exec(vkctx, &s->e, &s->shd)); + s->opts.single_plane = planes == 1; s->initialized = 1; fail: - if (spv_opaque) - spv->free_shader(spv, &spv_opaque); - if (spv) - spv->uninit(&spv); - return err; } diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index 47f3f3b75d..82b3dff054 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -3,6 +3,7 @@ clean:: OBJS-$(CONFIG_AVGBLUR_VULKAN_FILTER) += vulkan/avgblur.comp.spv.o OBJS-$(CONFIG_BWDIF_VULKAN_FILTER) += vulkan/bwdif.comp.spv.o +OBJS-$(CONFIG_CHROMABER_VULKAN_FILTER) += vulkan/chromaber.comp.spv.o OBJS-$(CONFIG_COLOR_VULKAN_FILTER) += vulkan/color.comp.spv.o OBJS-$(CONFIG_GBLUR_VULKAN_FILTER) += vulkan/gblur.comp.spv.o OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/debayer.comp.spv.o diff --git a/libavfilter/vulkan/chromaber.comp.glsl b/libavfilter/vulkan/chromaber.comp.glsl new file mode 100644 index 0000000000..d2e4e60f12 --- /dev/null +++ b/libavfilter/vulkan/chromaber.comp.glsl @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2026 Lynne + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma shader_stage(compute) + +#extension GL_EXT_shader_image_load_formatted : require +#extension GL_EXT_scalar_block_layout : require +#extension GL_EXT_nonuniform_qualifier : require + +layout (local_size_x_id = 253, local_size_y_id = 254, local_size_z_id = 255) in; + +layout (set = 0, binding = 0) uniform sampler2D input_img[]; +layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; + +layout (push_constant, scalar) uniform pushConstants { + vec2 dist; + bool single_plane; +}; + +void distort_rgba(ivec2 pos, ivec2 size) +{ + const vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; + const vec2 o = p * (dist - 1.0f); + + vec4 res; + res.r = texture(input_img[0], ((p - o)/2.0f) + 0.5f).r; + res.g = texture(input_img[0], ((p )/2.0f) + 0.5f).g; + res.b = texture(input_img[0], ((p + o)/2.0f) + 0.5f).b; + res.a = texture(input_img[0], ((p )/2.0f) + 0.5f).a; + imageStore(output_img[0], pos, res); +} + +void distort_plane(ivec2 pos, ivec2 size) +{ + vec2 p = ((vec2(pos)/vec2(size)) - 0.5f)*2.0f; + float d = sqrt(p.x*p.x + p.y*p.y); + p *= d / (d*dist); + vec4 res = texture(input_img[nonuniformEXT(gl_WorkGroupID.z)], (p/2.0f) + 0.5f); + imageStore(output_img[nonuniformEXT(gl_WorkGroupID.z)], pos, res); +} + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = imageSize(output_img[nonuniformEXT(gl_WorkGroupID.z)]); + if (any(greaterThanEqual(pos, size))) + return; + + if (single_plane) { + distort_rgba(pos, pos); + } else { + vec2 npos = vec2(pos)/vec2(size); + vec4 res = texture(input_img[0], npos); + if (gl_WorkGroupID.z == 0) + imageStore(output_img[0], pos, res); + else + distort_plane(pos, size); + } +} -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org