PR #22463 opened by Niklas Haas (haasn)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22463
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22463.patch

These commits were already reviewed by @ramiro and found to be OK, so I am 
re-submitting them as a separate PR to reduce the size of the main filtering PR.


>From d441f1a519ed8dba559f71b518c57c2a6b63fd3b Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 4 Mar 2026 16:25:07 +0100
Subject: [PATCH 01/12] swscale/ops_backend: use in/out_bump[] in process()

Instead of recomputing the input/output address on each iteration, we
can use the in_bump/out_bump arrays the way the x86 backend does.

I initially avoided this in order to ensure the reference backend always does
the correct thing, even if some future bug causes the bump values to be
computed incorrectly, but doing it this way makes an upcoming change easier.

(And besides, it would be easier to just add an av_assert2() to catch those
cases)

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_tmpl_common.c | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/libswscale/ops_tmpl_common.c b/libswscale/ops_tmpl_common.c
index c0e0d9f3fb..1f3eb6aa67 100644
--- a/libswscale/ops_tmpl_common.c
+++ b/libswscale/ops_tmpl_common.c
@@ -186,16 +186,20 @@ static void fn(process)(const SwsOpExec *exec, const void 
*priv,
 
     SwsOpIter iterdata;
     SwsOpIter *iter = &iterdata; /* for CONTINUE() macro to work */
+    for (int i = 0; i < 4; i++) {
+        iter->in[i]  = exec->in[i];
+        iter->out[i] = exec->out[i];
+    }
 
     for (iter->y = y_start; iter->y < y_end; iter->y++) {
-        for (int i = 0; i < 4; i++) {
-            iter->in[i]  = exec->in[i]  + (iter->y - y_start) * 
exec->in_stride[i];
-            iter->out[i] = exec->out[i] + (iter->y - y_start) * 
exec->out_stride[i];
-        }
-
         for (int block = bx_start; block < bx_end; block++) {
             iter->x = block * SWS_BLOCK_SIZE;
             CONTINUE(block_t, (void *) x, (void *) y, (void *) z, (void *) w);
         }
+
+        for (int i = 0; i < 4; i++) {
+            iter->in[i]  += exec->in_bump[i];
+            iter->out[i] += exec->out_bump[i];
+        }
     }
 }
-- 
2.52.0


>From 5a4179595da31222f045d4c76428f47be16ac98d Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 4 Mar 2026 23:11:12 +0100
Subject: [PATCH 02/12] swscale/ops_chain: allow free callback to take
 SwsOpPriv

I mainly want to be able to store two pointers side-by-side.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_chain.c      |  6 +++---
 libswscale/ops_chain.h      | 11 ++++++++---
 libswscale/ops_tmpl_float.c |  4 ++--
 libswscale/x86/ops.c        |  4 ++--
 4 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c
index 2445154186..1a4ac539ba 100644
--- a/libswscale/ops_chain.c
+++ b/libswscale/ops_chain.c
@@ -39,14 +39,14 @@ void ff_sws_op_chain_free_cb(void *ptr)
     SwsOpChain *chain = ptr;
     for (int i = 0; i < chain->num_impl + 1; i++) {
         if (chain->free[i])
-            chain->free[i](chain->impl[i].priv.ptr);
+            chain->free[i](chain->impl[i].priv);
     }
 
     av_free(chain);
 }
 
 int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
