PR #22652 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22652 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22652.patch
Switches to marking unused outputs as SWS_COMP_GARBAGE and checking for that instead of `next->comps.unused[]`. >From 959f2387787ebe77c8fd5dc2a64d6d2eccc9cd87 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 14:57:40 +0100 Subject: [PATCH 01/10] swscale/ops: simplify SwsOpList.order_src/dst Just define these directly as integer arrays; there's really no point in having them re-use SwsSwizzleOp; the only place this was ever even remotely relevant was in the no-op check, which any decent compiler should already be capable of optimizing into a single 32-bit comparison. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 32 +++++++++++++++++++++----------- libswscale/ops.h | 6 +++--- libswscale/ops_dispatch.c | 4 ++-- libswscale/ops_optimizer.c | 12 ++++++------ libswscale/vulkan/ops.c | 8 ++++---- 5 files changed, 36 insertions(+), 26 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 88bd289158..7976baeabd 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -323,7 +323,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) /* Active components are taken from the user-provided values, * other components are explicitly stripped */ for (int i = 0; i < op->rw.elems; i++) { - const int idx = op->rw.packed ? i : ops->order_src.in[i]; + const int idx = op->rw.packed ? i : ops->plane_src[i]; op->comps.flags[i] = ops->comps_src.flags[idx]; op->comps.min[i] = ops->comps_src.min[idx]; op->comps.max[i] = ops->comps_src.max[idx]; @@ -566,7 +566,8 @@ SwsOpList *ff_sws_op_list_alloc(void) if (!ops) return NULL; - ops->order_src = ops->order_dst = SWS_SWIZZLE(0, 1, 2, 3); + for (int i = 0; i < 4; i++) + ops->plane_src[i] = ops->plane_dst[i] = i; ff_fmt_clear(&ops->src); ff_fmt_clear(&ops->dst); return ops; @@ -693,7 +694,7 @@ bool ff_sws_op_list_is_noop(const SwsOpList *ops) */ const int num_planes = read->rw.packed ? 1 : read->rw.elems; for (int i = 0; i < num_planes; i++) { - if (ops->order_src.in[i] != ops->order_dst.in[i]) + if (ops->plane_src[i] != ops->plane_dst[i]) return false; } @@ -899,6 +900,20 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op, const bool unused[4]) } } +static void desc_plane_order(AVBPrint *bp, int nb_planes, const uint8_t *order) +{ + bool inorder = true; + for (int i = 0; i < nb_planes; i++) + inorder &= order[i] == i; + if (inorder) + return; + + av_bprintf(bp, ", via {"); + for (int i = 0; i < nb_planes; i++) + av_bprintf(bp, "%s%d", i ? ", " : "", order[i]); + av_bprintf(bp, "}"); +} + void ff_sws_op_list_print(void *log, int lev, int lev_extra, const SwsOpList *ops) { @@ -928,14 +943,9 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, ff_sws_op_desc(&bp, op, next->comps.unused); if (op->op == SWS_OP_READ || op->op == SWS_OP_WRITE) { - SwsSwizzleOp order = op->op == SWS_OP_READ ? ops->order_src : ops->order_dst; - if (order.mask != SWS_SWIZZLE(0, 1, 2, 3).mask) { - const int planes = op->rw.packed ? 1 : op->rw.elems; - av_bprintf(&bp, ", via {"); - for (int i = 0; i < planes; i++) - av_bprintf(&bp, "%s%d", i ? ", " : "", order.in[i]); - av_bprintf(&bp, "}"); - } + const int planes = op->rw.packed ? 1 : op->rw.elems; + desc_plane_order(&bp, planes, + op->op == SWS_OP_READ ? ops->plane_src : ops->plane_dst); } av_assert0(av_bprint_is_complete(&bp)); diff --git a/libswscale/ops.h b/libswscale/ops.h index 827ec04094..a6bbceced4 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -255,13 +255,13 @@ typedef struct SwsOpList { /* Metadata associated with this operation list */ SwsFormat src, dst; - /* Input/output plane pointer swizzle mask */ - SwsSwizzleOp order_src, order_dst; + /* Input/output plane indices */ + uint8_t plane_src[4], plane_dst[4]; /** * Source component metadata associated with pixel values from each * corresponding component (in plane/memory order, i.e. not affected by - * `order_src`). Lets the optimizer know additional information about + * `plane_src`). Lets the optimizer know additional information about * the value range and/or pixel data to expect. * * The default value of {0} is safe to pass in the case that no additional diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c index c1893f3db8..f842196265 100644 --- a/libswscale/ops_dispatch.c +++ b/libswscale/ops_dispatch.c @@ -418,8 +418,8 @@ static int compile(SwsGraph *graph, const SwsOpList *ops, SwsPass *input, }; for (int i = 0; i < 4; i++) { - p->idx_in[i] = i < p->planes_in ? ops->order_src.in[i] : -1; - p->idx_out[i] = i < p->planes_out ? ops->order_dst.in[i] : -1; + p->idx_in[i] = i < p->planes_in ? ops->plane_src[i] : -1; + p->idx_out[i] = i < p->planes_out ? ops->plane_dst[i] : -1; } const SwsFilterWeights *filter = read->rw.kernel; diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c index 542df8e956..c364d3efc6 100644 --- a/libswscale/ops_optimizer.c +++ b/libswscale/ops_optimizer.c @@ -385,7 +385,7 @@ retry: const int idx = nb_planes++; av_assert1(idx <= i); - ops->order_src.in[idx] = ops->order_src.in[i]; + ops->plane_src[idx] = ops->plane_src[i]; swiz.in[i] = idx; } @@ -501,7 +501,7 @@ retry: for (int dst = 0; dst < prev->rw.elems; dst++) { const int src = op->swizzle.in[dst]; if (src > dst && src < prev->rw.elems) { - FFSWAP(int, ops->order_src.in[dst], ops->order_src.in[src]); + FFSWAP(int, ops->plane_src[dst], ops->plane_src[src]); for (int i = dst; i < 4; i++) { if (op->swizzle.in[i] == dst) op->swizzle.in[i] = src; @@ -517,7 +517,7 @@ retry: for (int dst = 0; dst < next->rw.elems; dst++) { const int src = op->swizzle.in[dst]; if (src > dst && src < next->rw.elems) { - FFSWAP(int, ops->order_dst.in[dst], ops->order_dst.in[src]); + FFSWAP(int, ops->plane_dst[dst], ops->plane_dst[src]); FFSWAP(int, op->swizzle.in[dst], op->swizzle.in[src]); goto retry; } @@ -963,15 +963,15 @@ int ff_sws_op_list_subpass(SwsOpList *ops1, SwsOpList **out_rest) /* Determine metadata for the intermediate format */ const SwsPixelType type = op->type; - ops2->order_src = SWS_SWIZZLE(0, 1, 2, 3); ops2->comps_src = prev->comps; ops2->src.format = get_planar_fmt(type, nb_planes); ops2->src.desc = av_pix_fmt_desc_get(ops2->src.format); get_input_size(ops1, &ops2->src); - - ops1->order_dst = SWS_SWIZZLE(0, 1, 2, 3); ops1->dst = ops2->src; + for (int i = 0; i < nb_planes; i++) + ops1->plane_dst[i] = ops2->plane_src[i] = i; + ff_sws_op_list_remove_at(ops1, idx, ops1->num_ops - idx); ff_sws_op_list_remove_at(ops2, 0, idx); op = NULL; /* the above command may invalidate op */ diff --git a/libswscale/vulkan/ops.c b/libswscale/vulkan/ops.c index f04a0800d2..4d684116ca 100644 --- a/libswscale/vulkan/ops.c +++ b/libswscale/vulkan/ops.c @@ -245,11 +245,11 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s, return AVERROR(ENOTSUP); } else if (op->rw.packed) { GLSLF(1, %s = %s(imageLoad(src_img[%i], pos)); , - type_name, type_v, ops->order_src.in[0]); + 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->order_src.in[i]); + type_name, "xyzw"[i], type_s, ops->plane_src[i]); } break; } @@ -258,11 +258,11 @@ static int add_ops_glsl(VulkanPriv *p, FFVulkanOpsCtx *s, return AVERROR(ENOTSUP); } else if (op->rw.packed) { GLSLF(1, imageStore(dst_img[%i], pos, %s(%s)); , - ops->order_dst.in[0], type_v, type_name); + ops->plane_dst[0], type_v, type_name); } else { for (int i = 0; i < op->rw.elems; i++) GLSLF(1, imageStore(dst_img[%i], pos, %s(%s[%i])); , - ops->order_dst.in[i], type_v, type_name, i); + ops->plane_dst[i], type_v, type_name, i); } break; } -- 2.52.0 >From bc6c2f49df9352045a89e74de7a96b3fc07cb4d9 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 16:04:24 +0100 Subject: [PATCH 02/10] swscale/ops: slightly refactor unused[] computation Needed for the upcoming removal of op->comps.unused[]. This keeps the dependency array entirely within the ff_sws_op_list_update_comps() function, apart from being arguably simpler and easier to follow. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 56 ++++++++++++++++++------------------------------ 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 7976baeabd..09e97c8120 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -293,7 +293,6 @@ static void apply_filter_weights(SwsComps *comps, const SwsComps *prev, /* Infer + propagate known information about components */ void ff_sws_op_list_update_comps(SwsOpList *ops) { - SwsComps next = { .unused = {true, true, true, true} }; SwsComps prev = { .flags = { SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, }}; @@ -468,16 +467,18 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) } /* Backwards pass, solves for component dependencies */ + bool need_out[4] = { false, false, false, false }; for (int n = ops->num_ops - 1; n >= 0; n--) { SwsOp *op = &ops->ops[n]; + bool need_in[4] = { false, false, false, false }; switch (op->op) { case SWS_OP_READ: case SWS_OP_WRITE: for (int i = 0; i < op->rw.elems; i++) - op->comps.unused[i] = op->op == SWS_OP_READ; + need_in[i] = op->op == SWS_OP_WRITE; for (int i = op->rw.elems; i < 4; i++) - op->comps.unused[i] = next.unused[i]; + need_in[i] = need_out[i]; break; case SWS_OP_SWAP_BYTES: case SWS_OP_LSHIFT: @@ -490,55 +491,40 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) case SWS_OP_FILTER_H: case SWS_OP_FILTER_V: for (int i = 0; i < 4; i++) - op->comps.unused[i] = next.unused[i]; + need_in[i] = need_out[i]; break; - case SWS_OP_UNPACK: { - bool unused = true; - for (int i = 0; i < 4; i++) { - if (op->pack.pattern[i]) - unused &= next.unused[i]; - op->comps.unused[i] = i > 0; - } - op->comps.unused[0] = unused; + case SWS_OP_UNPACK: + for (int i = 0; i < 4 && op->pack.pattern[i]; i++) + need_in[0] |= need_out[i]; break; - } case SWS_OP_PACK: - for (int i = 0; i < 4; i++) { - if (op->pack.pattern[i]) - op->comps.unused[i] = next.unused[0]; - else - op->comps.unused[i] = true; - } + for (int i = 0; i < 4 && op->pack.pattern[i]; i++) + need_in[i] = need_out[0]; break; case SWS_OP_CLEAR: for (int i = 0; i < 4; i++) { - if (op->c.q4[i].den) - op->comps.unused[i] = true; - else - op->comps.unused[i] = next.unused[i]; + if (!op->c.q4[i].den) + need_in[i] = need_out[i]; } break; - case SWS_OP_SWIZZLE: { - bool unused[4] = { true, true, true, true }; + case SWS_OP_SWIZZLE: for (int i = 0; i < 4; i++) - unused[op->swizzle.in[i]] &= next.unused[i]; - for (int i = 0; i < 4; i++) - op->comps.unused[i] = unused[i]; + need_in[op->swizzle.in[i]] |= need_out[i]; break; - } case SWS_OP_LINEAR: - for (int j = 0; j < 4; j++) { - bool unused = true; - for (int i = 0; i < 4; i++) { + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { if (op->lin.m[i][j].num) - unused &= next.unused[i]; + need_in[j] |= need_out[i]; } - op->comps.unused[j] = unused; } break; } - next = op->comps; + for (int i = 0; i < 4; i++) { + need_out[i] = need_in[i]; + op->comps.unused[i] = !need_in[i]; + } } } -- 2.52.0 >From 4cefc1673a5651cbbb56d98f18b9e9dfa8961e7c Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 16:24:51 +0100 Subject: [PATCH 03/10] swscale/ops: mark all unused components as GARBAGE This only affects the print-out of the SWS_OP_WRITE at the end of every op, list, because the ops list print-out was otherwise already checking the unused mask. rgb24 -> bgr24: [ u8 XXXX -> +++X] SWS_OP_READ : 3 elem(s) packed >> 0 min: {0 0 0 _}, max: {255 255 255 _} [ u8 ...X -> +++X] SWS_OP_SWIZZLE : 2103 min: {0 0 0 _}, max: {255 255 255 _} - [ u8 ...X -> +++X] SWS_OP_WRITE : 3 elem(s) packed >> 0 + [ u8 ...X -> XXXX] SWS_OP_WRITE : 3 elem(s) packed >> 0 min: {0 0 0 _}, max: {255 255 255 _} (X = unused, z = byteswapped, + = exact, 0 = zero) Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 5 +++++ tests/ref/fate/sws-ops-list | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 09e97c8120..1220f70c42 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -472,6 +472,11 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) SwsOp *op = &ops->ops[n]; bool need_in[4] = { false, false, false, false }; + for (int i = 0; i < 4; i++) { + if (!need_out[i]) + op->comps.flags[i] = SWS_COMP_GARBAGE; + } + switch (op->op) { case SWS_OP_READ: case SWS_OP_WRITE: diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list index 4385d809e7..cafa68665d 100644 --- a/tests/ref/fate/sws-ops-list +++ b/tests/ref/fate/sws-ops-list @@ -1 +1 @@ -e5a6788fa43852d75544e7fb6ae7744d +da5cdcd09fffb274b454a64f1d95b073 -- 2.52.0 >From 08951549d8ec7e13fd71e658fbc9e72f93dd3a76 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 16:31:58 +0100 Subject: [PATCH 04/10] swscale/ops: strip value range from garbage components Just removes the unnecessary value range after the WRITE, as a result of the previous change and the fact that we already skipped printing these for unused components. rgb24 -> bgr24: [ u8 XXXX -> +++X] SWS_OP_READ : 3 elem(s) packed >> 0 min: {0 0 0 _}, max: {255 255 255 _} [ u8 ...X -> +++X] SWS_OP_SWIZZLE : 2103 min: {0 0 0 _}, max: {255 255 255 _} [ u8 ...X -> XXXX] SWS_OP_WRITE : 3 elem(s) packed >> 0 - min: {0 0 0 _}, max: {255 255 255 _} (X = unused, z = byteswapped, + = exact, 0 = zero) Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 4 +++- tests/ref/fate/sws-ops-list | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 1220f70c42..56fd1f81ec 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -473,8 +473,10 @@ void ff_sws_op_list_update_comps(SwsOpList *ops) bool need_in[4] = { false, false, false, false }; for (int i = 0; i < 4; i++) { - if (!need_out[i]) + if (!need_out[i]) { op->comps.flags[i] = SWS_COMP_GARBAGE; + op->comps.min[i] = op->comps.max[i] = (AVRational) {0}; + } } switch (op->op) { diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list index cafa68665d..48b038b6e5 100644 --- a/tests/ref/fate/sws-ops-list +++ b/tests/ref/fate/sws-ops-list @@ -1 +1 @@ -da5cdcd09fffb274b454a64f1d95b073 +3fcc132b8a56c71f099055ce4ff410f6 -- 2.52.0 >From 27105d0178d654edf71706e83670ebad88f2ea33 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 16:33:38 +0100 Subject: [PATCH 05/10] swscale/ops: test for SWS_COMP_GARBAGE instead of next->comps.unused When printing/describing operations. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 18 +++++++++--------- libswscale/ops.h | 2 +- libswscale/tests/sws_ops.c | 3 +-- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 56fd1f81ec..93d6350864 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -788,13 +788,13 @@ static void print_q(AVBPrint *bp, const AVRational q, bool ignore_den0) } static void print_q4(AVBPrint *bp, const AVRational q4[4], bool ignore_den0, - const bool unused[4]) + const SwsCompFlags flags[4]) { av_bprintf(bp, "{"); for (int i = 0; i < 4; i++) { if (i) av_bprintf(bp, " "); - if (unused && unused[i]) { + if (flags[i] & SWS_COMP_GARBAGE) { av_bprintf(bp, "_"); } else { print_q(bp, q4[i], ignore_den0); @@ -803,7 +803,7 @@ static void print_q4(AVBPrint *bp, const AVRational q4[4], bool ignore_den0, av_bprintf(bp, "}"); } -void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op, const bool unused[4]) +void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) { const char *name = ff_sws_op_type_name(op->op); @@ -838,7 +838,7 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op, const bool unused[4]) break; case SWS_OP_CLEAR: av_bprintf(bp, "%-20s: ", name); - print_q4(bp, op->c.q4, true, unused); + print_q4(bp, op->c.q4, true, op->comps.flags); break; case SWS_OP_SWIZZLE: av_bprintf(bp, "%-20s: %d%d%d%d", name, @@ -858,11 +858,11 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op, const bool unused[4]) break; case SWS_OP_MIN: av_bprintf(bp, "%-20s: x <= ", name); - print_q4(bp, op->c.q4, true, unused); + print_q4(bp, op->c.q4, true, op->comps.flags); break; case SWS_OP_MAX: av_bprintf(bp, "%-20s: ", name); - print_q4(bp, op->c.q4, true, unused); + print_q4(bp, op->c.q4, true, op->comps.flags); av_bprintf(bp, " <= x"); break; case SWS_OP_LINEAR: @@ -933,7 +933,7 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, next->comps.unused[2] ? 'X' : describe_comp_flags(op->comps.flags[2]), next->comps.unused[3] ? 'X' : describe_comp_flags(op->comps.flags[3])); - ff_sws_op_desc(&bp, op, next->comps.unused); + ff_sws_op_desc(&bp, op); if (op->op == SWS_OP_READ || op->op == SWS_OP_WRITE) { const int planes = op->rw.packed ? 1 : op->rw.elems; @@ -951,9 +951,9 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, { av_bprint_clear(&bp); av_bprintf(&bp, " min: "); - print_q4(&bp, op->comps.min, false, next->comps.unused); + print_q4(&bp, op->comps.min, false, op->comps.flags); av_bprintf(&bp, ", max: "); - print_q4(&bp, op->comps.max, false, next->comps.unused); + print_q4(&bp, op->comps.max, false, op->comps.flags); av_assert0(av_bprint_is_complete(&bp)); av_log(log, lev_extra, "%s\n", bp.str); } diff --git a/libswscale/ops.h b/libswscale/ops.h index a6bbceced4..abeaf97d27 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -233,7 +233,7 @@ typedef struct SwsOp { /** * Describe an operation in human-readable form. */ -void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op, const bool unused[4]); +void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op); /** * Frees any allocations associated with an SwsOp and sets it to {0}. diff --git a/libswscale/tests/sws_ops.c b/libswscale/tests/sws_ops.c index 91b3d8c4b7..02049156e7 100644 --- a/libswscale/tests/sws_ops.c +++ b/libswscale/tests/sws_ops.c @@ -50,7 +50,6 @@ static int cmp_str(const void *a, const void *b) return strcmp(a, b); } -static const bool unused[4] = { false, false, false, false }; static int register_op(SwsContext *ctx, void *opaque, SwsOp *op) { struct AVTreeNode **root = opaque; @@ -80,7 +79,7 @@ static int register_op(SwsContext *ctx, void *opaque, SwsOp *op) } av_bprint_init(&bp, 0, AV_BPRINT_SIZE_AUTOMATIC); - ff_sws_op_desc(&bp, op, unused); + ff_sws_op_desc(&bp, op); int ret = av_bprint_finalize(&bp, &desc); if (ret < 0) return ret; -- 2.52.0 >From e042a5ac8c68970bc3e300fee44d8dccde13faba Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 17:07:33 +0100 Subject: [PATCH 06/10] swscale/ops: remove redundant unused mask from ops printout This is now fully redundant with the previous op's output; because unused components are always marked as garbage on the input side. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 6 +----- tests/ref/fate/sws-ops-list | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 93d6350864..d6c9b16bd0 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -922,12 +922,8 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, const SwsOp *op = &ops->ops[i]; const SwsOp *next = i + 1 < ops->num_ops ? &ops->ops[i + 1] : op; av_bprint_clear(&bp); - av_bprintf(&bp, " [%3s %c%c%c%c -> %c%c%c%c] ", + av_bprintf(&bp, " [%3s %c%c%c%c] ", ff_sws_pixel_type_name(op->type), - op->comps.unused[0] ? 'X' : '.', - op->comps.unused[1] ? 'X' : '.', - op->comps.unused[2] ? 'X' : '.', - op->comps.unused[3] ? 'X' : '.', next->comps.unused[0] ? 'X' : describe_comp_flags(op->comps.flags[0]), next->comps.unused[1] ? 'X' : describe_comp_flags(op->comps.flags[1]), next->comps.unused[2] ? 'X' : describe_comp_flags(op->comps.flags[2]), diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list index 48b038b6e5..159dbbba2a 100644 --- a/tests/ref/fate/sws-ops-list +++ b/tests/ref/fate/sws-ops-list @@ -1 +1 @@ -3fcc132b8a56c71f099055ce4ff410f6 +a872c7d917c532ba92397c1410f04388 -- 2.52.0 >From b5b42ad59353eed420f49bd856bc23c4d29daf68 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 17:08:48 +0100 Subject: [PATCH 07/10] swscale/ops: remove unneeded conditional on describe_comp_flags next->comps.unused[] is redundant with SWS_COMP_GARBAGE now. Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index d6c9b16bd0..0a2a991833 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -920,14 +920,13 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, for (int i = 0; i < ops->num_ops; i++) { const SwsOp *op = &ops->ops[i]; - const SwsOp *next = i + 1 < ops->num_ops ? &ops->ops[i + 1] : op; av_bprint_clear(&bp); av_bprintf(&bp, " [%3s %c%c%c%c] ", ff_sws_pixel_type_name(op->type), - next->comps.unused[0] ? 'X' : describe_comp_flags(op->comps.flags[0]), - next->comps.unused[1] ? 'X' : describe_comp_flags(op->comps.flags[1]), - next->comps.unused[2] ? 'X' : describe_comp_flags(op->comps.flags[2]), - next->comps.unused[3] ? 'X' : describe_comp_flags(op->comps.flags[3])); + describe_comp_flags(op->comps.flags[0]), + describe_comp_flags(op->comps.flags[1]), + describe_comp_flags(op->comps.flags[2]), + describe_comp_flags(op->comps.flags[3])); ff_sws_op_desc(&bp, op); -- 2.52.0 >From 388d8e3a32ef9e951518fa1d7a6e32fa58b3eb75 Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 17:49:46 +0100 Subject: [PATCH 08/10] swscale/ops: only print SWS_OP_SCALE denom if not 1 Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.c | 4 +++- tests/ref/fate/sws-ops-list | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/libswscale/ops.c b/libswscale/ops.c index 0a2a991833..d86d8b0239 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -878,7 +878,9 @@ void ff_sws_op_desc(AVBPrint *bp, const SwsOp *op) av_bprintf(bp, "]"); break; case SWS_OP_SCALE: - av_bprintf(bp, "%-20s: * %d/%d", name, op->c.q.num, op->c.q.den); + av_bprintf(bp, "%-20s: * %d", name, op->c.q.num); + if (op->c.q.den != 1) + av_bprintf(bp, "/%d", op->c.q.den); break; case SWS_OP_FILTER_H: case SWS_OP_FILTER_V: { diff --git a/tests/ref/fate/sws-ops-list b/tests/ref/fate/sws-ops-list index 159dbbba2a..f24c95f388 100644 --- a/tests/ref/fate/sws-ops-list +++ b/tests/ref/fate/sws-ops-list @@ -1 +1 @@ -a872c7d917c532ba92397c1410f04388 +a2ed0581163448a2c398ee9992e8eaf6 -- 2.52.0 >From 75bdfacc61877963d6d8fbeb86e47df09bc33f8f Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 17:16:24 +0100 Subject: [PATCH 09/10] swscale/ops_optimizer: check COMP_GARBAGE instead of next->comps.unused Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops.h | 2 ++ libswscale/ops_optimizer.c | 34 ++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/libswscale/ops.h b/libswscale/ops.h index abeaf97d27..e3acbd4b18 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -85,6 +85,8 @@ typedef enum SwsCompFlags { SWS_COMP_SWAPPED = 1 << 3, /* byte order is swapped */ } SwsCompFlags; +#define SWS_OP_NEEDED(op, idx) (!((op)->comps.flags[idx] & SWS_COMP_GARBAGE)) + typedef union SwsConst { /* Generic constant value */ AVRational q4[4]; diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c index c364d3efc6..fd3c1ef969 100644 --- a/libswscale/ops_optimizer.c +++ b/libswscale/ops_optimizer.c @@ -126,7 +126,7 @@ static bool op_commute_swizzle(SwsOp *op, SwsOp *next) case SWS_OP_MAX: { const SwsConst c = next->c; for (int i = 0; i < 4; i++) { - if (next->comps.unused[i]) + if (!SWS_OP_NEEDED(op, i)) continue; const int j = op->swizzle.in[i]; if (seen[j] && av_cmp_q(next->c.q4[j], c.q4[i])) @@ -140,7 +140,7 @@ static bool op_commute_swizzle(SwsOp *op, SwsOp *next) case SWS_OP_DITHER: { const SwsDitherOp d = next->dither; for (int i = 0; i < 4; i++) { - if (next->comps.unused[i]) + if (!SWS_OP_NEEDED(op, i)) continue; const int j = op->swizzle.in[i]; if (seen[j] && next->dither.y_offset[j] != d.y_offset[i]) @@ -237,7 +237,7 @@ static int exact_log2_q(const AVRational x) * If a linear operation can be reduced to a scalar multiplication, returns * the corresponding scaling factor, or 0 otherwise. */ -static bool extract_scalar(const SwsLinearOp *c, SwsComps prev, SwsComps next, +static bool extract_scalar(const SwsLinearOp *c, SwsComps comps, SwsComps prev, SwsConst *out_scale) { SwsConst scale = {0}; @@ -248,7 +248,8 @@ static bool extract_scalar(const SwsLinearOp *c, SwsComps prev, SwsComps next, for (int i = 0; i < 4; i++) { const AVRational s = c->m[i][i]; - if ((prev.flags[i] & SWS_COMP_ZERO) || next.unused[i]) + if ((prev.flags[i] & SWS_COMP_ZERO) || + (comps.flags[i] & SWS_COMP_GARBAGE)) continue; if (scale.q.den && av_cmp_q(s, scale.q)) return false; @@ -363,10 +364,11 @@ retry: /* common helper variable */ bool noop = true; - if (next->comps.unused[0] && next->comps.unused[1] && - next->comps.unused[2] && next->comps.unused[3]) + if (!SWS_OP_NEEDED(op, 0) && !SWS_OP_NEEDED(op, 1) && + !SWS_OP_NEEDED(op, 2) && !SWS_OP_NEEDED(op, 3) && + op->op != SWS_OP_WRITE) { - /* Remove completely unused operations */ + /* Remove any operation whose output is not needed */ ff_sws_op_list_remove_at(ops, n, 1); goto retry; } @@ -378,7 +380,7 @@ retry: SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3); int nb_planes = 0; for (int i = 0; i < op->rw.elems; i++) { - if (next->comps.unused[i]) { + if (!SWS_OP_NEEDED(op, i)) { swiz.in[i] = 3 - (i - nb_planes); /* map to unused plane */ continue; } @@ -449,7 +451,7 @@ retry: { /* Redundant clear-to-zero of zero component */ op->c.q4[i].den = 0; - } else if (next->comps.unused[i]) { + } else if (!SWS_OP_NEEDED(op, i)) { /* Unnecessary clear of unused component */ op->c.q4[i] = (AVRational) {0, 0}; } else if (op->c.q4[i].den) { @@ -475,7 +477,7 @@ retry: case SWS_OP_SWIZZLE: for (int i = 0; i < 4; i++) { - if (next->comps.unused[i]) + if (!SWS_OP_NEEDED(op, i)) continue; if (op->swizzle.in[i] != i) noop = false; @@ -556,7 +558,7 @@ retry: case SWS_OP_MIN: for (int i = 0; i < 4; i++) { - if (next->comps.unused[i] || !op->c.q4[i].den) + if (!SWS_OP_NEEDED(op, i) || !op->c.q4[i].den) continue; if (av_cmp_q(op->c.q4[i], prev->comps.max[i]) < 0) noop = false; @@ -570,7 +572,7 @@ retry: case SWS_OP_MAX: for (int i = 0; i < 4; i++) { - if (next->comps.unused[i] || !op->c.q4[i].den) + if (!SWS_OP_NEEDED(op, i) || !op->c.q4[i].den) continue; if (av_cmp_q(prev->comps.min[i], op->c.q4[i]) < 0) noop = false; @@ -586,7 +588,7 @@ retry: for (int i = 0; i < 4; i++) { if (op->dither.y_offset[i] < 0) continue; - if (next->comps.unused[i] || (prev->comps.flags[i] & SWS_COMP_EXACT)) { + if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) { op->dither.y_offset[i] = -1; /* unnecessary dither */ goto retry; } else { @@ -643,7 +645,7 @@ retry: /* Optimize away unused rows */ for (int i = 0; i < 4; i++) { const uint32_t row = SWS_MASK_ROW(i); - if (!next->comps.unused[i] || !(op->lin.mask & row)) + if (SWS_OP_NEEDED(op, i) || !(op->lin.mask & row)) continue; for (int j = 0; j < 5; j++) op->lin.m[i][j] = Q(i == j); @@ -663,7 +665,7 @@ retry: } /* Multiplication by scalar constant */ - if (extract_scalar(&op->lin, prev->comps, next->comps, &c)) { + if (extract_scalar(&op->lin, op->comps, prev->comps, &c)) { op->op = SWS_OP_SCALE; op->c = c; goto retry; @@ -954,7 +956,7 @@ int ff_sws_op_list_subpass(SwsOpList *ops1, SwsOpList **out_rest) SwsSwizzleOp swiz_wr = SWS_SWIZZLE(0, 1, 2, 3); SwsSwizzleOp swiz_rd = SWS_SWIZZLE(0, 1, 2, 3); for (int i = 0; i < 4; i++) { - if (!op->comps.unused[i]) { + if (SWS_OP_NEEDED(prev, i)) { const int o = nb_planes++; swiz_wr.in[o] = i; swiz_rd.in[i] = o; -- 2.52.0 >From 5c77815bc0cb4e9832a644c9444d1a94370c99cd Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Sat, 28 Mar 2026 20:19:17 +0100 Subject: [PATCH 10/10] swscale/ops_chain: check SWS_COMP_GARBAGE instead of next->comps.unused Signed-off-by: Niklas Haas <[email protected]> --- libswscale/ops_chain.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index 092133426e..7a5706a525 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -75,11 +75,11 @@ int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, * For unfiltered SWS_OP_READ/SWS_OP_WRITE, SWS_OP_SWAP_BYTES and * SWS_OP_SWIZZLE, the exact type is not checked, just the size. * - * Components set in `next.unused` are ignored when matching. If `flexible` + * Components marked SWS_COMP_GARBAGE are ignored when matching. If `flexible` * is true, the op body is ignored - only the operation, pixel type, and * component masks are checked. */ -static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps next) +static int op_match(const SwsOp *op, const SwsOpEntry *entry) { int score = 10; if (op->op != entry->op) @@ -115,7 +115,7 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps nex if (op->op == SWS_OP_CLEAR) { /* Clear pattern must match exactly, regardless of `entry->flexible` */ for (int i = 0; i < 4; i++) { - if (!next.unused[i] && entry->unused[i] != !!op->c.q4[i].den) + if (SWS_OP_NEEDED(op, i) && entry->unused[i] != !!op->c.q4[i].den) return 0; } } @@ -147,9 +147,9 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps nex return score; case SWS_OP_CLEAR: for (int i = 0; i < 4; i++) { - if (!op->c.q4[i].den) + if (!op->c.q4[i].den || !SWS_OP_NEEDED(op, i)) continue; - if (av_cmp_q(op->c.q4[i], Q(entry->clear_value)) && !next.unused[i]) + if (av_cmp_q(op->c.q4[i], Q(entry->clear_value))) return 0; } return score; @@ -159,7 +159,7 @@ static int op_match(const SwsOp *op, const SwsOpEntry *entry, const SwsComps nex break; case SWS_OP_SWIZZLE: for (int i = 0; i < 4; i++) { - if (op->swizzle.in[i] != entry->swizzle.in[i] && !next.unused[i]) + if (SWS_OP_NEEDED(op, i) && op->swizzle.in[i] != entry->swizzle.in[i]) return 0; } return score; @@ -204,8 +204,6 @@ 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 }}; - 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; @@ -226,7 +224,7 @@ int ff_sws_op_compile_tables(SwsContext *ctx, const SwsOpTable *const tables[], params.table = table; for (int i = 0; table->entries[i]; i++) { const SwsOpEntry *entry = table->entries[i]; - int score = op_match(op, entry, next->comps); + int score = op_match(op, entry); if (score <= best_score) continue; if (entry->check && !entry->check(¶ms)) -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
