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]

Reply via email to