-                           void (*free)(void *), const SwsOpPriv *priv)
+                           void (*free)(SwsOpPriv), const SwsOpPriv *priv)
 {
     const int idx = chain->num_impl;
     if (idx == SWS_MAX_OPS)
@@ -235,7 +235,7 @@ int ff_sws_op_compile_tables(const SwsOpTable *const 
tables[], int num_tables,
     ret = ff_sws_op_chain_append(chain, best->func, best->free, &priv);
     if (ret < 0) {
         if (best->free)
-            best->free(priv.ptr);
+            best->free(priv);
         return ret;
     }
 
diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h
index 532622fd2c..e693145795 100644
--- a/libswscale/ops_chain.h
+++ b/libswscale/ops_chain.h
@@ -82,7 +82,7 @@ static_assert(offsetof(SwsOpImpl, priv) == 16, "SwsOpImpl 
layout mismatch");
 typedef struct SwsOpChain {
 #define SWS_MAX_OPS 16
     SwsOpImpl impl[SWS_MAX_OPS + 1]; /* reserve extra space for the entrypoint 
*/
-    void (*free[SWS_MAX_OPS + 1])(void *);
+    void (*free[SWS_MAX_OPS + 1])(SwsOpPriv);
     int num_impl;
     int cpu_flags; /* set of all used CPU flags */
 } SwsOpChain;
@@ -96,7 +96,7 @@ static inline void ff_sws_op_chain_free(SwsOpChain *chain)
 
 /* Returns 0 on success, or a negative error code. */
 int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
-                           void (*free)(void *), const SwsOpPriv *priv);
+                           void (*free)(SwsOpPriv), const SwsOpPriv *priv);
 
 typedef struct SwsOpEntry {
     /* Kernel metadata; reduced size subset of SwsOp */
@@ -119,9 +119,14 @@ typedef struct SwsOpEntry {
     /* Kernel implementation */
     SwsFuncPtr func;
     int (*setup)(const SwsOp *op, SwsOpPriv *out); /* optional */
-    void (*free)(void *priv);
+    void (*free)(SwsOpPriv priv);
 } SwsOpEntry;
 
+static inline void ff_op_priv_free(SwsOpPriv priv)
+{
+    av_free(priv.ptr);
+}
+
 typedef struct SwsOpTable {
     unsigned cpu_flags;   /* required CPU flags for this table */
     int block_size;       /* fixed block size of this table */
diff --git a/libswscale/ops_tmpl_float.c b/libswscale/ops_tmpl_float.c
index 2f1d249168..d5ac7fb75f 100644
--- a/libswscale/ops_tmpl_float.c
+++ b/libswscale/ops_tmpl_float.c
@@ -107,7 +107,7 @@ DECL_ENTRY(dither##N,
     .op = SWS_OP_DITHER,                                                       
 \
     .dither_size = N,                                                          
 \
     .setup = fn(setup_dither),                                                 
 \
-    .free = av_free,                                                           
 \
+    .free = ff_op_priv_free,                                                   
 \
 );
 
 WRAP_DITHER(0)
@@ -193,7 +193,7 @@ DECL_IMPL(linear_##NAME)
 DECL_ENTRY(linear_##NAME,                                                      
 \
     .op    = SWS_OP_LINEAR,                                                    
 \
     .setup = fn(setup_linear),                                                 
 \
-    .free  = av_free,                                                          
 \
+    .free  = ff_op_priv_free,                                                  
 \
     .linear_mask = (MASK),                                                     
 \
 );
 
diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c
index 8a7ad3ecca..76d374430c 100644
--- a/libswscale/x86/ops.c
+++ b/libswscale/x86/ops.c
@@ -237,7 +237,7 @@ static int setup_dither(const SwsOp *op, SwsOpPriv *out)
     DECL_MACRO(F32, dither##SIZE##EXT,                                         
 \
         .op    = SWS_OP_DITHER,                                                
 \
         .setup = setup_dither,                                                 
 \
-        .free  = (SIZE) ? av_free : NULL,                                      
 \
+        .free  = (SIZE) ? ff_op_priv_free : NULL,                              
 \
         .dither_size = SIZE,                                                   
 \
     );
 
@@ -259,7 +259,7 @@ static int setup_linear(const SwsOp *op, SwsOpPriv *out)
     DECL_ASM(F32, NAME##EXT,                                                   
 \
         .op    = SWS_OP_LINEAR,                                                
 \
         .setup = setup_linear,                                                 
 \
-        .free  = av_free,                                                      
 \
+        .free  = ff_op_priv_free,                                              
 \
         .linear_mask = (MASK),                                                 
 \
     );
 
-- 
2.52.0


>From d39a6694aa47c83fb7adecce52d421135094279d Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 4 Mar 2026 23:18:26 +0100
Subject: [PATCH 03/12] swscale/ops_chain: add more integer types to SwsOpPriv

In particular, I need i32, but the others are also reasonable additions.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_chain.h | 17 +++++++++++------
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h
index e693145795..d5d0aba066 100644
--- a/libswscale/ops_chain.h
+++ b/libswscale/ops_chain.h
@@ -44,12 +44,17 @@ typedef union SwsOpPriv {
 
     /* Common types */
     void *ptr;
-    int8_t    i8[16];
-    uint8_t   u8[16];
-    uint16_t u16[8];
-    int16_t  i16[8];
-    uint32_t u32[4];
-    float    f32[4];
+    uint8_t    u8[16];
+    int8_t     i8[16];
+    uint16_t   u16[8];
+    int16_t    i16[8];
+    uint32_t   u32[4];
+    int32_t    i32[4];
+    float      f32[4];
+    uint64_t   u64[2];
+    int64_t    i64[2];
+    uintptr_t uptr[2];
+    intptr_t  iptr[2];
 } SwsOpPriv;
 
 static_assert(sizeof(SwsOpPriv) == 16, "SwsOpPriv size mismatch");
-- 
2.52.0


>From 394521f0362830e1f6ae4c490ca18bad87ec9842 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Fri, 6 Mar 2026 19:25:00 +0100
Subject: [PATCH 04/12] swscale/graph: remove redundant check

Such formats are already rejected by ff_sws_decode/encode_pixfmt().

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/graph.c | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index c8caf9488e..b3a545f02b 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -573,9 +573,7 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
         goto fail;
 
     /* The new format conversion layer cannot scale for now */
-    if (src->width != dst->width || src->height != dst->height ||
-        src->desc->log2_chroma_h || src->desc->log2_chroma_w ||
-        dst->desc->log2_chroma_h || dst->desc->log2_chroma_w)
+    if (src->width != dst->width || src->height != dst->height)
         goto fail;
 
     /* The new code does not yet support alpha blending */
-- 
2.52.0


>From 4bf56c041d6ca257fbb1f2e4c2a057779101ae76 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Wed, 4 Mar 2026 14:40:14 +0100
Subject: [PATCH 05/12] swscale/graph: allow setup() to return an error code

Useful for a handful of reasons, including Vulkan (which depends on external
device resources), but also a change I want to make to the tail handling.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/graph.c        | 20 ++++++++++++++------
 libswscale/graph.h        |  7 ++++---
 libswscale/ops_dispatch.c |  6 ++++--
 libswscale/swscale.c      |  7 +++++--
 4 files changed, 27 insertions(+), 13 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index b3a545f02b..0402485f68 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -257,8 +257,8 @@ static void free_legacy_swscale(void *priv)
     sws_free_context(&sws);
 }
 
-static void setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
-                                 const SwsPass *pass)
+static int setup_legacy_swscale(const SwsFrame *out, const SwsFrame *in,
+                                const SwsPass *pass)
 {
     SwsContext *sws = pass->priv;
     SwsInternal *c = sws_internal(sws);
@@ -269,6 +269,8 @@ static void setup_legacy_swscale(const SwsFrame *out, const 
SwsFrame *in,
 
     if (usePal(sws->src_format))
         ff_update_palette(c, (const uint32_t *) in->data[1]);
+
+    return 0;
 }
 
 static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
@@ -634,12 +636,13 @@ static void free_lut3d(void *priv)
     ff_sws_lut3d_free(&lut);
 }
 
-static void setup_lut3d(const SwsFrame *out, const SwsFrame *in, const SwsPass 
*pass)
+static int setup_lut3d(const SwsFrame *out, const SwsFrame *in, const SwsPass 
*pass)
 {
     SwsLut3D *lut = pass->priv;
 
     /* Update dynamic frame metadata from the original source frame */
     ff_sws_lut3d_update(lut, &pass->graph->src.color);
+    return 0;
 }
 
 static void run_lut3d(const SwsFrame *out, const SwsFrame *in, int y, int h,
@@ -882,7 +885,7 @@ static void get_field(SwsGraph *graph, const AVFrame 
*avframe, SwsFrame *frame)
     frame->height = (frame->height + (graph->field == FIELD_TOP)) >> 1;
 }
 
-void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
+int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
 {
     av_assert0(dst->format == graph->dst.hw_format || dst->format == 
graph->dst.format);
     av_assert0(src->format == graph->src.hw_format || src->format == 
graph->src.format);
@@ -896,8 +899,11 @@ void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, 
const AVFrame *src)
         graph->exec.pass   = pass;
         graph->exec.input  = pass->input ? &pass->input->output->frame : 
&src_field;
         graph->exec.output = pass->output->avframe ? &pass->output->frame : 
&dst_field;
-        if (pass->setup)
-            pass->setup(graph->exec.output, graph->exec.input, pass);
+        if (pass->setup) {
+            int ret = pass->setup(graph->exec.output, graph->exec.input, pass);
+            if (ret < 0)
+                return ret;
+        }
 
         if (pass->num_slices == 1) {
             pass->run(graph->exec.output, graph->exec.input, 0, pass->height, 
pass);
@@ -905,4 +911,6 @@ void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, 
const AVFrame *src)
             avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 
0);
         }
     }
