PR #22928 opened by Lynne
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22928
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22928.patch

Passes tests. Just curious about it.


>From 11bf4bf480ed524b696823cbb964d1083694f0fe Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 15:47:17 +0200
Subject: [PATCH 1/6] swscale/graph: support allocating hardware intermediate
 frames

Sponsored-by: Sovereign Tech Fund
---
 libswscale/graph.c      | 52 +++++++++++++++++++++++++++++++++++++++--
 libswscale/vulkan/ops.c |  7 ++++++
 libswscale/vulkan/ops.h |  6 +++++
 3 files changed, 63 insertions(+), 2 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index 2020fcf8c7..3a9bf68d70 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -21,6 +21,7 @@
 #include "libavutil/avassert.h"
 #include "libavutil/cpu.h"
 #include "libavutil/error.h"
+#include "libavutil/hwcontext.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/macros.h"
 #include "libavutil/mem.h"
@@ -37,6 +38,9 @@
 #include "swscale_internal.h"
 #include "graph.h"
 #include "ops.h"
+#if CONFIG_VULKAN
+#include "vulkan/ops.h"
+#endif
 
 int ff_sws_pass_aligned_width(const SwsPass *pass, int width)
 {
@@ -85,6 +89,31 @@ static int frame_alloc_planes(AVFrame *dst)
     return 0;
 }
 
+#if CONFIG_VULKAN
+static int pass_alloc_output_hw(SwsPass *pass, AVFrame *avframe,
+                                AVBufferRef *dev_ref)
+{
+    SwsPassBuffer *buffer = pass->output;
+    AVBufferRef *frames_ref = av_hwframe_ctx_alloc(dev_ref);
+    if (!frames_ref)
+        return AVERROR(ENOMEM);
+
+    AVHWFramesContext *hwfc = (AVHWFramesContext *)frames_ref->data;
+    hwfc->format    = AV_PIX_FMT_VULKAN;
+    hwfc->sw_format = pass->format;
+    hwfc->width     = buffer->width;
+    hwfc->height    = buffer->height;
+
+    int ret = av_hwframe_ctx_init(frames_ref);
+    if (ret >= 0) {
+        avframe->format = AV_PIX_FMT_VULKAN;
+        ret = av_hwframe_get_buffer(frames_ref, avframe, 0);
+    }
+    av_buffer_unref(&frames_ref);
+    return ret;
+}
+#endif
+
 static int pass_alloc_output(SwsPass *pass)
 {
     if (!pass || pass->output->avframe)
@@ -94,16 +123,35 @@ static int pass_alloc_output(SwsPass *pass)
     AVFrame *avframe = av_frame_alloc();
     if (!avframe)
         return AVERROR(ENOMEM);
-    avframe->format = pass->format;
     avframe->width  = buffer->width;
     avframe->height = buffer->height;
 
-    int ret = frame_alloc_planes(avframe);
+    int ret;
+
+#if CONFIG_VULKAN
+    const SwsGraph *graph = pass->graph;
+    if (graph->src.hw_format == AV_PIX_FMT_VULKAN &&
+        graph->dst.hw_format == AV_PIX_FMT_VULKAN) {
+        AVBufferRef *dev_ref = ff_sws_vk_device_ref(graph->ctx);
+        if (dev_ref) {
+            ret = pass_alloc_output_hw(pass, avframe, dev_ref);
+            if (ret >= 0)
+                goto done;
+            av_frame_unref(avframe);
+        }
+    }
+#endif
+
+    avframe->format = pass->format;
+    ret = frame_alloc_planes(avframe);
     if (ret < 0) {
         av_frame_free(&avframe);
         return ret;
     }
 
+#if CONFIG_VULKAN
+done:
+#endif
     buffer->avframe = avframe;
     ff_sws_frame_from_avframe(&buffer->frame, avframe);
     return 0;
diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index cf128b00c1..f06352883b 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -82,6 +82,13 @@ int ff_sws_vk_init(SwsContext *sws, AVBufferRef *dev_ref)
     return 0;
 }
 
