PR #22510 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22510 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22510.patch
In anticipation of the main filtering branch. From f98493ff765baa2d16ec100dcd38266c8b04908c Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Mon, 9 Mar 2026 13:07:25 +0100 Subject: [PATCH 01/13] swscale/graph: allocate output buffers after adding all passes There's no reason to immediately allocate all of these; we can do it at the end when we know for sure which passes we have. This will matter especially if we ever add a way to remove passes again after adding them (spoiler: we will). Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/graph.c | 11 +++++++---- libswscale/graph.h | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libswscale/graph.c b/libswscale/graph.c index db11591862..9776ff2736 100644 --- a/libswscale/graph.c +++ b/libswscale/graph.c @@ -141,10 +141,6 @@ int ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt, goto fail; } - ret = pass_alloc_output(input); - if (ret < 0) - goto fail; - if (!align) { pass->slice_h = pass->height; pass->num_slices = 1; @@ -795,6 +791,13 @@ int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat * if (ret < 0) goto error; + /* Resolve output buffers for all intermediate passes */ + for (int i = 0; i < graph->num_passes; i++) { + ret = pass_alloc_output(graph->passes[i]->input); + if (ret < 0) + goto error; + } + *out_graph = graph; return 0; diff --git a/libswscale/graph.h b/libswscale/graph.h index 4267851baa..f50ffc5894 100644 --- a/libswscale/graph.h +++ b/libswscale/graph.h @@ -84,7 +84,7 @@ struct SwsPass { * Filter input. This pass's output will be resolved to form this pass's. * input. If NULL, the original input image is used. */ - const SwsPass *input; + SwsPass *input; /** * Filter output buffer. Allocated on demand and freed automatically. -- 2.52.0 From 549d39927b950d476101fc187b7a74562e1fb727 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Mon, 9 Mar 2026 15:45:18 +0100 Subject: [PATCH 02/13] swscale/ops: loop over copied list instead of original (cosmetic) Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 9bea2f0b4f..ece49e299f 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -555,11 +555,11 @@ SwsOpList *ff_sws_op_list_duplicate(const SwsOpList *ops) return NULL; } - for (int i = 0; i < ops->num_ops; i++) { - const SwsOp *op = &ops->ops[i]; + for (int i = 0; i < copy->num_ops; i++) { + const SwsOp *op = ©->ops[i]; switch (op->op) { case SWS_OP_DITHER: - av_refstruct_ref(copy->ops[i].dither.matrix); + av_refstruct_ref(op->dither.matrix); break; } } -- 2.52.0 From df6a2d7f287b72ce168a7d61ae1cc5f2c5fa0a85 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sun, 15 Mar 2026 12:37:08 +0100 Subject: [PATCH 03/13] swscale/ops_dispatch: remove slice_align hack Added in commit 00907e1244ef3de to hack around a problem that was caused by the Vulkan backend's incorrect use of the ops dispatch code, which was fixed properly in commit 143cb5650123. This logic never made sense to begin with, it was only meant to disable the memcpy logic for Vulkan specifically. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_dispatch.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index 239b2e3d7b..713b34b86b 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -152,8 +152,7 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, const int plane_w = (aligned_w + sub_x) >> sub_x; const int plane_pad = (comp->over_read + sub_x) >> sub_x; const int plane_size = plane_w * p->pixel_bits_in >> 3; - if (comp->slice_align) - p->memcpy_in |= plane_size + plane_pad > in->linesize[idx]; + p->memcpy_in |= plane_size + plane_pad > in->linesize[idx]; exec->in[i] = in->data[idx]; exec->in_stride[i] = in->linesize[idx]; exec->in_sub_y[i] = sub_y; @@ -168,8 +167,7 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, const int plane_w = (aligned_w + sub_x) >> sub_x; const int plane_pad = (comp->over_write + sub_x) >> sub_x; const int plane_size = plane_w * p->pixel_bits_out >> 3; - if (comp->slice_align) - p->memcpy_out |= plane_size + plane_pad > out->linesize[idx]; + p->memcpy_out |= plane_size + plane_pad > out->linesize[idx]; exec->out[i] = out->data[idx]; exec->out_stride[i] = out->linesize[idx]; exec->out_sub_y[i] = sub_y; -- 2.52.0 From 9df844aba5e45b8350f367cee35a74b6607b3b98 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 6 Mar 2026 22:41:24 +0100 Subject: [PATCH 04/13] swscale/ops_dispatch: properly handle negative strides The `memcpy_in` condition is reversed for negative strides, which require a memcpy() on the *first* line, not the last line. Additionally, the check just completely didn't work for negative linesizes, due to comparing against a negative stride. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_dispatch.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index 713b34b86b..fb176dba15 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -40,7 +40,8 @@ typedef struct SwsOpPass { int pixel_bits_out; int idx_in[4]; int idx_out[4]; - bool memcpy_in; + bool memcpy_first; + bool memcpy_last; bool memcpy_out; } SwsOpPass; @@ -141,7 +142,8 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, p->tail_off_out = safe_width * p->pixel_bits_out >> 3; p->tail_size_in = tail_size * p->pixel_bits_in >> 3; p->tail_size_out = tail_size * p->pixel_bits_out >> 3; - p->memcpy_in = false; + p->memcpy_first = false; + p->memcpy_last = false; p->memcpy_out = false; for (int i = 0; i < p->planes_in; i++) { @@ -152,7 +154,12 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, const int plane_w = (aligned_w + sub_x) >> sub_x; const int plane_pad = (comp->over_read + sub_x) >> sub_x; const int plane_size = plane_w * p->pixel_bits_in >> 3; - p->memcpy_in |= plane_size + plane_pad > in->linesize[idx]; + const int total_size = plane_size + plane_pad; + if (in->linesize[idx] >= 0) { + p->memcpy_last |= total_size > in->linesize[idx]; + } else { + p->memcpy_first |= total_size > -in->linesize[idx]; + } exec->in[i] = in->data[idx]; exec->in_stride[i] = in->linesize[idx]; exec->in_sub_y[i] = sub_y; @@ -167,7 +174,7 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, const int plane_w = (aligned_w + sub_x) >> sub_x; const int plane_pad = (comp->over_write + sub_x) >> sub_x; const int plane_size = plane_w * p->pixel_bits_out >> 3; - p->memcpy_out |= plane_size + plane_pad > out->linesize[idx]; + p->memcpy_out |= plane_size + plane_pad > FFABS(out->linesize[idx]); exec->out[i] = out->data[idx]; exec->out_stride[i] = out->linesize[idx]; exec->out_sub_y[i] = sub_y; @@ -268,7 +275,9 @@ static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y, * * 1. We can overread the input, unless this is the last line of an * unpadded buffer. All defined operations can handle arbitrary pixel - * input, so overread of arbitrary data is fine. + * input, so overread of arbitrary data is fine. For flipped images, + * this condition is actually *inverted* to where the first line is + * the one at the end of the buffer. * * 2. We can overwrite the output, as long as we don't write more than the * amount of pixels that fit into one linesize. So we always need to @@ -280,8 +289,8 @@ static void op_pass_run(const SwsFrame *out, const SwsFrame *in, const int y, * need to worry about this for the end of a slice. */ - const int last_slice = y + h == pass->height; - const bool memcpy_in = last_slice && p->memcpy_in; + const bool memcpy_in = p->memcpy_last && y + h == pass->height || + p->memcpy_first && y == 0; const bool memcpy_out = p->memcpy_out; const int num_blocks = p->num_blocks; const int blocks_main = num_blocks - memcpy_out; -- 2.52.0 From 3dbf9ef4bc7dfe8de052e79fa28fb9286f754fc0 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Tue, 10 Mar 2026 17:03:37 +0100 Subject: [PATCH 05/13] swscale/ops_dispatch: correctly round tail size If the block size is somehow less than 8, this may round down, leading to one byte too few being copied (e.g. for monow/rgb4). This was never an issue for current backends because they all have block sizes of 8 or larger, but a future platform may have different requirements. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_dispatch.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index fb176dba15..4e44fa562d 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -140,8 +140,8 @@ static int op_pass_setup(const SwsFrame *out, const SwsFrame *in, const int tail_size = pass->width - safe_width; p->tail_off_in = safe_width * p->pixel_bits_in >> 3; p->tail_off_out = safe_width * p->pixel_bits_out >> 3; - p->tail_size_in = tail_size * p->pixel_bits_in >> 3; - p->tail_size_out = tail_size * p->pixel_bits_out >> 3; + p->tail_size_in = (tail_size * p->pixel_bits_in + 7) >> 3; + p->tail_size_out = (tail_size * p->pixel_bits_out + 7) >> 3; p->memcpy_first = false; p->memcpy_last = false; p->memcpy_out = false; -- 2.52.0 From 6c578b58fa9f68843faaaa28650eda224ff6e25a Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Mon, 9 Mar 2026 13:00:28 +0100 Subject: [PATCH 06/13] swscale/ops_dispatch: also print ops list after optimizing Will make more sense in light of the fact that this may not correspond to the op list actually sent to the backends, due to subpass splitting. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_dispatch.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index 4e44fa562d..abd8810a6b 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -401,6 +401,8 @@ int ff_sws_compile_pass(SwsGraph *graph, SwsOpList **pops, int flags, ret = ff_sws_op_list_optimize(ops); if (ret < 0) goto out; + av_log(ctx, AV_LOG_DEBUG, "Operation list after optimizing:\n"); + ff_sws_op_list_print(ctx, AV_LOG_DEBUG, AV_LOG_TRACE, ops); } ret = compile(graph, ops, input, output); -- 2.52.0 From 215183b8e61d819ce02beaba629b5da888a4a966 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 12 Mar 2026 13:30:23 +0100 Subject: [PATCH 07/13] swscale/ops_chain: refactor setup() signature This is basically a cosmetic commit that groups all of the parameters to setup() into a single struct, as well as the return type. This gives the immediate benefit of freeing up 8 bytes per op table entry, though the main motivation will come in the following commits. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_backend.h | 13 +++++----- libswscale/ops_chain.c | 51 ++++++++++++++++++++----------------- libswscale/ops_chain.h | 31 ++++++++++++++-------- libswscale/ops_tmpl_float.c | 21 ++++++++------- libswscale/x86/ops.c | 48 +++++++++++++++++++--------------- 5 files changed, 94 insertions(+), 70 deletions(-) diff --git a/libswscale/ops_backend.h b/libswscale/ops_backend.h index b1616f6b02..6ece263178 100644 --- a/libswscale/ops_backend.h +++ b/libswscale/ops_backend.h @@ -114,14 +114,15 @@ typedef struct SwsOpIter { (iter, &impl[1], __VA_ARGS__) /* Helper macros for common op setup code */ -#define DECL_SETUP(NAME) \ - static int fn(NAME)(const SwsOp *op, SwsOpPriv *out) +#define DECL_SETUP(NAME, PARAMS, OUT) \ + static int fn(NAME)(const SwsImplParams *PARAMS, SwsImplResult *OUT) -#define SETUP_MEMDUP(c) ff_setup_memdup(&(c), sizeof(c), out) -static inline int ff_setup_memdup(const void *c, size_t size, SwsOpPriv *out) +#define SETUP_MEMDUP(c, out) ff_setup_memdup(&(c), sizeof(c), out) +static inline int ff_setup_memdup(const void *c, size_t size, SwsImplResult *out) { - out->ptr = av_memdup(c, size); - return out->ptr ? 0 : AVERROR(ENOMEM); + out->priv.ptr = av_memdup(c, size); + out->free = ff_op_priv_free; + return out->priv.ptr ? 0 : AVERROR(ENOMEM); } /* Helper macro for declaring op table entries */ diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index 1a4ac539ba..6e0a9cf9b3 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -203,7 +203,6 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, const SwsOpEntry *best = NULL; const SwsOp *op = &ops->ops[0]; int ret, best_score = 0, best_cpu_flags; - SwsOpPriv priv = {0}; for (int n = 0; n < num_tables; n++) { const SwsOpTable *table = tables[n]; @@ -225,17 +224,20 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, if (!best) return AVERROR(ENOTSUP); + SwsImplResult res = {0}; if (best->setup) { - ret = best->setup(op, &priv); + const SwsImplParams params = { .op = op }; + ret = best->setup(¶ms, &res); if (ret < 0) return ret; } chain->cpu_flags |= best_cpu_flags; - ret = ff_sws_op_chain_append(chain, best->func, best->free, &priv); + ret = ff_sws_op_chain_append(chain, res.func ? res.func : best->func, + res.free, &res.priv); if (ret < 0) { - if (best->free) - best->free(priv); + if (res.free) + res.free(res.priv); return ret; } @@ -246,44 +248,45 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0) -int ff_sws_setup_u8(const SwsOp *op, SwsOpPriv *out) +int ff_sws_setup_u8(const SwsImplParams *params, SwsImplResult *out) { - out->u8[0] = op->c.u; + out->priv.u8[0] = params->op->c.u; return 0; } -int ff_sws_setup_u(const SwsOp *op, SwsOpPriv *out) +int ff_sws_setup_u(const SwsImplParams *params, SwsImplResult *out) { + const SwsOp *op = params->op; switch (op->type) { - case SWS_PIXEL_U8: out->u8[0] = op->c.u; return 0; - case SWS_PIXEL_U16: out->u16[0] = op->c.u; return 0; - case SWS_PIXEL_U32: out->u32[0] = op->c.u; return 0; - case SWS_PIXEL_F32: out->f32[0] = op->c.u; return 0; + case SWS_PIXEL_U8: out->priv.u8[0] = op->c.u; return 0; + case SWS_PIXEL_U16: out->priv.u16[0] = op->c.u; return 0; + case SWS_PIXEL_U32: out->priv.u32[0] = op->c.u; return 0; + case SWS_PIXEL_F32: out->priv.f32[0] = op->c.u; return 0; default: return AVERROR(EINVAL); } } -int ff_sws_setup_q(const SwsOp *op, SwsOpPriv *out) +int ff_sws_setup_q(const SwsImplParams *params, SwsImplResult *out) { + const SwsOp *op = params->op; switch (op->type) { - case SWS_PIXEL_U8: out->u8[0] = q2pixel(uint8_t, op->c.q); return 0; - case SWS_PIXEL_U16: out->u16[0] = q2pixel(uint16_t, op->c.q); return 0; - case SWS_PIXEL_U32: out->u32[0] = q2pixel(uint32_t, op->c.q); return 0; - case SWS_PIXEL_F32: out->f32[0] = q2pixel(float, op->c.q); return 0; + case SWS_PIXEL_U8: out->priv.u8[0] = q2pixel(uint8_t, op->c.q); return 0; + case SWS_PIXEL_U16: out->priv.u16[0] = q2pixel(uint16_t, op->c.q); return 0; + case SWS_PIXEL_U32: out->priv.u32[0] = q2pixel(uint32_t, op->c.q); return 0; + case SWS_PIXEL_F32: out->priv.f32[0] = q2pixel(float, op->c.q); return 0; default: return AVERROR(EINVAL); } - - return 0; } -int ff_sws_setup_q4(const SwsOp *op, SwsOpPriv *out) +int ff_sws_setup_q4(const SwsImplParams *params, SwsImplResult *out) { + const SwsOp *op = params->op; for (int i = 0; i < 4; i++) { switch (op->type) { - case SWS_PIXEL_U8: out->u8[i] = q2pixel(uint8_t, op->c.q4[i]); break; - case SWS_PIXEL_U16: out->u16[i] = q2pixel(uint16_t, op->c.q4[i]); break; - case SWS_PIXEL_U32: out->u32[i] = q2pixel(uint32_t, op->c.q4[i]); break; - case SWS_PIXEL_F32: out->f32[i] = q2pixel(float, op->c.q4[i]); break; + case SWS_PIXEL_U8: out->priv.u8[i] = q2pixel(uint8_t, op->c.q4[i]); break; + case SWS_PIXEL_U16: out->priv.u16[i] = q2pixel(uint16_t, op->c.q4[i]); break; + case SWS_PIXEL_U32: out->priv.u32[i] = q2pixel(uint32_t, op->c.q4[i]); break; + case SWS_PIXEL_F32: out->priv.f32[i] = q2pixel(float, op->c.q4[i]); break; default: return AVERROR(EINVAL); } } diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h index 76ab5d964b..4eb12a5306 100644 --- a/libswscale/ops_chain.h +++ b/libswscale/ops_chain.h @@ -37,6 +37,8 @@ * that is an implementation detail of the specific backend. */ +typedef struct SwsOpTable SwsOpTable; + /** * Private data for each kernel. */ @@ -60,12 +62,6 @@ typedef union SwsOpPriv { static_assert(sizeof(SwsOpPriv) == 16, "SwsOpPriv size mismatch"); -/* Setup helpers */ -int ff_sws_setup_u(const SwsOp *op, SwsOpPriv *out); -int ff_sws_setup_u8(const SwsOp *op, SwsOpPriv *out); -int ff_sws_setup_q(const SwsOp *op, SwsOpPriv *out); -int ff_sws_setup_q4(const SwsOp *op, SwsOpPriv *out); - /** * Per-kernel execution context. * @@ -104,6 +100,16 @@ static inline void ff_sws_op_chain_free(SwsOpChain *chain) int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, void (*free)(SwsOpPriv), const SwsOpPriv *priv); +typedef struct SwsImplParams { + const SwsOp *op; +} SwsImplParams; + +typedef struct SwsImplResult { + SwsFuncPtr func; /* overrides `SwsOpEntry.func` if non-NULL */ + SwsOpPriv priv; /* private data for this implementation instance */ + void (*free)(SwsOpPriv priv); /* free function for `priv` */ +} SwsImplResult; + typedef struct SwsOpEntry { /* Kernel metadata; reduced size subset of SwsOp */ SwsOpType op; @@ -124,20 +130,25 @@ typedef struct SwsOpEntry { /* Kernel implementation */ SwsFuncPtr func; - int (*setup)(const SwsOp *op, SwsOpPriv *out); /* optional */ - void (*free)(SwsOpPriv priv); + int (*setup)(const SwsImplParams *params, SwsImplResult *out); /* optional */ } SwsOpEntry; +/* Setup helpers */ +int ff_sws_setup_u(const SwsImplParams *params, SwsImplResult *out); +int ff_sws_setup_u8(const SwsImplParams *params, SwsImplResult *out); +int ff_sws_setup_q(const SwsImplParams *params, SwsImplResult *out); +int ff_sws_setup_q4(const SwsImplParams *params, SwsImplResult *out); + static inline void ff_op_priv_free(SwsOpPriv priv) { av_free(priv.ptr); } -typedef struct SwsOpTable { +struct SwsOpTable { unsigned cpu_flags; /* required CPU flags for this table */ int block_size; /* fixed block size of this table */ const SwsOpEntry *entries[]; /* terminated by NULL */ -} SwsOpTable; +}; /** * "Compile" a single op by looking it up in a list of fixed size op tables. diff --git a/libswscale/ops_tmpl_float.c b/libswscale/ops_tmpl_float.c index d5ac7fb75f..ea5d3cf3d0 100644 --- a/libswscale/ops_tmpl_float.c +++ b/libswscale/ops_tmpl_float.c @@ -41,23 +41,27 @@ #define FMT_CHAR f #include "ops_tmpl_common.c" -DECL_SETUP(setup_dither) +DECL_SETUP(setup_dither, params, out) { + const SwsOp *op = params->op; const int size = 1 << op->dither.size_log2; if (size == 1) { /* We special case this value */ av_assert1(!av_cmp_q(op->dither.matrix[0], av_make_q(1, 2))); - out->ptr = NULL; + out->priv.ptr = NULL; return 0; } const int width = FFMAX(size, SWS_BLOCK_SIZE); - pixel_t *matrix = out->ptr = av_malloc(sizeof(pixel_t) * size * width); + pixel_t *matrix = out->priv.ptr = av_malloc(sizeof(pixel_t) * size * width); if (!matrix) return AVERROR(ENOMEM); + out->free = ff_op_priv_free; - static_assert(sizeof(out->ptr) <= sizeof(uint8_t[8]), ">8 byte pointers not supported"); - int8_t *offset = &out->i8[8]; + static_assert(sizeof(out->priv.ptr) <= sizeof(uint8_t[8]), + ">8 byte pointers not supported"); + + int8_t *offset = &out->priv.i8[8]; for (int i = 0; i < 4; i++) offset[i] = op->dither.y_offset[i]; @@ -107,7 +111,6 @@ DECL_ENTRY(dither##N, .op = SWS_OP_DITHER, \ .dither_size = N, \ .setup = fn(setup_dither), \ - .free = ff_op_priv_free, \ ); WRAP_DITHER(0) @@ -126,8 +129,9 @@ typedef struct { pixel_t k[4]; } fn(LinCoeffs); -DECL_SETUP(setup_linear) +DECL_SETUP(setup_linear, params, out) { + const SwsOp *op = params->op; fn(LinCoeffs) c; for (int i = 0; i < 4; i++) { @@ -136,7 +140,7 @@ DECL_SETUP(setup_linear) c.k[i] = av_q2pixel(op->lin.m[i][4]); } - return SETUP_MEMDUP(c); + return SETUP_MEMDUP(c, out); } /** @@ -193,7 +197,6 @@ DECL_IMPL(linear_##NAME) DECL_ENTRY(linear_##NAME, \ .op = SWS_OP_LINEAR, \ .setup = fn(setup_linear), \ - .free = ff_op_priv_free, \ .linear_mask = (MASK), \ ); diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c index 76d374430c..bd4b030333 100644 --- a/libswscale/x86/ops.c +++ b/libswscale/x86/ops.c @@ -83,11 +83,11 @@ .pack.pattern = {X, Y, Z, W}, \ ); \ -static int setup_swap_bytes(const SwsOp *op, SwsOpPriv *out) +static int setup_swap_bytes(const SwsImplParams *params, SwsImplResult *out) { - const int mask = ff_sws_pixel_type_size(op->type) - 1; + const int mask = ff_sws_pixel_type_size(params->op->type) - 1; for (int i = 0; i < 16; i++) - out->u8[i] = (i & ~mask) | (mask - (i & mask)); + out->priv.u8[i] = (i & ~mask) | (mask - (i & mask)); return 0; } @@ -113,10 +113,11 @@ static int setup_swap_bytes(const SwsOp *op, SwsOpPriv *out) .unused[IDX] = true, \ ); -static int setup_clear(const SwsOp *op, SwsOpPriv *out) +static int setup_clear(const SwsImplParams *params, SwsImplResult *out) { + const SwsOp *op = params->op; for (int i = 0; i < 4; i++) - out->u32[i] = (uint32_t) op->c.q4[i].num; + out->priv.u32[i] = (uint32_t) op->c.q4[i].num; return 0; } @@ -146,9 +147,9 @@ static int setup_clear(const SwsOp *op, SwsOpPriv *out) .convert.expand = true, \ ); -static int setup_shift(const SwsOp *op, SwsOpPriv *out) +static int setup_shift(const SwsImplParams *params, SwsImplResult *out) { - out->u16[0] = op->c.u; + out->priv.u16[0] = params->op->c.u; return 0; } @@ -191,12 +192,13 @@ static int setup_shift(const SwsOp *op, SwsOpPriv *out) .scale = { .num = ((1 << (BITS)) - 1), .den = 1 }, \ ); -static int setup_dither(const SwsOp *op, SwsOpPriv *out) +static int setup_dither(const SwsImplParams *params, SwsImplResult *out) { + const SwsOp *op = params->op; /* 1x1 matrix / single constant */ if (!op->dither.size_log2) { const AVRational k = op->dither.matrix[0]; - out->f32[0] = (float) k.num / k.den; + out->priv.f32[0] = (float) k.num / k.den; return 0; } @@ -214,9 +216,10 @@ static int setup_dither(const SwsOp *op, SwsOpPriv *out) * typically 320 bytes for a 16x16 dither matrix. */ const int stride = size * sizeof(float); const int num_rows = size + max_offset; - float *matrix = out->ptr = av_mallocz(num_rows * stride); + float *matrix = out->priv.ptr = av_mallocz(num_rows * stride); if (!matrix) return AVERROR(ENOMEM); + out->free = ff_op_priv_free; for (int i = 0; i < size * size; i++) matrix[i] = (float) op->dither.matrix[i].num / op->dither.matrix[i].den; @@ -224,9 +227,10 @@ static int setup_dither(const SwsOp *op, SwsOpPriv *out) memcpy(&matrix[size * size], matrix, max_offset * stride); /* Store relative pointer offset to each row inside extra space */ - static_assert(sizeof(out->ptr) <= sizeof(int16_t[4]), ">8 byte pointers not supported"); + static_assert(sizeof(out->priv.ptr) <= sizeof(int16_t[4]), + ">8 byte pointers not supported"); assert(max_offset * stride <= INT16_MAX); - int16_t *off_out = &out->i16[4]; + int16_t *off_out = &out->priv.i16[4]; for (int i = 0; i < 4; i++) off_out[i] = off[i] >= 0 ? (off[i] & (size - 1)) * stride : -1; @@ -237,15 +241,17 @@ static int setup_dither(const SwsOp *op, SwsOpPriv *out) DECL_MACRO(F32, dither##SIZE##EXT, \ .op = SWS_OP_DITHER, \ .setup = setup_dither, \ - .free = (SIZE) ? ff_op_priv_free : NULL, \ .dither_size = SIZE, \ ); -static int setup_linear(const SwsOp *op, SwsOpPriv *out) +static int setup_linear(const SwsImplParams *params, SwsImplResult *out) { - float *matrix = out->ptr = av_mallocz(sizeof(float[4][5])); + const SwsOp *op = params->op; + + float *matrix = out->priv.ptr = av_mallocz(sizeof(float[4][5])); if (!matrix) return AVERROR(ENOMEM); + out->free = ff_op_priv_free; for (int y = 0; y < 4; y++) { for (int x = 0; x < 5; x++) @@ -259,7 +265,6 @@ static int setup_linear(const SwsOp *op, SwsOpPriv *out) DECL_ASM(F32, NAME##EXT, \ .op = SWS_OP_LINEAR, \ .setup = setup_linear, \ - .free = ff_op_priv_free, \ .linear_mask = (MASK), \ ); @@ -651,20 +656,21 @@ do { static void normalize_clear(SwsOp *op) { static_assert(sizeof(uint32_t) == sizeof(int), "int size mismatch"); - SwsOpPriv priv; + SwsImplResult res; union { uint32_t u32; int i; } c; - ff_sws_setup_q4(op, &priv); + ff_sws_setup_q4(&(const SwsImplParams) { .op = op }, &res); + for (int i = 0; i < 4; i++) { if (!op->c.q4[i].den) continue; switch (ff_sws_pixel_type_size(op->type)) { - case 1: c.u32 = 0x1010101U * priv.u8[i]; break; - case 2: c.u32 = (uint32_t)priv.u16[i] << 16 | priv.u16[i]; break; - case 4: c.u32 = priv.u32[i]; break; + case 1: c.u32 = 0x1010101U * res.priv.u8[i]; break; + case 2: c.u32 = (uint32_t) res.priv.u16[i] << 16 | res.priv.u16[i]; break; + case 4: c.u32 = res.priv.u32[i]; break; } op->c.q4[i].num = c.i; -- 2.52.0 From c95cb1fc8c24c0d81f2e024185c9b39c2022e4cd Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sun, 15 Mar 2026 12:44:47 +0100 Subject: [PATCH 08/13] swscale/ops_chain: take pointer to SwsOpPriv in free() cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes compilation on MSVC for ARM (32-bit): error C2719: 'unnamed-parameter': formal parameter with requested alignment of 16 won't be aligned Reported-by: Martin Storsjö <[email protected]> Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_chain.c | 6 +++--- libswscale/ops_chain.h | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index 6e0a9cf9b3..5a003828b7 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); + chain->free[i](&chain->impl[i].priv); } av_free(chain); } int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, - void (*free)(SwsOpPriv), const SwsOpPriv *priv) + void (*free)(SwsOpPriv *), const SwsOpPriv *priv) { const int idx = chain->num_impl; if (idx == SWS_MAX_OPS) @@ -237,7 +237,7 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, res.free, &res.priv); if (ret < 0) { if (res.free) - res.free(res.priv); + res.free(&res.priv); return ret; } diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h index 4eb12a5306..7ee9775955 100644 --- a/libswscale/ops_chain.h +++ b/libswscale/ops_chain.h @@ -84,7 +84,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])(SwsOpPriv); + void (*free[SWS_MAX_OPS + 1])(SwsOpPriv *); int num_impl; int cpu_flags; /* set of all used CPU flags */ } SwsOpChain; @@ -98,7 +98,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)(SwsOpPriv), const SwsOpPriv *priv); + void (*free)(SwsOpPriv *priv), const SwsOpPriv *priv); typedef struct SwsImplParams { const SwsOp *op; @@ -107,7 +107,7 @@ typedef struct SwsImplParams { typedef struct SwsImplResult { SwsFuncPtr func; /* overrides `SwsOpEntry.func` if non-NULL */ SwsOpPriv priv; /* private data for this implementation instance */ - void (*free)(SwsOpPriv priv); /* free function for `priv` */ + void (*free)(SwsOpPriv *priv); /* free function for `priv` */ } SwsImplResult; typedef struct SwsOpEntry { @@ -139,9 +139,9 @@ int ff_sws_setup_u8(const SwsImplParams *params, SwsImplResult *out); int ff_sws_setup_q(const SwsImplParams *params, SwsImplResult *out); int ff_sws_setup_q4(const SwsImplParams *params, SwsImplResult *out); -static inline void ff_op_priv_free(SwsOpPriv priv) +static inline void ff_op_priv_free(SwsOpPriv *priv) { - av_free(priv.ptr); + av_freep(&priv->ptr); } struct SwsOpTable { -- 2.52.0 From 364a9c751dcf36245ee4c1fb64dd0d726c3b345d Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 12 Mar 2026 21:37:39 +0100 Subject: [PATCH 09/13] swscale/ops_chain: add SwsOpTable to SwsImplParams Mainly so setup functions can look at table->block_size, and perhaps the table flags, as well as anything else we may add in the future. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_chain.c | 9 +++++---- libswscale/ops_chain.h | 1 + 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index 5a003828b7..aee8f8558e 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -201,8 +201,9 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, const SwsOp *next = ops->num_ops > 1 ? &ops->ops[1] : &dummy; const unsigned cpu_flags = av_get_cpu_flags(); const SwsOpEntry *best = NULL; + const SwsOpTable *best_table = NULL; const SwsOp *op = &ops->ops[0]; - int ret, best_score = 0, best_cpu_flags; + int ret, best_score = 0; for (int n = 0; n < num_tables; n++) { const SwsOpTable *table = tables[n]; @@ -215,7 +216,7 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, int score = op_match(op, entry, next->comps); if (score > best_score) { best_score = score; - best_cpu_flags = table->cpu_flags; + best_table = table; best = entry; } } @@ -226,13 +227,13 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, SwsImplResult res = {0}; if (best->setup) { - const SwsImplParams params = { .op = op }; + const SwsImplParams params = { .op = op, .table = best_table }; ret = best->setup(¶ms, &res); if (ret < 0) return ret; } - chain->cpu_flags |= best_cpu_flags; + chain->cpu_flags |= best_table->cpu_flags; ret = ff_sws_op_chain_append(chain, res.func ? res.func : best->func, res.free, &res.priv); if (ret < 0) { diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h index 7ee9775955..a42c5061a4 100644 --- a/libswscale/ops_chain.h +++ b/libswscale/ops_chain.h @@ -101,6 +101,7 @@ int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, void (*free)(SwsOpPriv *priv), const SwsOpPriv *priv); typedef struct SwsImplParams { + const SwsOpTable *table; const SwsOp *op; } SwsImplParams; -- 2.52.0 From 6e2d0f6c7a50aef2b0ecd05483810b7b16c0cc40 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Fri, 13 Mar 2026 13:55:34 +0100 Subject: [PATCH 10/13] swscale/ops_chain: add SwsContext to SwsImplParams Mainly so that implementations can consult sws->flags, to e.g. decide whether the kernel needs to be bit-exact. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_backend.c | 4 ++-- libswscale/ops_chain.c | 11 ++++++++--- libswscale/ops_chain.h | 5 +++-- libswscale/x86/ops.c | 4 ++-- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/libswscale/ops_backend.c b/libswscale/ops_backend.c index ceecae4f99..aaaa75ed0d 100644 --- a/libswscale/ops_backend.c +++ b/libswscale/ops_backend.c @@ -69,8 +69,8 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) /* Make on-stack copy of `ops` to iterate over */ SwsOpList rest = *ops; do { - ret = ff_sws_op_compile_tables(tables, FF_ARRAY_ELEMS(tables), &rest, - SWS_BLOCK_SIZE, chain); + ret = ff_sws_op_compile_tables(ctx, tables, FF_ARRAY_ELEMS(tables), + &rest, SWS_BLOCK_SIZE, chain); } while (ret == AVERROR(EAGAIN)); if (ret < 0) { diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index aee8f8558e..9ebbe675f0 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -193,8 +193,8 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps nex return 0; } -int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, - SwsOpList *ops, const int block_size, +int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[], + int num_tables, SwsOpList *ops, const int block_size, SwsOpChain *chain) { static const SwsOp dummy = { .comps.unused = { true, true, true, true }}; @@ -227,7 +227,12 @@ int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, SwsImplResult res = {0}; if (best->setup) { - const SwsImplParams params = { .op = op, .table = best_table }; + const SwsImplParams params = { + .ctx = ctx, + .op = op, + .table = best_table, + }; + ret = best->setup(¶ms, &res); if (ret < 0) return ret; diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h index a42c5061a4..de8e81f9c0 100644 --- a/libswscale/ops_chain.h +++ b/libswscale/ops_chain.h @@ -103,6 +103,7 @@ int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, typedef struct SwsImplParams { const SwsOpTable *table; const SwsOp *op; + SwsContext *ctx; } SwsImplParams; typedef struct SwsImplResult { @@ -157,8 +158,8 @@ struct SwsOpTable { * * Returns 0, AVERROR(EAGAIN), or a negative error code. */ -int ff_sws_op_compile_tables(const SwsOpTable *const tables[], int num_tables, - SwsOpList *ops, const int block_size, +int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[], + int num_tables, SwsOpList *ops, const int block_size, SwsOpChain *chain); #endif diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c index bd4b030333..969fca6128 100644 --- a/libswscale/x86/ops.c +++ b/libswscale/x86/ops.c @@ -728,8 +728,8 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) op->type = SWS_PIXEL_U8; } - ret = ff_sws_op_compile_tables(tables, FF_ARRAY_ELEMS(tables), &rest, - op_block_size, chain); + ret = ff_sws_op_compile_tables(ctx, tables, FF_ARRAY_ELEMS(tables), + &rest, op_block_size, chain); } while (ret == AVERROR(EAGAIN)); if (ret < 0) { -- 2.52.0 From c5aa1ae3ac0738c8ce694edda888346a43e4f593 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 12 Mar 2026 21:41:55 +0100 Subject: [PATCH 11/13] swscale/ops_chain: allow implementations to expose over_read/write And plumb it all the way through to the SwsCompiledOp. This is cleaner than setting up this metadata up-front in x86/ops.c; and more importantly, it allows us to determine the amount of over-read programmatically during ops setup. Sponsored-by: Sovereign Tech Fund Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_backend.c | 2 ++ libswscale/ops_chain.c | 5 ++++- libswscale/ops_chain.h | 6 +++++- libswscale/x86/ops.c | 8 +++++--- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/libswscale/ops_backend.c b/libswscale/ops_backend.c index aaaa75ed0d..cbc7eba1b4 100644 --- a/libswscale/ops_backend.c +++ b/libswscale/ops_backend.c @@ -86,6 +86,8 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) .slice_align = 1, .block_size = SWS_BLOCK_SIZE, .cpu_flags = chain->cpu_flags, + .over_read = chain->over_read, + .over_write = chain->over_write, .priv = chain, .free = ff_sws_op_chain_free_cb, }; diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index 9ebbe675f0..d159acb119 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -238,7 +238,6 @@ int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[], return ret; } - chain->cpu_flags |= best_table->cpu_flags; ret = ff_sws_op_chain_append(chain, res.func ? res.func : best->func, res.free, &res.priv); if (ret < 0) { @@ -247,6 +246,10 @@ int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[], return ret; } + chain->cpu_flags |= best_table->cpu_flags; + chain->over_read = FFMAX(chain->over_read, res.over_read); + chain->over_write = FFMAX(chain->over_write, res.over_write); + ops->ops++; ops->num_ops--; return ops->num_ops ? AVERROR(EAGAIN) : 0; diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h index de8e81f9c0..37c7303510 100644 --- a/libswscale/ops_chain.h +++ b/libswscale/ops_chain.h @@ -86,7 +86,9 @@ typedef struct SwsOpChain { SwsOpImpl impl[SWS_MAX_OPS + 1]; /* reserve extra space for the entrypoint */ void (*free[SWS_MAX_OPS + 1])(SwsOpPriv *); int num_impl; - int cpu_flags; /* set of all used CPU flags */ + int cpu_flags; /* set of all used CPU flags */ + int over_read; /* chain over-reads input by this many bytes */ + int over_write; /* chain over-writes output by this many bytes */ } SwsOpChain; SwsOpChain *ff_sws_op_chain_alloc(void); @@ -110,6 +112,8 @@ typedef struct SwsImplResult { SwsFuncPtr func; /* overrides `SwsOpEntry.func` if non-NULL */ SwsOpPriv priv; /* private data for this implementation instance */ void (*free)(SwsOpPriv *priv); /* free function for `priv` */ + int over_read; /* implementation over-reads input by this many bytes */ + int over_write; /* implementation over-writes output by this many bytes */ } SwsImplResult; typedef struct SwsOpEntry { diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c index 969fca6128..88db0d3e76 100644 --- a/libswscale/x86/ops.c +++ b/libswscale/x86/ops.c @@ -710,9 +710,9 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) /* 3-component reads/writes process one extra garbage word */ if (read && read->rw.packed && read->rw.elems == 3) - out->over_read = sizeof(uint32_t); + chain->over_read = sizeof(uint32_t); if (write->rw.packed && write->rw.elems == 3) - out->over_write = sizeof(uint32_t); + chain->over_write = sizeof(uint32_t); /* Make on-stack copy of `ops` to iterate over */ @@ -764,7 +764,9 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) return ret; } - out->cpu_flags = chain->cpu_flags; + out->cpu_flags = chain->cpu_flags; + out->over_read = chain->over_read; + out->over_write = chain->over_write; return 0; } -- 2.52.0 From 96c910fe457863a18b6b14b4035d8bf2807c9a7c Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 12 Mar 2026 21:47:58 +0100 Subject: [PATCH 12/13] swscale/x86/ops: move over_read/write determination to setup func Signed-off-by: Niklas Haas <[email protected]> --- libswscale/x86/ops.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c index 88db0d3e76..92db9cb3af 100644 --- a/libswscale/x86/ops.c +++ b/libswscale/x86/ops.c @@ -58,10 +58,26 @@ REF_PATTERN(NAME, 1, 1, 1, 0), \ REF_PATTERN(NAME, 1, 1, 1, 1) +static int setup_rw(const SwsImplParams *params, SwsImplResult *out) +{ + const SwsOp *op = params->op; + + /* 3-component reads/writes process one extra garbage word */ + if (op->rw.packed && op->rw.elems == 3) { + switch (op->op) { + case SWS_OP_READ: out->over_read = sizeof(uint32_t); break; + case SWS_OP_WRITE: out->over_write = sizeof(uint32_t); break; + } + } + + return 0; +} + #define DECL_RW(EXT, TYPE, NAME, OP, ELEMS, PACKED, FRAC) \ DECL_ASM(TYPE, NAME##ELEMS##EXT, \ .op = SWS_OP_##OP, \ .rw = { .elems = ELEMS, .packed = PACKED, .frac = FRAC }, \ + .setup = setup_rw, \ ); #define DECL_PACKED_RW(EXT, DEPTH) \ @@ -708,13 +724,6 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) .block_size = 2 * FFMIN(mmsize, 32) / ff_sws_op_list_max_size(ops), }; - /* 3-component reads/writes process one extra garbage word */ - if (read && read->rw.packed && read->rw.elems == 3) - chain->over_read = sizeof(uint32_t); - if (write->rw.packed && write->rw.elems == 3) - chain->over_write = sizeof(uint32_t); - - /* Make on-stack copy of `ops` to iterate over */ SwsOpList rest = *ops; do { -- 2.52.0 From 9ca8dc89b94c1ec0bd45089da92599fd2cb2553f Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Thu, 12 Mar 2026 21:49:16 +0100 Subject: [PATCH 13/13] swscale/x86/ops: cosmetic And remove a pointless assertion. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/x86/ops.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c index 92db9cb3af..f4d35ec37b 100644 --- a/libswscale/x86/ops.c +++ b/libswscale/x86/ops.c @@ -696,16 +696,12 @@ static void normalize_clear(SwsOp *op) static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) { + int ret; const int cpu_flags = av_get_cpu_flags(); const int mmsize = get_mmsize(cpu_flags); if (mmsize < 0) return mmsize; - const SwsOp *read = ff_sws_op_list_input(ops); - const SwsOp *write = ff_sws_op_list_output(ops); - av_assert1(write); - int ret; - /* Special fast path for in-place packed shuffle */ ret = solve_shuffle(ops, mmsize, out); if (ret != AVERROR(ENOTSUP)) @@ -759,6 +755,8 @@ static int compile(SwsContext *ctx, SwsOpList *ops, SwsCompiledOp *out) out->func = NAME; \ } while (0) + const SwsOp *read = ff_sws_op_list_input(ops); + const SwsOp *write = ff_sws_op_list_output(ops); const int read_planes = read ? (read->rw.packed ? 1 : read->rw.elems) : 0; const int write_planes = write->rw.packed ? 1 : write->rw.elems; switch (FFMAX(read_planes, write_planes)) { -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