+
+    return 0;
 }
diff --git a/libswscale/graph.h b/libswscale/graph.h
index 5ccebcb808..4267851baa 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -48,8 +48,8 @@ typedef void (*SwsPassFunc)(const SwsFrame *out, const 
SwsFrame *in,
 /**
  * Function to run from the main thread before processing any lines.
  */
-typedef void (*SwsPassSetup)(const SwsFrame *out, const SwsFrame *in,
-                             const SwsPass *pass);
+typedef int (*SwsPassSetup)(const SwsFrame *out, const SwsFrame *in,
+                            const SwsPass *pass);
 
 /**
  * Represents an allocated output buffer for a filter pass.
@@ -93,6 +93,7 @@ struct SwsPass {
 
     /**
      * Called once from the main thread before running the filter. Optional.
+     * Returns 0 or a negative error code.
      */
     SwsPassSetup setup;
 
@@ -195,6 +196,6 @@ int ff_sws_graph_reinit(SwsContext *ctx, const SwsFormat 
*dst, const SwsFormat *
  * Dispatch the filter graph on a single field of the given frames. Internally
  * threaded.
  */
-void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src);
+int ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src);
 
 #endif /* SWSCALE_GRAPH_H */
diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index 7046e3956e..655b26cac9 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -117,8 +117,8 @@ static inline void get_row_data(const SwsOpPass *p, const 
int y,
         out[i] = base->out[i] + (y >> base->out_sub_y[i]) * 
base->out_stride[i];
 }
 
