This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 6219d4fdd1fb61b7fff0c713f92dbdd156feaa35 Author: Lynne <[email protected]> AuthorDate: Sun Jul 12 14:10:31 2026 +0900 Commit: Lynne <[email protected]> CommitDate: Mon Jul 13 18:57:37 2026 +0900 vf_scale_vulkan: port to compile-time SPIR-V generation Same functionality as before, but cleaner. --- libavfilter/vf_scale_vulkan.c | 229 +++++++++-------------------------- libavfilter/vulkan/Makefile | 3 +- libavfilter/vulkan/debayer.comp.glsl | 1 + libavfilter/vulkan/scale.comp.glsl | 120 ++++++++++++++++++ 4 files changed, 182 insertions(+), 171 deletions(-) diff --git a/libavfilter/vf_scale_vulkan.c b/libavfilter/vf_scale_vulkan.c index 19b4e5e5ac..4e54d8806c 100644 --- a/libavfilter/vf_scale_vulkan.c +++ b/libavfilter/vf_scale_vulkan.c @@ -18,9 +18,7 @@ * 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 "scale_eval.h" #include "filters.h" @@ -28,6 +26,9 @@ #include "video.h" #include "libswscale/swscale.h" +extern const unsigned char ff_scale_comp_spv_data[]; +extern const unsigned int ff_scale_comp_spv_len; + extern const unsigned char ff_debayer_comp_spv_data[]; extern const unsigned int ff_debayer_comp_spv_len; @@ -45,6 +46,14 @@ enum DebayerFunc { DB_NB, }; +/* Output modes, must match scale.comp.glsl */ +enum ScaleMode { + MODE_COPY = 0, + MODE_NV12, + MODE_YUV420, + MODE_YUV444, +}; + typedef struct ScaleVulkanContext { FFVulkanContext vkctx; SwsContext *sws; @@ -62,6 +71,7 @@ typedef struct ScaleVulkanContext { int crop_y; int crop_w; int crop_h; + float in_dims[2]; } opts; char *out_format_string; @@ -73,145 +83,17 @@ typedef struct ScaleVulkanContext { enum DebayerFunc debayer; } ScaleVulkanContext; -static const char scale_bilinear[] = { - C(0, vec4 scale_bilinear(int idx, ivec2 pos, vec2 crop_range, vec2 crop_off)) - C(0, { ) - C(1, vec2 npos = (vec2(pos) + 0.5f) / imageSize(output_img[idx]); ) - C(1, npos *= crop_range; /* Reduce the range */ ) - C(1, npos += crop_off; /* Offset the start */ ) - C(1, return texture(input_img[idx], npos); ) - C(0, } ) -}; - -static const char rgb2yuv[] = { - C(0, vec4 rgb2yuv(vec4 src, int fullrange) ) - C(0, { ) - C(1, src *= yuv_matrix; ) - C(1, if (fullrange == 1) { ) - C(2, src += vec4(0.0, 0.5, 0.5, 0.0); ) - C(1, } else { ) - C(2, src *= vec4(219.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0, 1.0); ) - C(2, src += vec4(16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 0.0); ) - C(1, } ) - C(1, return src; ) - C(0, } ) -}; - -static const char write_nv12[] = { - C(0, void write_nv12(vec4 src, ivec2 pos) ) - C(0, { ) - C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) - C(1, pos /= ivec2(2); ) - C(1, imageStore(output_img[1], pos, vec4(src.g, src.b, 0.0, 0.0)); ) - C(0, } ) -}; - -static const char write_420[] = { - C(0, void write_420(vec4 src, ivec2 pos) ) - C(0, { ) - C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) - C(1, pos /= ivec2(2); ) - C(1, imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); ) - C(1, imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); ) - C(0, } ) -}; - -static const char write_444[] = { - C(0, void write_444(vec4 src, ivec2 pos) ) - C(0, { ) - C(1, imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); ) - C(1, imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); ) - C(1, imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); ) - C(0, } ) -}; - -static int init_scale_shader(AVFilterContext *ctx, FFVulkanShader *shd, - FFVulkanDescriptorSetBinding *desc, AVFrame *in) -{ - ScaleVulkanContext *s = ctx->priv; - GLSLD( scale_bilinear ); - - if (s->vkctx.output_format != s->vkctx.input_format) { - GLSLD( rgb2yuv ); - } - - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLD(write_nv12); break; - case AV_PIX_FMT_YUV420P: GLSLD( write_420); break; - case AV_PIX_FMT_YUV444P: GLSLD( write_444); break; - default: break; - } - - GLSLC(0, void main() ); - GLSLC(0, { ); - GLSLC(1, ivec2 size; ); - GLSLC(1, ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ); - GLSLF(1, vec2 in_d = vec2(%i, %i); ,in->width, in->height); - GLSLC(1, vec2 c_r = vec2(crop_w, crop_h) / in_d; ); - GLSLC(1, vec2 c_o = vec2(crop_x, crop_y) / in_d; ); - GLSLC(0, ); - - if (s->vkctx.output_format == s->vkctx.input_format) { - for (int i = 0; i < desc[1].elems; i++) { - GLSLF(1, size = imageSize(output_img[%i]); ,i); - GLSLC(1, if (IS_WITHIN(pos, size)) { ); - switch (s->scaler) { - case F_NEAREST: - case F_BILINEAR: - GLSLF(2, vec4 res = scale_bilinear(%i, pos, c_r, c_o); ,i); - GLSLF(2, imageStore(output_img[%i], pos, res); ,i); - break; - }; - GLSLC(1, } ); - } - } else { - GLSLC(1, vec4 res = scale_bilinear(0, pos, c_r, c_o); ); - GLSLF(1, res = rgb2yuv(res, %i); ,s->out_range == AVCOL_RANGE_JPEG); - switch (s->vkctx.output_format) { - case AV_PIX_FMT_NV12: GLSLC(1, write_nv12(res, pos); ); break; - case AV_PIX_FMT_YUV420P: GLSLC(1, write_420(res, pos); ); break; - case AV_PIX_FMT_YUV444P: GLSLC(1, write_444(res, pos); ); break; - default: return AVERROR(EINVAL); - } - } - - GLSLC(0, } ); - - if (s->vkctx.output_format != s->vkctx.input_format) { - const AVLumaCoefficients *lcoeffs; - double tmp_mat[3][3]; - - lcoeffs = av_csp_luma_coeffs_from_avcsp(in->colorspace); - if (!lcoeffs) { - av_log(ctx, AV_LOG_ERROR, "Unsupported colorspace\n"); - return AVERROR(EINVAL); - } - - ff_fill_rgb2yuv_table(lcoeffs, tmp_mat); - - for (int y = 0; y < 3; y++) - for (int x = 0; x < 3; x++) - s->opts.yuv_matrix[x][y] = tmp_mat[x][y]; - s->opts.yuv_matrix[3][3] = 1.0; - } - - return 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; VkFilter sampler_mode; + enum ScaleMode mode; ScaleVulkanContext *s = ctx->priv; FFVulkanContext *vkctx = &s->vkctx; FFVulkanShader *shd = &s->shd; - FFVkSPIRVCompiler *spv; - FFVulkanDescriptorSetBinding *desc; int in_planes = av_pix_fmt_count_planes(s->vkctx.input_format); + int out_planes = av_pix_fmt_count_planes(s->vkctx.output_format); switch (s->scaler) { case F_NEAREST: @@ -222,29 +104,33 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) break; }; - spv = ff_vk_spirv_init(); - if (!spv) { - av_log(ctx, AV_LOG_ERROR, "Unable to initialize SPIR-V compiler!\n"); - return AVERROR_EXTERNAL; + if (s->vkctx.output_format == s->vkctx.input_format) { + mode = MODE_COPY; + } else { + switch (s->vkctx.output_format) { + case AV_PIX_FMT_NV12: mode = MODE_NV12; break; + case AV_PIX_FMT_YUV420P: mode = MODE_YUV420; break; + case AV_PIX_FMT_YUV444P: mode = MODE_YUV444; break; + default: return AVERROR(EINVAL); + } } 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, sampler_mode)); - RET(ff_vk_shader_init(vkctx, &s->shd, "scale", - VK_SHADER_STAGE_COMPUTE_BIT, - NULL, 0, - 32, 32, 1, - 0)); + SPEC_LIST_CREATE(sl, 3, 3*sizeof(int32_t)) + SPEC_LIST_ADD(sl, 0, 32, out_planes); + SPEC_LIST_ADD(sl, 1, 32, mode); + SPEC_LIST_ADD(sl, 2, 32, s->out_range == AVCOL_RANGE_JPEG); + + ff_vk_shader_load(&s->shd, VK_SHADER_STAGE_COMPUTE_BIT, sl, + (uint32_t []) { 32, 32, 1 }, 0); - desc = (FFVulkanDescriptorSetBinding []) { + const FFVulkanDescriptorSetBinding desc[] = { { .name = "input_img", .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .mem_layout = NULL, - .mem_quali = "readonly", - .dimensions = 2, .elems = in_planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, .samplers = DUP_SAMPLER(s->sampler), @@ -252,46 +138,47 @@ static av_cold int init_filter(AVFilterContext *ctx, AVFrame *in) { .name = "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 = av_pix_fmt_count_planes(s->vkctx.output_format), + .elems = out_planes, .stages = VK_SHADER_STAGE_COMPUTE_BIT, }, }; - RET(ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0)); - - GLSLC(0, layout(push_constant, std430) uniform pushConstants { ); - GLSLC(1, mat4 yuv_matrix; ); - GLSLC(1, int crop_x; ); - GLSLC(1, int crop_y; ); - GLSLC(1, int crop_w; ); - GLSLC(1, int crop_h; ); - GLSLC(0, }; ); - GLSLC(0, ); + ff_vk_shader_add_descriptor_set(vkctx, &s->shd, desc, 2, 0, 0); ff_vk_shader_add_push_const(&s->shd, 0, sizeof(s->opts), VK_SHADER_STAGE_COMPUTE_BIT); - err = init_scale_shader(ctx, shd, desc, in); - if (err < 0) - goto fail; + if (mode != MODE_COPY) { + const AVLumaCoefficients *lcoeffs; + double tmp_mat[3][3]; + + lcoeffs = av_csp_luma_coeffs_from_avcsp(in->colorspace); + if (!lcoeffs) { + av_log(ctx, AV_LOG_ERROR, "Unsupported colorspace\n"); + err = AVERROR(EINVAL); + goto fail; + } + + ff_fill_rgb2yuv_table(lcoeffs, tmp_mat); + + for (int y = 0; y < 3; y++) + for (int x = 0; x < 3; x++) + s->opts.yuv_matrix[x][y] = tmp_mat[x][y]; + s->opts.yuv_matrix[3][3] = 1.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")); + s->opts.in_dims[0] = in->width; + s->opts.in_dims[1] = in->height; + + RET(ff_vk_shader_link(vkctx, shd, + ff_scale_comp_spv_data, + ff_scale_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; } @@ -455,6 +342,8 @@ static int scale_vulkan_config_output(AVFilterLink *outlink) if (!s->sws) return AVERROR(ENOMEM); av_opt_set(s->sws, "sws_flags", "unstable", 0); + av_opt_set(s->sws, "scaler", + s->scaler == F_NEAREST ? "point" : "bilinear", 0); } else if (s->vkctx.output_format != s->vkctx.input_format) { if (!ff_vk_mt_is_np_rgb(s->vkctx.input_format)) { av_log(avctx, AV_LOG_ERROR, "Unsupported input format for conversion\n"); diff --git a/libavfilter/vulkan/Makefile b/libavfilter/vulkan/Makefile index 2cfe9cfa93..8a0912b4df 100644 --- a/libavfilter/vulkan/Makefile +++ b/libavfilter/vulkan/Makefile @@ -8,7 +8,8 @@ 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 +OBJS-$(CONFIG_SCALE_VULKAN_FILTER) += vulkan/scale.comp.spv.o \ + vulkan/debayer.comp.spv.o OBJS-$(CONFIG_SCDET_VULKAN_FILTER) += vulkan/scdet.comp.spv.o OBJS-$(CONFIG_OVERLAY_VULKAN_FILTER) += vulkan/overlay.comp.spv.o OBJS-$(CONFIG_FLIP_VULKAN_FILTER) += vulkan/flip.comp.spv.o diff --git a/libavfilter/vulkan/debayer.comp.glsl b/libavfilter/vulkan/debayer.comp.glsl index 0a4e22de99..90a1516717 100644 --- a/libavfilter/vulkan/debayer.comp.glsl +++ b/libavfilter/vulkan/debayer.comp.glsl @@ -36,6 +36,7 @@ layout(push_constant, std430) uniform pushConstants { int crop_y; int crop_w; int crop_h; + vec2 in_dims; }; #define LD(xo, yo) \ diff --git a/libavfilter/vulkan/scale.comp.glsl b/libavfilter/vulkan/scale.comp.glsl new file mode 100644 index 0000000000..b439344106 --- /dev/null +++ b/libavfilter/vulkan/scale.comp.glsl @@ -0,0 +1,120 @@ +/* + * + * Copyright (c) Lynne <[email protected]> + * + * 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; + +#define MODE_COPY 0 +#define MODE_NV12 1 +#define MODE_YUV420 2 +#define MODE_YUV444 3 + +layout (constant_id = 0) const int nb_planes = 0; +layout (constant_id = 1) const int mode = MODE_COPY; +layout (constant_id = 2) const int fullrange = 0; + +layout (set = 0, binding = 0) uniform sampler2D input_img[]; +layout (set = 0, binding = 1) uniform writeonly image2D output_img[]; + +layout (push_constant, std430) uniform pushConstants { + mat4 yuv_matrix; + int crop_x; + int crop_y; + int crop_w; + int crop_h; + vec2 in_dims; +}; + +vec4 scale_bilinear(int idx, ivec2 pos, vec2 crop_range, vec2 crop_off) +{ + vec2 npos = (vec2(pos) + 0.5f) / imageSize(output_img[idx]); + npos *= crop_range; /* Reduce the range */ + npos += crop_off; /* Offset the start */ + return texture(input_img[idx], npos); +} + +vec4 rgb2yuv(vec4 src) +{ + src *= yuv_matrix; + if (fullrange == 1) { + src += vec4(0.0, 0.5, 0.5, 0.0); + } else { + src *= vec4(219.0 / 255.0, 224.0 / 255.0, 224.0 / 255.0, 1.0); + src += vec4(16.0 / 255.0, 128.0 / 255.0, 128.0 / 255.0, 0.0); + } + return src; +} + +void write_nv12(vec4 src, ivec2 pos) +{ + imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); + pos /= ivec2(2); + imageStore(output_img[1], pos, vec4(src.g, src.b, 0.0, 0.0)); +} + +void write_420(vec4 src, ivec2 pos) +{ + imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); + pos /= ivec2(2); + imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); + imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); +} + +void write_444(vec4 src, ivec2 pos) +{ + imageStore(output_img[0], pos, vec4(src.r, 0.0, 0.0, 0.0)); + imageStore(output_img[1], pos, vec4(src.g, 0.0, 0.0, 0.0)); + imageStore(output_img[2], pos, vec4(src.b, 0.0, 0.0, 0.0)); +} + +void main() +{ + ivec2 pos = ivec2(gl_GlobalInvocationID.xy); + vec2 c_r = vec2(crop_w, crop_h) / in_dims; + vec2 c_o = vec2(crop_x, crop_y) / in_dims; + + if (mode == MODE_COPY) { + for (int i = 0; i < nb_planes; i++) { + ivec2 size = imageSize(output_img[i]); + if (any(greaterThanEqual(pos, size))) + continue; + + vec4 res = scale_bilinear(i, pos, c_r, c_o); + imageStore(output_img[i], pos, res); + } + } else { + ivec2 size = imageSize(output_img[0]); + if (any(greaterThanEqual(pos, size))) + return; + + vec4 res = rgb2yuv(scale_bilinear(0, pos, c_r, c_o)); + switch (mode) { + case MODE_NV12: write_nv12(res, pos); break; + case MODE_YUV420: write_420(res, pos); break; + case MODE_YUV444: write_444(res, pos); break; + } + } +} _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