+AVBufferRef *ff_sws_vk_device_ref(SwsContext *sws)
+{
+    SwsInternal *c = sws_internal(sws);
+    FFVulkanOpsCtx *s = c->hw_priv;
+    return s ? s->vkctx.device_ref : NULL;
+}
+
 #define MAX_DITHER_BUFS 4
 
 typedef struct VulkanPriv {
diff --git a/libswscale/vulkan/ops.h b/libswscale/vulkan/ops.h
index 0421dba446..6c9ae6c7fd 100644
--- a/libswscale/vulkan/ops.h
+++ b/libswscale/vulkan/ops.h
@@ -38,4 +38,10 @@ typedef struct FFVulkanOpsCtx {
 
 int ff_sws_vk_init(SwsContext *sws, AVBufferRef *dev_ref);
 
+/**
+ * Returns the Vulkan device reference associated with `sws`, or NULL if
+ * Vulkan has not been initialized for this context.
+ */
+AVBufferRef *ff_sws_vk_device_ref(SwsContext *sws);
+
 #endif /* SWSCALE_VULKAN_OPS_H */
-- 
2.52.0


>From 332ba56e4eed967530015c88324e59b054672393 Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 12:23:03 +0200
Subject: [PATCH 2/6] swscale/vulkan: base dispatch size on output image size,
 rather than input

Sponsored-by: Sovereign Tech Fund
---
 libswscale/vulkan/ops.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index f06352883b..5ef4667051 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -151,8 +151,8 @@ static void process(const SwsFrame *dst, const SwsFrame 
*src, int y, int h,
     ff_vk_exec_bind_shader(&p->s->vkctx, ec, &p->shd);
 
     vk->CmdDispatch(ec->buf,
-                    FFALIGN(src_f->width, p->shd.lg_size[0])/p->shd.lg_size[0],
-                    FFALIGN(src_f->height, 
p->shd.lg_size[1])/p->shd.lg_size[1],
+                    FFALIGN(dst_f->width, p->shd.lg_size[0])/p->shd.lg_size[0],
+                    FFALIGN(dst_f->height, 
p->shd.lg_size[1])/p->shd.lg_size[1],
                     1);
 
     ff_vk_exec_submit(&p->s->vkctx, ec);
@@ -985,7 +985,7 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
     GLSLC(0, void main()                                                      
);
     GLSLC(0, {                                                                
);
     GLSLC(1,     ivec2 pos = ivec2(gl_GlobalInvocationID.xy);                 
);
-    GLSLC(1,     ivec2 size = imageSize(src_img[0]);                          
);
+    GLSLC(1,     ivec2 size = imageSize(dst_img[0]);                          
);
     GLSLC(1,     if (any(greaterThanEqual(pos, size)))                        
);
     GLSLC(2,         return;                                                  
);
     GLSLC(0,                                                                  
);
-- 
2.52.0


>From 2a595cb254a06a816523940b252341f6fdcda2ea Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 12:21:09 +0200
Subject: [PATCH 3/6] swscale/vulkan: make dither buffer allocation path
 generic

Just a simple rename.

Sponsored-by: Sovereign Tech Fund
---
 libswscale/vulkan/ops.c | 99 ++++++++++++++++++++++++-----------------
 1 file changed, 57 insertions(+), 42 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 5ef4667051..2a0cd78530 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -90,13 +90,14 @@ AVBufferRef *ff_sws_vk_device_ref(SwsContext *sws)
 }
 
 #define MAX_DITHER_BUFS 4
+#define MAX_DATA_BUFS (MAX_DITHER_BUFS)
 
 typedef struct VulkanPriv {
     FFVulkanOpsCtx *s;
     FFVkExecPool e;
     FFVulkanShader shd;
-    FFVkBuffer dither_buf[MAX_DITHER_BUFS];
-    int nb_dither_buf;
+    FFVkBuffer data_bufs[MAX_DATA_BUFS];
+    int nb_data_bufs;
     enum FFVkShaderRepFormat src_rep;
     enum FFVkShaderRepFormat dst_rep;
 } VulkanPriv;
@@ -164,54 +165,68 @@ static void free_fn(void *priv)
     VulkanPriv *p = priv;
     ff_vk_exec_pool_free(&p->s->vkctx, &p->e);
     ff_vk_shader_free(&p->s->vkctx, &p->shd);
-    for (int i = 0; i < p->nb_dither_buf; i++)
-        ff_vk_free_buf(&p->s->vkctx, &p->dither_buf[i]);
+    for (int i = 0; i < p->nb_data_bufs; i++)
+        ff_vk_free_buf(&p->s->vkctx, &p->data_bufs[i]);
     av_refstruct_unref(&p->s);
     av_free(priv);
 }
 
-static int create_dither_bufs(FFVulkanOpsCtx *s, VulkanPriv *p, SwsOpList *ops)
+static int create_dither_buf(FFVulkanOpsCtx *s, VulkanPriv *p,
+                             const SwsDitherOp *dd, FFVkBuffer *buf)
 {
     int err;
-    p->nb_dither_buf = 0;
+
+    int size = (1 << dd->size_log2);
+    err = ff_vk_create_buf(&s->vkctx, buf,
+                           size*size*sizeof(float), NULL, NULL,
+                           VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                           VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+                           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+    if (err < 0)
+        return err;
+
+    float *dither_data;
+    err = ff_vk_map_buffer(&s->vkctx, buf, (uint8_t **)&dither_data, 0);
+    if (err < 0)
+        goto fail;
+
+    for (int i = 0; i < size; i++) {
+        for (int j = 0; j < size; j++) {
+            const AVRational r = dd->matrix[i*size + j];
+            dither_data[i*size + j] = r.num/(float)r.den;
+        }
+    }
+
+    ff_vk_unmap_buffer(&s->vkctx, buf, 1);
+
+    return 0;
+
+fail:
+    ff_vk_free_buf(&p->s->vkctx, buf);
+    return err;
+}
+
+static int create_bufs(FFVulkanOpsCtx *s, VulkanPriv *p, SwsOpList *ops)
+{
+    int err;
+    p->nb_data_bufs = 0;
     for (int n = 0; n < ops->num_ops; n++) {
         const SwsOp *op = &ops->ops[n];
-        if (op->op != SWS_OP_DITHER)
-            continue;
-        av_assert0(p->nb_dither_buf + 1 <= MAX_DITHER_BUFS);
-
-        int size = (1 << op->dither.size_log2);
-        int idx = p->nb_dither_buf;
-        err = ff_vk_create_buf(&s->vkctx, &p->dither_buf[idx],
-                               size*size*sizeof(float), NULL, NULL,
-                               VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
-                               VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
-                               VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
-        if (err < 0)
-            goto fail;
-        p->nb_dither_buf++;
-
-        float *dither_data;
-        err = ff_vk_map_buffer(&s->vkctx, &p->dither_buf[idx],
-                               (uint8_t **)&dither_data, 0);
-        if (err < 0)
-            goto fail;
-
-        for (int i = 0; i < size; i++) {
-            for (int j = 0; j < size; j++) {
-                const AVRational r = op->dither.matrix[i*size + j];
-                dither_data[i*size + j] = r.num/(float)r.den;
-            }
+        if (op->op == SWS_OP_DITHER) {
+            av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
+            err = create_dither_buf(s, p, &op->dither,
+                                    &p->data_bufs[p->nb_data_bufs]);
+            if (err < 0)
+                goto fail;
+            p->nb_data_bufs++;
         }
-
-        ff_vk_unmap_buffer(&s->vkctx, &p->dither_buf[idx], 1);
     }
 
     return 0;
 
 fail:
-    for (int i = 0; i < p->nb_dither_buf; i++)
-        ff_vk_free_buf(&p->s->vkctx, &p->dither_buf[i]);
+    for (int i = 0; i < p->nb_data_bufs; i++)
+        ff_vk_free_buf(&p->s->vkctx, &p->data_bufs[i]);
     return err;
 }
 
@@ -227,7 +242,7 @@ struct DitherData {
 };
 
 typedef struct SPIRVIDs {
-    int in_vars[3 + MAX_DITHER_BUFS];
+    int in_vars[3 + MAX_DATA_BUFS];
 
     int glfn;
     int ep;
@@ -598,7 +613,7 @@ static int add_ops_spirv(VulkanPriv *p, FFVulkanOpsCtx *s,
     if (op_r)
         p->src_rep = op_r->type == SWS_PIXEL_F32 ? FF_VK_REP_FLOAT : 
FF_VK_REP_UINT;
 
-    FFVulkanDescriptorSetBinding desc_set[MAX_DITHER_BUFS] = {
+    FFVulkanDescriptorSetBinding desc_set[MAX_DATA_BUFS] = {
         {
             .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
             .stages = VK_SHADER_STAGE_COMPUTE_BIT,
@@ -613,7 +628,7 @@ static int add_ops_spirv(VulkanPriv *p, FFVulkanOpsCtx *s,
     ff_vk_shader_add_descriptor_set(&s->vkctx, shd, desc_set, 2, 0, 0);
 
     /* Create dither buffers */
-    int err = create_dither_bufs(s, p, ops);
+    int err = create_bufs(s, p, ops);
     if (err < 0)
         return err;
 
@@ -953,7 +968,7 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
     add_desc_read_write(&buf_desc[nb_desc++], &p->dst_rep, write);
     ff_vk_shader_add_descriptor_set(&s->vkctx, shd, buf_desc, nb_desc, 0, 0);
 
-    err = create_dither_bufs(s, p, ops);
+    err = create_bufs(s, p, ops);
     if (err < 0)
         return err;
 
@@ -1191,9 +1206,9 @@ static int compile(SwsContext *sws, SwsOpList *ops, 
SwsCompiledOp *out, int glsl
     if (err < 0)
         goto fail;
 
-    for (int i = 0; i < p->nb_dither_buf; i++)
+    for (int i = 0; i < p->nb_data_bufs; i++)
         ff_vk_shader_update_desc_buffer(&s->vkctx, &p->e.contexts[0], &p->shd,
-                                        1, i, 0, &p->dither_buf[i],
+                                        1, i, 0, &p->data_bufs[i],
                                         0, VK_WHOLE_SIZE, VK_FORMAT_UNDEFINED);
 
     *out = (SwsCompiledOp) {
-- 
2.52.0


>From 7abac315b0a8df466c785cb9ec7216d3e59cff0e Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 12:26:04 +0200
Subject: [PATCH 4/6] swscale/vulkan: make buffer descriptor generation generic

Again, simple rename.

Sponsored-by: Sovereign Tech Fund
---
 libswscale/vulkan/ops.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 2a0cd78530..4c2301a192 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -973,25 +973,25 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
         return err;
 
     nb_desc = 0;
-    char dither_buf_name[MAX_DITHER_BUFS][64];
-    char dither_mat_name[MAX_DITHER_BUFS][64];
+    char data_buf_name[MAX_DATA_BUFS][256];
+    char data_str_name[MAX_DATA_BUFS][256];
     for (int n = 0; n < ops->num_ops; n++) {
         const SwsOp *op = &ops->ops[n];
-        if (op->op != SWS_OP_DITHER)
-            continue;
-        int size = (1 << op->dither.size_log2);
-        av_assert0(size < 8192);
-        snprintf(dither_buf_name[nb_desc], 64, "dither_buf%i", n);
-        snprintf(dither_mat_name[nb_desc], 64, "float dither_mat%i[%i][%i];",
-                 n, size, size);
-        buf_desc[nb_desc] = (FFVulkanDescriptorSetBinding) {
-            .name        = dither_buf_name[nb_desc],
-            .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
-            .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
-            .mem_layout  = "scalar",
-            .buf_content = dither_mat_name[nb_desc],
-        };
-        nb_desc++;
+        if (op->op == SWS_OP_DITHER) {
+            int size = (1 << op->dither.size_log2);
+            av_assert0(size < 8192);
+            snprintf(data_buf_name[nb_desc], 256, "dither_buf%i", n);
+            snprintf(data_str_name[nb_desc], 256, "float 
dither_mat%i[%i][%i];",
+                     n, size, size);
+            buf_desc[nb_desc] = (FFVulkanDescriptorSetBinding) {
+                .name        = data_buf_name[nb_desc],
+                .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+                .mem_layout  = "scalar",
+                .buf_content = data_str_name[nb_desc],
+            };
+            nb_desc++;
+        }
     }
     if (nb_desc)
         ff_vk_shader_add_descriptor_set(&s->vkctx, shd, buf_desc,
-- 
2.52.0


>From e205c9f7e9653bf3ae6c065ad6d7e62f59164d1a Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 12:22:04 +0200
Subject: [PATCH 5/6] swscale/vulkan: allocate buffers for scaling filters

Simply allocates buffers to hold filter data.

Sponsored-by: Sovereign Tech Fund
---
 libswscale/vulkan/ops.c | 73 ++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 72 insertions(+), 1 deletion(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index 4c2301a192..ee16457752 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -90,7 +90,8 @@ AVBufferRef *ff_sws_vk_device_ref(SwsContext *sws)
 }
 
 #define MAX_DITHER_BUFS 4
-#define MAX_DATA_BUFS (MAX_DITHER_BUFS)
+#define MAX_FILT_BUFS 4
+#define MAX_DATA_BUFS (MAX_DITHER_BUFS + MAX_FILT_BUFS*4)
 
 typedef struct VulkanPriv {
     FFVulkanOpsCtx *s;
@@ -171,6 +172,41 @@ static void free_fn(void *priv)
     av_free(priv);
 }
 
+static int create_filter_buf(FFVulkanOpsCtx *s, VulkanPriv *p,
+                             const SwsFilterWeights *wd, FFVkBuffer *buf)
+{
+    int err;
+
+    /* Weights */
+    err = ff_vk_create_buf(&s->vkctx, buf,
+                           wd->num_weights*sizeof(float) +
+                           wd->dst_size*sizeof(int32_t), NULL, NULL,
+                           VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
+                           VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+                           VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
+    if (err < 0)
+        goto fail;
+
+    float *weights_data;
+    err = ff_vk_map_buffer(&s->vkctx, buf,
+                           (uint8_t **)&weights_data, 0);
+    if (err < 0)
+        goto fail;
+    for (int i = 0; i < wd->num_weights; i++)
+        weights_data[i] = (float) wd->weights[i] / SWS_FILTER_SCALE;
+
+    memcpy(weights_data + wd->num_weights,
+           wd->offsets, wd->dst_size*sizeof(int32_t));
+
+    ff_vk_unmap_buffer(&s->vkctx, buf, 1);
+
+    return 0;
+
+fail:
+    ff_vk_free_buf(&p->s->vkctx, buf);
+    return 0;
+}
+
 static int create_dither_buf(FFVulkanOpsCtx *s, VulkanPriv *p,
                              const SwsDitherOp *dd, FFVkBuffer *buf)
 {
@@ -219,6 +255,21 @@ static int create_bufs(FFVulkanOpsCtx *s, VulkanPriv *p, 
SwsOpList *ops)
             if (err < 0)
                 goto fail;
             p->nb_data_bufs++;
+        } else if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V) {
+            av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
+            err = create_filter_buf(s, p, op->filter.kernel,
+                                    &p->data_bufs[p->nb_data_bufs]);
+            if (err < 0)
+                goto fail;
+            p->nb_data_bufs++;
+        } else if ((op->op == SWS_OP_READ ||
+                    op->op == SWS_OP_WRITE) && op->rw.filter) {
+            av_assert0(p->nb_data_bufs + 1 <= FF_ARRAY_ELEMS(p->data_bufs));
+            err = create_filter_buf(s, p, op->rw.kernel,
+                                    &p->data_bufs[p->nb_data_bufs]);
+            if (err < 0)
+                goto fail;
+            p->nb_data_bufs++;
         }
     }
 
@@ -991,6 +1042,26 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
                 .buf_content = data_str_name[nb_desc],
             };
             nb_desc++;
+        } else if (op->op == SWS_OP_FILTER_H || op->op == SWS_OP_FILTER_V ||
+                   ((op->op == SWS_OP_READ || op->op == SWS_OP_WRITE) &&
+                    op->rw.filter)) {
+            const SwsFilterWeights *wd = (op->op == SWS_OP_READ ||
+                                          op->op == SWS_OP_WRITE) ?
+                                         op->rw.kernel : op->filter.kernel;
+            snprintf(data_buf_name[nb_desc], 256, "filter_buf%i", n);
+            snprintf(data_str_name[nb_desc], 256,
+                     "float filter_w%i[%i][%i];\n"
+                 "    int filter_o%i[%i];",
+                     n, wd->dst_size, wd->filter_size,
+                     n, wd->dst_size);
+            buf_desc[nb_desc] = (FFVulkanDescriptorSetBinding) {
+                .name        = data_buf_name[nb_desc],
+                .type        = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
+                .stages      = VK_SHADER_STAGE_COMPUTE_BIT,
+                .mem_layout  = "scalar",
+                .buf_content = data_str_name[nb_desc],
+            };
+            nb_desc++;
         }
     }
     if (nb_desc)
-- 
2.52.0


>From 1137c848872123ea24e41258ec676b9c908cdcab Mon Sep 17 00:00:00 2001
From: Lynne <[email protected]>
Date: Sun, 26 Apr 2026 12:54:05 +0200
Subject: [PATCH 6/6] swscale/vulkan: add support for filtering on SWS_OP_READ

Sponsored-by: Sovereign Tech Fund
---
 libswscale/vulkan/ops.c | 49 +++++++++++++++++++++++++++++++++--------
 1 file changed, 40 insertions(+), 9 deletions(-)

diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c
index ee16457752..3fd7b0b2b8 100644
--- a/libswscale/vulkan/ops.c
+++ b/libswscale/vulkan/ops.c
@@ -991,6 +991,44 @@ static void 
add_desc_read_write(FFVulkanDescriptorSetBinding *out_desc,
 #define QSTR "(%i/%i%s)"
 #define QTYPE(Q) (Q).num, (Q).den, cur_type == SWS_PIXEL_F32 ? ".0f" : ""
 
+static void read_glsl(SwsOpList *ops, const SwsOp *op, FFVulkanShader *shd,
+                      int idx, const char *type_name,
+                      const char *type_v, const char *type_s)
+{
+    const SwsFilterWeights *wd = op->rw.kernel;
+    if (op->rw.filter) {
+        const char *axis    = op->rw.filter == SWS_OP_FILTER_H ? "pos.x" : 
"pos.y";
+        const char *coord_x = op->rw.filter == SWS_OP_FILTER_H ? "o + i" : 
"pos.x";
+        const char *coord_y = op->rw.filter == SWS_OP_FILTER_H ? "pos.y" : "o 
+ i";
+        GLSLC(1, tmp = vec4(0);                                               
);
+        av_bprintf(&shd->src, "    int o = filter_o%i[%s];\n", idx, axis);
+        av_bprintf(&shd->src, "    for (int i = 0; i < %i; i++) {\n",
+                   wd->filter_size);
+        av_bprintf(&shd->src, "        float w = filter_w%i[%s][i];\n",
+                   idx, axis);
+        if (op->rw.packed) {
+            GLSLF(2, tmp += w * %s(imageLoad(src_img[%i], ivec2(%s, %s)));     
,
+                  type_v, ops->plane_src[0], coord_x, coord_y);
+        } else {
+            for (int i = 0; i < op->rw.elems; i++)
+                GLSLF(2,
+                      tmp.%c += w * %s(imageLoad(src_img[%i], ivec2(%s, 
%s))[0]); ,
+                      "xyzw"[i], type_s, ops->plane_src[i], coord_x, coord_y);
+        }
+        GLSLC(1, }                                                            
);
+        GLSLC(1, f32 = tmp;                                                   
);
+    } else {
+        if (op->rw.packed) {
+            GLSLF(1, %s = %s(imageLoad(src_img[%i], pos));                     
,
+                  type_name, type_v, ops->plane_src[0]);
+        } else {
+            for (int i = 0; i < op->rw.elems; i++)
+                GLSLF(1, %s.%c = %s(imageLoad(src_img[%i], pos)[0]);           
,
+                      type_name, "xyzw"[i], type_s, ops->plane_src[i]);
+        }
+    }
+}
+
 static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
                         SwsOpList *ops, FFVulkanShader *shd)
 {
@@ -1097,16 +1135,9 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s,
 
         switch (op->op) {
         case SWS_OP_READ: {
-            if (op->rw.frac || op->rw.filter) {
+            if (op->rw.frac)
                 return AVERROR(ENOTSUP);
-            } else if (op->rw.packed) {
-                GLSLF(1, %s = %s(imageLoad(src_img[%i], pos));                 
,
-                      type_name, type_v, ops->plane_src[0]);
-            } else {
-                for (int i = 0; i < op->rw.elems; i++)
-                    GLSLF(1, %s.%c = %s(imageLoad(src_img[%i], pos)[0]);       
,
-                          type_name, "xyzw"[i], type_s, ops->plane_src[i]);
-            }
+            read_glsl(ops, op, shd, n, type_name, type_v, type_s);
             break;
         }
         case SWS_OP_WRITE: {
-- 
2.52.0

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

Reply via email to