-static void op_pass_setup(const SwsFrame *out, const SwsFrame *in,
-                          const SwsPass *pass)
+static int op_pass_setup(const SwsFrame *out, const SwsFrame *in,
+                         const SwsPass *pass)
 {
     const AVPixFmtDescriptor *indesc  = av_pix_fmt_desc_get(in->format);
     const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format);
@@ -180,6 +180,8 @@ static void op_pass_setup(const SwsFrame *out, const 
SwsFrame *in,
         exec->in_bump[i]  = exec->in_stride[i]  - blocks_main * 
exec->block_size_in;
         exec->out_bump[i] = exec->out_stride[i] - blocks_main * 
exec->block_size_out;
     }
+
+    return 0;
 }
 
 /* Dispatch kernel over the last column of the image using memcpy */
diff --git a/libswscale/swscale.c b/libswscale/swscale.c
index 0968b0808f..b2090209d4 100644
--- a/libswscale/swscale.c
+++ b/libswscale/swscale.c
@@ -1395,8 +1395,11 @@ int sws_scale_frame(SwsContext *sws, AVFrame *dst, const 
AVFrame *src)
         return ret;
 
 process_frame:
-    for (int field = 0; field < (bot ? 2 : 1); field++)
-        ff_sws_graph_run(c->graph[field], dst, src);
+    for (int field = 0; field < (bot ? 2 : 1); field++) {
+        ret = ff_sws_graph_run(c->graph[field], dst, src);
+        if (ret < 0)
+            return ret;
+    }
 
     return 0;
 }
-- 
2.52.0


>From d0271e177e4b6919ab84f800b4b86fca2ac71a6b Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Mon, 9 Mar 2026 17:22:36 +0100
Subject: [PATCH 06/12] swscale/ops_optimizer: always clear unused dither
 components

Makes the op list a bit more stable.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_optimizer.c  | 4 ++--
 tests/ref/fate/sws-ops-list | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c
index abba9df2ff..520069f859 100644
--- a/libswscale/ops_optimizer.c
+++ b/libswscale/ops_optimizer.c
@@ -518,9 +518,9 @@ retry:
 
         case SWS_OP_DITHER:
             for (int i = 0; i < 4; i++) {
-                if (next->comps.unused[i] || op->dither.y_offset[i] < 0)
+                if (op->dither.y_offset[i] < 0)
                     continue;
-                if (prev->comps.flags[i] & SWS_COMP_EXACT) {
+                if (next->comps.unused[i] || (prev->comps.flags[i] & 
SWS_COMP_EXACT)) {
                     op->dither.y_offset[i] = -1; /* unnecessary dither */
                     goto retry;
                 } else {
diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list
index 13049a0c14..c83105406f 100644
--- a/tests/ref/fate/sws-ops-list
+++ b/tests/ref/fate/sws-ops-list
@@ -1 +1 @@
-a312bd79cadff3e2e02fd14ae7e54e26
+121d60ee0261ca5d9650a8d1e9e8a060
-- 
2.52.0


>From 454aa6629d7a410cc9f0f7b65e7364d03efb3d51 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Fri, 6 Mar 2026 20:27:37 +0100
Subject: [PATCH 07/12] swscale/ops_dispatch: add helper function to clean up
 SwsCompiledOp

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_dispatch.c | 12 +++++++++---
 libswscale/ops_dispatch.h |  2 ++
 tests/checkasm/sw_ops.c   |  7 +++----
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index 655b26cac9..1c3d3239f6 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -95,15 +95,21 @@ int ff_sws_ops_compile(SwsContext *ctx, const SwsOpList 
*ops, SwsCompiledOp *out
     return AVERROR(ENOTSUP);
 }
 
+void ff_sws_compiled_op_unref(SwsCompiledOp *comp)
+{
+    if (comp->free)
+        comp->free(comp->priv);
+
+    *comp = (SwsCompiledOp) {0};
+}
+
 static void op_pass_free(void *ptr)
 {
     SwsOpPass *p = ptr;
     if (!p)
         return;
 
-    if (p->comp.free)
-        p->comp.free(p->comp.priv);
-
+    ff_sws_compiled_op_unref(&p->comp);
     av_free(p);
 }
 
diff --git a/libswscale/ops_dispatch.h b/libswscale/ops_dispatch.h
index ebbd1f68e7..ca99457e28 100644
--- a/libswscale/ops_dispatch.h
+++ b/libswscale/ops_dispatch.h
@@ -100,4 +100,6 @@ typedef struct SwsCompiledOp {
     void (*free)(void *priv);
 } SwsCompiledOp;
 
+void ff_sws_compiled_op_unref(SwsCompiledOp *comp);
+
 #endif /* SWSCALE_OPS_DISPATCH_H */
diff --git a/tests/checkasm/sw_ops.c b/tests/checkasm/sw_ops.c
index 6c509567eb..b20a7559f1 100644
--- a/tests/checkasm/sw_ops.c
+++ b/tests/checkasm/sw_ops.c
@@ -241,10 +241,9 @@ static void check_ops(const char *report, const unsigned 
ranges[NB_PLANES],
         bench(comp_new.func, &exec, comp_new.priv, 0, 0, PIXELS / 
comp_new.block_size, LINES);
     }
 
-    if (comp_new.func != comp_ref.func && comp_new.free)
-        comp_new.free(comp_new.priv);
-    if (comp_ref.free)
-        comp_ref.free(comp_ref.priv);
+    if (comp_new.func != comp_ref.func)
+        ff_sws_compiled_op_unref(&comp_new);
+    ff_sws_compiled_op_unref(&comp_ref);
     sws_free_context(&ctx);
 }
 
-- 
2.52.0


>From 275694c98a0886489b3156c58ba4db04e6653440 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Mon, 9 Mar 2026 12:53:07 +0100
Subject: [PATCH 08/12] swscale/ops_dispatch: have ff_sws_compile_pass() take
 ownership of `ops`

More useful than just allowing it to "modify" the ops; in practice this means
the contents will be undefined anyways - might as well have this function
take care of freeing it afterwards as well.

Will make things simpler with regards to subpass splitting.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/graph.c        |  2 +-
 libswscale/ops.h          |  4 +++-
 libswscale/ops_dispatch.c | 19 +++++++++++++------
 3 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index 0402485f68..dda23ddca2 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -608,7 +608,7 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
     av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
     ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
 
-    ret = ff_sws_compile_pass(graph, ops, SWS_OP_FLAG_OPTIMIZE, dst, input, 
output);
+    ret = ff_sws_compile_pass(graph, &ops, SWS_OP_FLAG_OPTIMIZE, dst, input, 
output);
     if (ret < 0)
         goto fail;
 
diff --git a/libswscale/ops.h b/libswscale/ops.h
index 37e8f06331..20c9c25605 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -312,9 +312,11 @@ enum SwsOpCompileFlags {
  * Resolves an operation list to a graph pass. The first and last operations
  * must be a read/write respectively. `flags` is a list of SwsOpCompileFlags.
  *
+ * Takes over ownership of `ops` and sets it to NULL, even on failure.
+ *
  * Note: `ops` may be modified by this function.
  */
-int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags,
+int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **ops, int flags,
                         const SwsFormat *dst, SwsPass *input, SwsPass 
**output);
 
 #endif
diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index 1c3d3239f6..37ad4fc940 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -369,16 +369,17 @@ fail:
     return ret;
 }
 
-int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, int flags,
+int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **pops, int flags,
                         const SwsFormat *dst, SwsPass *input, SwsPass **output)
 {
     SwsContext *ctx = graph->ctx;
-    int ret;
+    SwsOpList *ops = *pops;
+    int ret = 0;
 
     /* Check if the whole operation graph is an end-to-end no-op */
     if (ff_sws_op_list_is_noop(ops)) {
         *output = input;
-        return 0;
+        goto out;
     }
 
     const SwsOp *read  = ff_sws_op_list_input(ops);
@@ -386,14 +387,20 @@ int ff_sws_compile_pass(SwsGraph *graph, SwsOpList *ops, 
int flags,
     if (!read || !write) {
         av_log(ctx, AV_LOG_ERROR, "First and last operations must be a read "
                "and write, respectively.\n");
-        return AVERROR(EINVAL);
+        ret = AVERROR(EINVAL);
+        goto out;
     }
 
     if (flags & SWS_OP_FLAG_OPTIMIZE) {
         ret = ff_sws_op_list_optimize(ops);
         if (ret < 0)
-            return ret;
+            goto out;
     }
 
-    return compile(graph, ops, dst, input, output);
+    ret = compile(graph, ops, dst, input, output);
+
+out:
+    ff_sws_op_list_free(&ops);
+    *pops = NULL;
+    return ret;
 }
-- 
2.52.0


>From 3bc30a0dca9d7cca9d9d8175f5ca7a2ee8be24da Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Fri, 27 Feb 2026 12:34:18 +0100
Subject: [PATCH 09/12] swscale/ops_dispatch: infer destination format from
 SwsOpList

This is now redundant.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/graph.c        | 2 +-
 libswscale/ops.h          | 4 ++--
 libswscale/ops_dispatch.c | 9 +++++----
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index dda23ddca2..2408fa93f8 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -608,7 +608,7 @@ static int add_convert_pass(SwsGraph *graph, const 
SwsFormat *src,
     av_log(ctx, AV_LOG_DEBUG, "Unoptimized operation list:\n");
     ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops);
 
-    ret = ff_sws_compile_pass(graph, &ops, SWS_OP_FLAG_OPTIMIZE, dst, input, 
output);
+    ret = ff_sws_compile_pass(graph, &ops, SWS_OP_FLAG_OPTIMIZE, input, 
output);
     if (ret < 0)
         goto fail;
 
diff --git a/libswscale/ops.h b/libswscale/ops.h
index 20c9c25605..bc8e751f7b 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -225,7 +225,7 @@ typedef struct SwsOpList {
     SwsOp *ops;
     int num_ops;
 
-    /* Purely informative metadata associated with this operation list */
+    /* Metadata associated with this operation list */
     SwsFormat src, dst;
 
     /* Input/output plane pointer swizzle mask */
@@ -317,6 +317,6 @@ enum SwsOpCompileFlags {
  * Note: `ops` may be modified by this function.
  */
 int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **ops, int flags,
-                        const SwsFormat *dst, SwsPass *input, SwsPass 
**output);
+                        SwsPass *input, SwsPass **output);
 
 #endif
diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index 37ad4fc940..7874f3c494 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -322,8 +322,8 @@ static int rw_pixel_bits(const SwsOp *op)
     return elems * size * bits;
 }
 
-static int compile(SwsGraph *graph, const SwsOpList *ops,
-                   const SwsFormat *dst, SwsPass *input, SwsPass **output)
+static int compile(SwsGraph *graph, const SwsOpList *ops, SwsPass *input,
+                   SwsPass **output)
 {
     SwsContext *ctx = graph->ctx;
     SwsOpPass *p = av_mallocz(sizeof(*p));
@@ -334,6 +334,7 @@ static int compile(SwsGraph *graph, const SwsOpList *ops,
     if (ret < 0)
         goto fail;
 
+    const SwsFormat *dst = &ops->dst;
     if (p->comp.opaque) {
         SwsCompiledOp c = p->comp;
         av_free(p);
@@ -370,7 +371,7 @@ fail:
 }
 
 int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **pops, int flags,
-                        const SwsFormat *dst, SwsPass *input, SwsPass **output)
+                        SwsPass *input, SwsPass **output)
 {
     SwsContext *ctx = graph->ctx;
     SwsOpList *ops = *pops;
@@ -397,7 +398,7 @@ int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **pops, 
int flags,
             goto out;
     }
 
-    ret = compile(graph, ops, dst, input, output);
+    ret = compile(graph, ops, input, output);
 
 out:
     ff_sws_op_list_free(&ops);
-- 
2.52.0


>From 07acd56c33b348bda1f60783d081977432bc37e7 Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Mon, 9 Mar 2026 17:46:10 +0100
Subject: [PATCH 10/12] swscale/ops_dispatch: move ENOTSUP error to
 ff_sws_compile_pass()

Or else this might false-positive when we retry compilation after subpass
splitting.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_dispatch.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index 7874f3c494..239b2e3d7b 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -90,8 +90,6 @@ int ff_sws_ops_compile(SwsContext *ctx, const SwsOpList *ops, 
SwsCompiledOp *out
         return 0;
     }
 
-    av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
-    ff_sws_op_list_print(ctx, AV_LOG_WARNING, AV_LOG_TRACE, ops);
     return AVERROR(ENOTSUP);
 }
 
@@ -401,6 +399,10 @@ int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **pops, 
int flags,
     ret = compile(graph, ops, input, output);
 
 out:
+    if (ret == AVERROR(ENOTSUP)) {
+        av_log(ctx, AV_LOG_WARNING, "No backend found for operations:\n");
+        ff_sws_op_list_print(ctx, AV_LOG_WARNING, AV_LOG_TRACE, ops);
+    }
     ff_sws_op_list_free(&ops);
     *pops = NULL;
     return ret;
-- 
2.52.0


>From 6805f29c023e6dba1964108f2db427ee719687cd Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Mon, 9 Mar 2026 16:48:26 +0100
Subject: [PATCH 11/12] swscale/x86/ops: add section comments (cosmetic)

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/x86/ops_float.asm | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/libswscale/x86/ops_float.asm b/libswscale/x86/ops_float.asm
index c9dc408a9b..610f2402b3 100644
--- a/libswscale/x86/ops_float.asm
+++ b/libswscale/x86/ops_float.asm
@@ -130,6 +130,9 @@ IF W,   vpermq mw, mw, q3120
         CONTINUE tmp0q
 %endmacro
 
+;---------------------------------------------------------
+; Arithmetic operations
+
 %macro min_max 0
 op min
 IF X,   vbroadcastss m8,  [implq + SwsOpImpl.priv + 0]
@@ -179,6 +182,9 @@ IF W,   mulps mw2, m8
         CONTINUE tmp0q
 %endmacro
 
+;---------------------------------------------------------
+; Dithering
+
 %macro dither0 0
 op dither0
         ; constant offset for all channels
@@ -253,6 +259,9 @@ op dither%1
         dither 8
 %endmacro
 
+;---------------------------------------------------------
+; Linear transformations
+
 %xdefine MASK(I, J)  (1 << (5 * (I) + (J)))
 %xdefine MASK_OFF(I) MASK(I, 4)
 %xdefine MASK_ROW(I) (0x1F << (5 * (I)))
-- 
2.52.0


>From 5ad6010859484f73df51866c3e060a2b697c682b Mon Sep 17 00:00:00 2001
From: Niklas Haas <[email protected]>
Date: Mon, 9 Mar 2026 16:44:39 +0100
Subject: [PATCH 12/12] tests/checkasm/sw_ops: declare temporary arrays static

These are quite large; GCC on my end warns about big stack allocations.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <[email protected]>
---
 tests/checkasm/sw_ops.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/checkasm/sw_ops.c b/tests/checkasm/sw_ops.c
index b20a7559f1..0eaca21402 100644
--- a/tests/checkasm/sw_ops.c
+++ b/tests/checkasm/sw_ops.c
@@ -117,10 +117,10 @@ static void check_ops(const char *report, const unsigned 
ranges[NB_PLANES],
 
     declare_func(void, const SwsOpExec *, const void *, int bx, int y, int 
bx_end, int y_end);
 
-    DECLARE_ALIGNED_64(char, src0)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
-    DECLARE_ALIGNED_64(char, src1)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
-    DECLARE_ALIGNED_64(char, dst0)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
-    DECLARE_ALIGNED_64(char, dst1)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
+    static DECLARE_ALIGNED_64(char, src0)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
+    static DECLARE_ALIGNED_64(char, src1)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
+    static DECLARE_ALIGNED_64(char, dst0)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
+    static DECLARE_ALIGNED_64(char, dst1)[NB_PLANES][LINES][PIXELS * 
sizeof(uint32_t[4])];
 
     if (!ctx)
         return;
-- 
2.52.0

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

Reply via email to