Commit a3d9acfaed for ffmpeg

commit a3d9acfaed39bded9261339304b1cf4f149fdcaa
Author: Niklas Haas <git@haasn.dev>
Date:   Tue Oct 20 20:55:04 2026 +0200

    swscale/ops: switch SwsComps from SoA to AoS

    And name them SwsOpPad instead. This is a purely cosmetic refactor that
    makes some parts of the code easier, since it allows taking pointers to
    specific output pads without having to thread through the index as well.

    Signed-off-by: Niklas Haas <git@haasn.dev>

diff --git a/libswscale/format.c b/libswscale/format.c
index 4f75adcc0f..1167c0446c 100644
--- a/libswscale/format.c
+++ b/libswscale/format.c
@@ -1093,7 +1093,7 @@ static int decode_pixfmt(SwsOpList *ops, const SwsFormat *fmt)
     SwsReadWriteOp rw_op;
     SwsSwizzleOp swizzle;
     SwsPackOp unpack;
-    SwsComps *comps = &ops->comps_src;
+    SwsOpPad *inputs = ops->inputs;
     SwsShiftOp shift;

     RET(fmt_analyze(fmt->format, &rw_op, &unpack, &swizzle, &shift,
@@ -1106,7 +1106,7 @@ static int decode_pixfmt(SwsOpList *ops, const SwsFormat *fmt)
     const int swapped = ff_sws_pixel_type_size(raw_type) > 1 &&
                         (desc->flags & AV_PIX_FMT_FLAG_BE) != NATIVE_ENDIAN_FLAG;
     for (int i = 0; i < rw_op.elems; i++) {
-        comps->flags[i] = (integer ? SWS_COMP_EXACT   : 0) |
+        inputs[i].flags = (integer ? SWS_COMP_EXACT   : 0) |
                           (swapped ? SWS_COMP_SWAPPED : 0);
     }

@@ -1118,8 +1118,8 @@ static int decode_pixfmt(SwsOpList *ops, const SwsFormat *fmt)
         for (int c = 0; c < desc->nb_components; c++) {
             const int bits  = desc->comp[c].depth + shift.amount;
             const int idx   = swizzle.in[is_ya ? 3 * c : c];
-            comps->min[idx] = Q(0);
-            comps->max[idx] = intmax_q64(bits);
+            inputs[idx].min = Q(0);
+            inputs[idx].max = intmax_q64(bits);
         }
     }

diff --git a/libswscale/ops.c b/libswscale/ops.c
index 800a41befa..07159b57d8 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -287,31 +287,34 @@ static SwsCompFlags merge_comp_flags(SwsCompFlags a, SwsCompFlags b)
     return ((a & b) & flags_and) | ((a | b) & flags_or);
 }

-static void apply_filter_weights(SwsComps *comps, const SwsComps *prev,
+static void apply_filter_weights(SwsOpPad outputs[4], const SwsOpPad inputs[4],
                                  const SwsFilterWeights *weights)
 {
     const AVRational64 posw = { weights->sum_positive, SWS_FILTER_SCALE };
     const AVRational64 negw = { weights->sum_negative, SWS_FILTER_SCALE };
     for (int i = 0; i < 4; i++) {
-        comps->flags[i]  = prev->flags[i] & SWS_COMP_DIRTY;
-        comps->dep_in[i] = prev->dep_in[i];
+        outputs[i].flags  = inputs[i].flags & SWS_COMP_DIRTY;
+        outputs[i].dep_in = inputs[i].dep_in;
         /* Only point sampling preserves exactness */
         if (weights->filter_size != 1)
-            comps->flags[i] &= ~SWS_COMP_EXACT;
+            outputs[i].flags &= ~SWS_COMP_EXACT;
         /* Update min/max assuming extremes */
-        comps->min[i] = ff_add_q64(ff_mul_q64(prev->min[i], posw),
-                                   ff_mul_q64(prev->max[i], negw));
-        comps->max[i] = ff_add_q64(ff_mul_q64(prev->min[i], negw),
-                                   ff_mul_q64(prev->max[i], posw));
+        outputs[i].min = ff_add_q64(ff_mul_q64(inputs[i].min, posw),
+                                    ff_mul_q64(inputs[i].max, negw));
+        outputs[i].max = ff_add_q64(ff_mul_q64(inputs[i].min, negw),
+                                    ff_mul_q64(inputs[i].max, posw));
     }
 }

 /* Infer + propagate known information about components */
 void ff_sws_op_list_update_comps(SwsOpList *ops)
 {
-    SwsComps prev = { .flags = {
-        SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE, SWS_COMP_GARBAGE,
-    }};
+    SwsOpPad prev[4] = {
+        { .flags = SWS_COMP_GARBAGE },
+        { .flags = SWS_COMP_GARBAGE },
+        { .flags = SWS_COMP_GARBAGE },
+        { .flags = SWS_COMP_GARBAGE },
+    };

     /* Forwards pass, propagates knowledge about the incoming pixel values */
     for (int n = 0; n < ops->num_ops; n++) {
@@ -326,29 +329,40 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         case SWS_OP_FILTER_V:
         case SWS_OP_LUT_3D:
             break; /* special cases, handled below */
-        default:
-            memcpy(op->comps.min, prev.min, sizeof(prev.min));
-            memcpy(op->comps.max, prev.max, sizeof(prev.max));
-            ff_sws_apply_op_q(op, op->comps.min);
-            ff_sws_apply_op_q(op, op->comps.max);
+        default: {
+            AVRational64 min[4], max[4];
+            for (int i = 0; i < 4; i++) {
+                min[i] = prev[i].min;
+                max[i] = prev[i].max;
+            }
+            ff_sws_apply_op_q(op, min);
+            ff_sws_apply_op_q(op, max);
+            for (int i = 0; i < 4; i++) {
+                op->outputs[i].min = min[i];
+                op->outputs[i].max = max[i];
+            }
             break;
         }
+        }

         for (int i = 0; i < 4; i++) {
-            op->comps.flags[i]  = SWS_COMP_IDENTITY;
-            op->comps.dep_in[i] = SWS_COMP_NONE;
+            op->outputs[i].flags  = SWS_COMP_IDENTITY;
+            op->outputs[i].dep_in = SWS_COMP_NONE;
         }

         #define FORWARD(I, J, EXPR) do {                                        \
-            SwsCompFlags flags = prev.flags[J];                                 \
-            op->comps.flags[I] = merge_comp_flags(op->comps.flags[I], (EXPR));  \
-            op->comps.dep_in[I] |= prev.dep_in[J];                              \
+            SwsOpPad *dst = &op->outputs[I];                                    \
+            const SwsOpPad *src = &prev[J];                                     \
+            SwsCompFlags flags = src->flags;                                    \
+            dst->flags = merge_comp_flags(dst->flags, (EXPR));                  \
+            dst->dep_in |= src->dep_in;                                         \
         } while (0)

         #define RESET(I) do {                                                   \
-            op->comps.flags[I] = SWS_COMP_GARBAGE;                              \
-            op->comps.min[I] = op->comps.max[I] = (AVRational64) {0};           \
-            op->comps.dep_in[I] = SWS_COMP_NONE;                                \
+            SwsOpPad *dst = &op->outputs[I];                                    \
+            dst->flags = SWS_COMP_GARBAGE;                                      \
+            dst->min = dst->max = (AVRational64) {0};                           \
+            dst->dep_in = SWS_COMP_NONE;                                        \
         } while (0)

         switch (op->op) {
@@ -363,11 +377,13 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 case SWS_RW_PLANAR:  idx = ops->plane_src[i]; break;
                 }

-                av_assert0(!(ops->comps_src.flags[idx] & SWS_COMP_GARBAGE));
-                op->comps.flags[i] = ops->comps_src.flags[idx] & SWS_COMP_DIRTY;
-                op->comps.min[i]   = ops->comps_src.min[idx];
-                op->comps.max[i]   = ops->comps_src.max[idx];
-                op->comps.dep_in[i] = SWS_COMP(i);
+                const SwsOpPad *src = &ops->inputs[idx];
+                SwsOpPad *dst = &op->outputs[i];
+                av_assert0(!(src->flags & SWS_COMP_GARBAGE));
+                dst->flags  = src->flags & SWS_COMP_DIRTY;
+                dst->min    = src->min;
+                dst->max    = src->max;
+                dst->dep_in = SWS_COMP(i);

                 /**
                  * Don't mark packed or fractional reads as a copy, because the
@@ -377,24 +393,24 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                  * is already covered by the no-op check.
                  */
                 if (op->rw.mode == SWS_RW_PLANAR && !op->rw.frac)
-                    op->comps.flags[i] |= SWS_COMP_COPY;
+                    dst->flags |= SWS_COMP_COPY;
             }

             if (op->rw.filter.op) {
-                const SwsComps prev = op->comps;
-                apply_filter_weights(&op->comps, &prev, op->rw.filter.kernel);
+                memcpy(prev, op->outputs, sizeof(prev));
+                apply_filter_weights(op->outputs, prev, op->rw.filter.kernel);
             }
             break;
         case SWS_OP_SWAP_BYTES:
             for (int i = 0; i < 4; i++) {
                 FORWARD(i, i, (flags ^ SWS_COMP_SWAPPED) & SWS_COMP_DIRTY);
-                op->comps.min[i] = prev.min[i];
-                op->comps.max[i] = prev.max[i];
+                op->outputs[i].min = prev[i].min;
+                op->outputs[i].max = prev[i].max;
             }
             break;
         case SWS_OP_WRITE:
             for (int i = 0; i < op->rw.elems; i++)
-                av_assert1(!(prev.flags[i] & SWS_COMP_GARBAGE));
+                av_assert1(!(prev[i].flags & SWS_COMP_GARBAGE));
             for (int i = 0; i < 4; i++)
                 FORWARD(i, i, flags);
             break;
@@ -405,27 +421,30 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
             break;
         case SWS_OP_MIN:
         case SWS_OP_MAX: {
-            AVRational64 *bound = op->op == SWS_OP_MIN ? op->comps.max : op->comps.min;
             for (int i = 0; i < 4; i++) {
+                SwsOpPad *dst = &op->outputs[i];
                 FORWARD(i, i, flags);
                 if (op->clamp.limit[i].den)
-                    op->comps.flags[i] &= SWS_COMP_DIRTY;
-                if (!bound[i].den) /* reset undefined bounds to known range */
-                    bound[i] = op->clamp.limit[i];
+                    dst->flags &= SWS_COMP_DIRTY;
+
+                /* Reset undefined bounds to known range */
+                AVRational64 *bound = op->op == SWS_OP_MIN ? &dst->max : &dst->min;
+                if (!bound->den)
+                    *bound = op->clamp.limit[i];
             }
             break;
         }
         case SWS_OP_DITHER:
             for (int i = 0; i < 4; i++) {
                 FORWARD(i, i, flags);
-                op->comps.min[i] = prev.min[i];
-                op->comps.max[i] = prev.max[i];
+                op->outputs[i].min = prev[i].min;
+                op->outputs[i].max = prev[i].max;
                 if (op->dither.y_offset[i] < 0)
                     continue;
                 /* Strip zero flag because of the nonzero dithering offset */
-                op->comps.flags[i] &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
-                op->comps.min[i] = ff_add_q64(op->comps.min[i], op->dither.min);
-                op->comps.max[i] = ff_add_q64(op->comps.max[i], op->dither.max);
+                op->outputs[i].flags &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
+                op->outputs[i].min = ff_add_q64(op->outputs[i].min, op->dither.min);
+                op->outputs[i].max = ff_add_q64(op->outputs[i].max, op->dither.max);
             }
             break;
         case SWS_OP_UNPACK:
@@ -434,8 +453,8 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 if (pattern) {
                     av_assert1(pattern < 32);
                     FORWARD(i, 0, flags & SWS_COMP_DIRTY);
-                    op->comps.min[i] = Q(0);
-                    op->comps.max[i] = Q((1ULL << pattern) - 1);
+                    op->outputs[i].min = Q(0);
+                    op->outputs[i].max = Q((1ULL << pattern) - 1);
                 } else
                     RESET(i);
             }
@@ -451,11 +470,11 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         case SWS_OP_CLEAR:
             for (int i = 0; i < 4; i++) {
                 if (SWS_COMP_TEST(op->clear.mask, i)) {
-                    op->comps.flags[i] = SWS_COMP_CONST;
+                    op->outputs[i].flags = SWS_COMP_CONST;
                     if (op->clear.value[i].num == 0)
-                        op->comps.flags[i] |= SWS_COMP_ZERO;
+                        op->outputs[i].flags |= SWS_COMP_ZERO;
                     if (op->clear.value[i].den == 1)
-                        op->comps.flags[i] |= SWS_COMP_EXACT;
+                        op->outputs[i].flags |= SWS_COMP_EXACT;
                 } else {
                     FORWARD(i, i, flags);
                 }
@@ -468,56 +487,56 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         case SWS_OP_CONVERT:
             for (int i = 0; i < 4; i++) {
                 FORWARD(i, i, flags);
-                if (!(prev.flags[i] & SWS_COMP_EXACT))
-                    op->comps.flags[i] &= SWS_COMP_DIRTY;
+                if (!(prev[i].flags & SWS_COMP_EXACT))
+                    op->outputs[i].flags &= SWS_COMP_DIRTY;
                 if (ff_sws_pixel_type_is_int(op->convert.to))
-                    op->comps.flags[i] |= SWS_COMP_EXACT;
+                    op->outputs[i].flags |= SWS_COMP_EXACT;
             }
             break;
         case SWS_OP_LINEAR:
             for (int i = 0; i < 4; i++) {
-                AVRational64 min = Q(0), max = Q(0);
+                SwsOpPad *dst = &op->outputs[i];
                 bool first = true;
+                dst->min = dst->max = Q(0);
                 for (int j = 0; j < 4; j++) {
+                    const SwsOpPad *src = &prev[j];
                     const AVRational64 k = op->lin.m[i][j];
-                    AVRational64 mink = ff_mul_q64(prev.min[j], k);
-                    AVRational64 maxk = ff_mul_q64(prev.max[j], k);
+                    AVRational64 mink = ff_mul_q64(src->min, k);
+                    AVRational64 maxk = ff_mul_q64(src->max, k);
                     if (k.num) {
                         FORWARD(i, j, flags);
                         if (k.den != 1) /* fractional coefficient */
-                            op->comps.flags[i] &= ~SWS_COMP_EXACT;
+                            dst->flags &= ~SWS_COMP_EXACT;
                         if (k.num < 0)
                             FFSWAP(AVRational64, mink, maxk);
-                        min = ff_add_q64(min, mink);
-                        max = ff_add_q64(max, maxk);
+                        dst->min = ff_add_q64(dst->min, mink);
+                        dst->max = ff_add_q64(dst->max, maxk);
                         if (!first || ff_cmp_q64(k, Q(1)))
-                            op->comps.flags[i] &= SWS_COMP_DIRTY;
+                            dst->flags &= SWS_COMP_DIRTY;
                         first = false;
                     }
                 }
                 if (op->lin.m[i][4].num) { /* nonzero offset */
-                    op->comps.flags[i] &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
+                    dst->flags &= ~SWS_COMP_ZERO & SWS_COMP_DIRTY;
                     if (op->lin.m[i][4].den != 1) /* fractional offset */
-                        op->comps.flags[i] &= ~SWS_COMP_EXACT;
-                    min = ff_add_q64(min, op->lin.m[i][4]);
-                    max = ff_add_q64(max, op->lin.m[i][4]);
+                        dst->flags &= ~SWS_COMP_EXACT;
+                    dst->min = ff_add_q64(dst->min, op->lin.m[i][4]);
+                    dst->max = ff_add_q64(dst->max, op->lin.m[i][4]);
                 }
-                op->comps.min[i] = min;
-                op->comps.max[i] = max;
             }
             break;
         case SWS_OP_SCALE:
             for (int i = 0; i < 4; i++) {
                 FORWARD(i, i, flags & SWS_COMP_DIRTY);
                 if (op->scale.factor.den != 1) /* fractional scale */
-                    op->comps.flags[i] &= ~SWS_COMP_EXACT;
+                    op->outputs[i].flags &= ~SWS_COMP_EXACT;
                 if (op->scale.factor.num < 0)
-                    FFSWAP(AVRational64, op->comps.min[i], op->comps.max[i]);
+                    FFSWAP(AVRational64, op->outputs[i].min, op->outputs[i].max);
             }
             break;
         case SWS_OP_FILTER_H:
         case SWS_OP_FILTER_V: {
-            apply_filter_weights(&op->comps, &prev, op->filter.kernel);
+            apply_filter_weights(op->outputs, prev, op->filter.kernel);
             break;
         }
         case SWS_OP_LUT_3D:
@@ -527,20 +546,20 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
                 for (int j = 0; j < 3; j++)
                     FORWARD(i, j, flags & SWS_COMP_GARBAGE);
                 /* LUT output domain is always scaled to full 16-bit range */
-                op->comps.min[i] = Q(0);
-                op->comps.max[i] = Q(UINT16_MAX);
+                op->outputs[i].min = Q(0);
+                op->outputs[i].max = Q(UINT16_MAX);
             }
             /* Pass through alpha channel untouched */
             FORWARD(3, 3, flags);
-            op->comps.min[3] = prev.min[3];
-            op->comps.max[3] = prev.max[3];
+            op->outputs[3].min = prev[3].min;
+            op->outputs[3].max = prev[3].max;
             break;
         case SWS_OP_INVALID:
         case SWS_OP_TYPE_NB:
             av_unreachable("Invalid operation type!");
         }

-        prev = op->comps;
+        memcpy(prev, op->outputs, sizeof(prev));
     }

     /* Backwards pass, solves for output component dependencies */
@@ -551,7 +570,7 @@ void ff_sws_op_list_update_comps(SwsOpList *ops)
         SwsCompMask need_in[4] = {0};

         for (int i = 0; i < 4; i++) {
-            op->comps.dep_out[i] = need_out[i];
+            op->outputs[i].dep_out = need_out[i];
             if (!need_out[i])
                 RESET(i);
         }
@@ -1018,29 +1037,28 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra,
         av_log(log, lev, "%s\n", bp->str);

         for (int i = 0; i < 4; i++) {
+            const SwsOpPad *pad = &op->outputs[i];
             if (!SWS_COMP_TEST(mask, i))
                 continue;
             av_bprint_clear(bp);
             av_bprintf(bp, "    %c: %3s,", "XYZW"[i], ff_sws_pixel_type_name(op_result_type(op)));
-            print_deps(bp, op->comps.dep_in[i]);
+            print_deps(bp, pad->dep_in);
             av_bprintf(bp, " ->");
-            print_deps(bp, op->comps.dep_out[i]);
-            av_bprintf(bp, " %7s", describe_comp_flags(op->comps.flags[i]));
+            print_deps(bp, pad->dep_out);
+            av_bprintf(bp, " %7s", describe_comp_flags(pad->flags));

-            const AVRational64 min = op->comps.min[i];
-            const AVRational64 max = op->comps.max[i];
-            if (min.den || max.den) {
-                if (op->comps.flags[i] & SWS_COMP_ZERO) {
+            if (pad->min.den || pad->max.den) {
+                if (pad->flags & SWS_COMP_ZERO) {
                     /* don't print any value */
-                } else if (op->comps.flags[i] & SWS_COMP_CONST) {
+                } else if (pad->flags & SWS_COMP_CONST) {
                     av_bprintf(bp, " = ");
-                    av_assert1(ff_cmp_q64(min, max) == 0);
-                    print_q(bp, min);
+                    av_assert1(ff_cmp_q64(pad->min, pad->max) == 0);
+                    print_q(bp, pad->min);
                 } else {
                     av_bprintf(bp, " in [");
-                    print_q(bp, min);
+                    print_q(bp, pad->min);
                     av_bprintf(bp, ", ");
-                    print_q(bp, max);
+                    print_q(bp, pad->max);
                     av_bprintf(bp, "]");
                 }
             }
diff --git a/libswscale/ops.h b/libswscale/ops.h
index a6dc28621a..35263b9d74 100644
--- a/libswscale/ops.h
+++ b/libswscale/ops.h
@@ -83,16 +83,16 @@ typedef enum SwsCompFlags {
     SWS_COMP_CONST   = 1 << 5, /* value is a fixed constant */
 } SwsCompFlags;

-typedef struct SwsComps {
-    SwsCompFlags flags[4]; /* knowledge about (output) component contents */
+typedef struct SwsOpPad {
+    SwsCompFlags flags; /* knowledge about (output) component contents */

     /* Keeps track of the known possible value range, or {0, 0} for undefined
      * or (unknown range) floating point inputs */
-    AVRational64 min[4], max[4];
+    AVRational64 min, max;

     /* Keeps track of input (forward) and output (reverse) dependencies */
-    SwsCompMask dep_in[4], dep_out[4];
-} SwsComps;
+    SwsCompMask dep_in, dep_out;
+} SwsOpPad;

 typedef enum SwsReadWriteMode {
     /**
@@ -263,10 +263,10 @@ typedef struct SwsOp {
      * Note that backends may rely on the presence and accuracy of this
      * metadata for all operations, during ff_sws_ops_compile().
      */
-    SwsComps comps;
+    SwsOpPad outputs[4];
 } SwsOp;

-#define SWS_OP_NEEDED(op, idx) (!((op)->comps.flags[idx] & SWS_COMP_GARBAGE))
+#define SWS_OP_NEEDED(op, idx) (!((op)->outputs[idx].flags & SWS_COMP_GARBAGE))

 /* Compute SwsCompMask from a mask of needed components */
 SwsCompMask ff_sws_comp_mask_needed(const SwsOp *op);
@@ -313,7 +313,7 @@ typedef struct SwsOpList {
      * The default value of {0} is safe to pass in the case that no additional
      * information is known.
      */
-    SwsComps comps_src;
+    SwsOpPad inputs[4];
 } SwsOpList;

 SwsOpList *ff_sws_op_list_alloc(void);
diff --git a/libswscale/ops_dispatch.c b/libswscale/ops_dispatch.c
index a0d049f616..3177f4b667 100644
--- a/libswscale/ops_dispatch.c
+++ b/libswscale/ops_dispatch.c
@@ -482,12 +482,12 @@ static void op_list_get_plane_copy(const SwsOpList *ops, SwsPass *pass)
     const SwsOp *prev = &ops->ops[ops->num_ops - 2];
     int *plane_copy = pass->output->plane_copy;
     for (int i = 0; i < write->rw.elems; i++) {
-        SwsCompFlags flags = prev->comps.flags[i];
+        SwsCompFlags flags = prev->outputs[i].flags;
         if (!(flags & SWS_COMP_COPY))
             continue;

         const int out_idx = ops->plane_dst[i];
-        switch (prev->comps.dep_in[i]) {
+        switch (prev->outputs[i].dep_in) {
         case SWS_COMP(0): plane_copy[out_idx] = ops->plane_src[0]; break;
         case SWS_COMP(1): plane_copy[out_idx] = ops->plane_src[1]; break;
         case SWS_COMP(2): plane_copy[out_idx] = ops->plane_src[2]; break;
@@ -707,7 +707,7 @@ static SwsCompMask plane_mask_flags(const SwsOp *op, SwsCompFlags flags)
 {
     SwsCompMask planes = 0;
     for (int c = 0; c < 4; c++) {
-        if (op->comps.flags[c] & flags)
+        if (op->outputs[c].flags & flags)
             planes |= SWS_COMP(c);
     }

diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c
index 77e6d01682..bd1723f053 100644
--- a/libswscale/ops_optimizer.c
+++ b/libswscale/ops_optimizer.c
@@ -235,7 +235,7 @@ static int exact_log2_q64(const AVRational64 x)
  * the corresponding scaling factor, or 0 otherwise.
  */
 static bool extract_scalar(const SwsLinearOp *c,
-                           const SwsComps *comps, const SwsComps *prev,
+                           const SwsOpPad outputs[4], const SwsOpPad inputs[4],
                            SwsScaleOp *out_scale)
 {
     SwsScaleOp scale = {0};
@@ -246,8 +246,8 @@ static bool extract_scalar(const SwsLinearOp *c,

     for (int i = 0; i < 4; i++) {
         const AVRational64 s = c->m[i][i];
-        if ((prev->flags[i]  & SWS_COMP_ZERO) ||
-            (comps->flags[i] & SWS_COMP_GARBAGE))
+        if ((inputs[i].flags  & SWS_COMP_ZERO) ||
+            (outputs[i].flags & SWS_COMP_GARBAGE))
             continue;
         if (scale.factor.den && ff_cmp_q64(s, scale.factor))
             return false;
@@ -260,7 +260,7 @@ static bool extract_scalar(const SwsLinearOp *c,
 }

 /* Extracts an integer clear operation (subset) from the given linear op. */
-static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
+static bool extract_constant_rows(SwsLinearOp *c, const SwsOpPad inputs[4],
                                   SwsClearOp *out_clear)
 {
     const uint32_t mask = ff_sws_linear_mask(c);
@@ -271,7 +271,7 @@ static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,
         bool const_row = c->m[i][4].den == 1; /* offset is integer */
         for (int j = 0; j < 4; j++) {
             const_row &= c->m[i][j].num == 0 || /* scalar is zero */
-                         (prev->flags[j] & SWS_COMP_ZERO); /* input is zero */
+                         (inputs[j].flags & SWS_COMP_ZERO); /* input is zero */
         }
         if (const_row && (mask & SWS_MASK_ROW(i))) {
             clear.mask |= SWS_COMP(i);
@@ -289,7 +289,7 @@ static bool extract_constant_rows(SwsLinearOp *c, const SwsComps *prev,

 /* Unswizzle a linear operation by aligning single-input rows with
  * their corresponding diagonal */
-static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
+static bool extract_swizzle(SwsLinearOp *op, const SwsOpPad inputs[4],
                             SwsSwizzleOp *out_swiz)
 {
     SwsSwizzleOp swiz = SWS_SWIZZLE(0, 1, 2, 3);
@@ -299,7 +299,7 @@ static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
     uint32_t nonzero = 0;
     for (int i = 0; i < 4; i++) {
         for (int j = 0; j < 4; j++) {
-            if (!c.m[i][j].num || (prev->flags[j] & SWS_COMP_ZERO))
+            if (!c.m[i][j].num || (inputs[j].flags & SWS_COMP_ZERO))
                 continue;
             nonzero |= SWS_MASK(i, j);
         }
@@ -332,7 +332,7 @@ static bool extract_swizzle(SwsLinearOp *op, const SwsComps *prev,
 static int op_result_is_exact(const SwsOp *op)
 {
     for (int i = 0; i < 4; i++) {
-        if (SWS_OP_NEEDED(op, i) && !(op->comps.flags[i] & SWS_COMP_EXACT))
+        if (SWS_OP_NEEDED(op, i) && !(op->outputs[i].flags & SWS_COMP_EXACT))
             return false;
     }

@@ -464,8 +464,8 @@ retry:
                 if (!SWS_COMP_TEST(op->clear.mask, i))
                     continue;

-                if ((prev->comps.flags[i] & SWS_COMP_ZERO) &&
-                    !(prev->comps.flags[i] & SWS_COMP_GARBAGE) &&
+                if ((prev->outputs[i].flags & SWS_COMP_ZERO) &&
+                    !(prev->outputs[i].flags & SWS_COMP_GARBAGE) &&
                     op->clear.value[i].num == 0)
                 {
                     /* Redundant clear-to-zero of zero component */
@@ -585,7 +585,7 @@ retry:
             for (int i = 0; i < 4; i++) {
                 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
                     continue;
-                if (ff_cmp_q64(op->clamp.limit[i], prev->comps.max[i]) >= 0)
+                if (ff_cmp_q64(op->clamp.limit[i], prev->outputs[i].max) >= 0)
                     op->clamp.limit[i] = (AVRational64) {0}; /* no-op */
                 else
                     noop = false;
@@ -601,7 +601,7 @@ retry:
             for (int i = 0; i < 4; i++) {
                 if (!SWS_OP_NEEDED(op, i) || !op->clamp.limit[i].den)
                     continue;
-                if (ff_cmp_q64(prev->comps.min[i], op->clamp.limit[i]) >= 0)
+                if (ff_cmp_q64(prev->outputs[i].min, op->clamp.limit[i]) >= 0)
                     op->clamp.limit[i] = (AVRational64) {0};
                 else
                     noop = false;
@@ -617,7 +617,7 @@ retry:
             for (int i = 0; i < 4; i++) {
                 if (op->dither.y_offset[i] < 0)
                     continue;
-                if (!SWS_OP_NEEDED(op, i) || (prev->comps.flags[i] & SWS_COMP_EXACT)) {
+                if (!SWS_OP_NEEDED(op, i) || (prev->outputs[i].flags & SWS_COMP_EXACT)) {
                     op->dither.y_offset[i] = -1; /* unnecessary dither */
                     goto retry;
                 } else {
@@ -664,7 +664,7 @@ retry:
             /* Optimize away zero columns */
             for (int j = 0; j < 4; j++) {
                 const uint32_t col = SWS_MASK_COL(j);
-                if (!(prev->comps.flags[j] & SWS_COMP_ZERO) || !(mask & col))
+                if (!(prev->outputs[j].flags & SWS_COMP_ZERO) || !(mask & col))
                     continue;
                 for (int i = 0; i < 4; i++)
                     op->lin.m[i][j] = Q(i == j);
@@ -682,7 +682,7 @@ retry:
             }

             /* Convert constant rows to explicit clear instruction */
-            if (extract_constant_rows(&op->lin, &prev->comps, &clear)) {
+            if (extract_constant_rows(&op->lin, prev->outputs, &clear)) {
                 RET(ff_sws_op_list_insert_at(ops, n + 1, &(SwsOp) {
                     .op    = SWS_OP_CLEAR,
                     .type  = op->type,
@@ -692,14 +692,14 @@ retry:
             }

             /* Multiplication by scalar constant */
-            if (extract_scalar(&op->lin, &op->comps, &prev->comps, &scale)) {
+            if (extract_scalar(&op->lin, op->outputs, prev->outputs, &scale)) {
                 op->op    = SWS_OP_SCALE;
                 op->scale = scale;
                 goto retry;
             }

             /* Swizzle by fixed pattern */
-            if (extract_swizzle(&op->lin, &prev->comps, &swizzle)) {
+            if (extract_swizzle(&op->lin, prev->outputs, &swizzle)) {
                 RET(ff_sws_op_list_insert_at(ops, n, &(SwsOp) {
                     .op      = SWS_OP_SWIZZLE,
                     .type    = op->type,
@@ -1203,9 +1203,9 @@ int ff_sws_op_list_split_at(SwsOpList *ops1, SwsOpList **out_ops2, int index)
     for (int i = 0; i < nb_planes; i++) {
         const int idx = swiz_wr.in[i];
         ops1->plane_dst[i] = ops2->plane_src[i] = i;
-        ops2->comps_src.flags[i]  = prev->comps.flags[idx];
-        ops2->comps_src.min[i]    = prev->comps.min[idx];
-        ops2->comps_src.max[i]    = prev->comps.max[idx];
+        ops2->inputs[i].flags  = prev->outputs[idx].flags;
+        ops2->inputs[i].min    = prev->outputs[idx].min;
+        ops2->inputs[i].max    = prev->outputs[idx].max;
     }

     ff_sws_op_list_remove_at(ops1, index, ops1->num_ops - index);
diff --git a/libswscale/uops.c b/libswscale/uops.c
index 21396daf70..8d6ad36692 100644
--- a/libswscale/uops.c
+++ b/libswscale/uops.c
@@ -228,18 +228,15 @@ static bool exact_product_f32(float a, float b)
     return !b || result == a;
 }

-static bool exact_prod(SwsPixelType type, SwsPixel coef,
-                       const SwsComps *comps, int idx)
+static bool exact_prod(SwsPixelType type, SwsPixel coef, const SwsOpPad *input)
 {
-    const AVRational64 minq = comps->min[idx];
-    const AVRational64 maxq = comps->max[idx];
     if (ff_sws_pixel_type_is_int(type))
         return true;
-    else if (!minq.den || !maxq.den)
+    else if (!input->min.den || !input->max.den)
         return false; /* unknown bounds */

-    const SwsPixel min = ff_sws_pixel_from_q64(type, minq);
-    const SwsPixel max = ff_sws_pixel_from_q64(type, maxq);
+    const SwsPixel min = ff_sws_pixel_from_q64(type, input->min);
+    const SwsPixel max = ff_sws_pixel_from_q64(type, input->max);
     switch (type) {
     case SWS_PIXEL_F32:
         return exact_product_f32(coef.f32, min.f32) &&
@@ -476,7 +473,7 @@ static int translate_dither_op(SwsUOpList *ops, SwsUOpFlags flags, const SwsOp *

 static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
                                SwsUOpFlags flags, const SwsOp *op,
-                               const SwsComps *input)
+                               const SwsOpPad inputs[4])
 {
     SwsUOp uop = {
         .type = op->type,
@@ -502,7 +499,7 @@ static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
                 uop.par.lin.zero |= SWS_MASK(i, j);
             else if (j < 4 && k.num == k.den)
                 uop.par.lin.one |= SWS_MASK(i, j);
-            else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, input, j)))
+            else if (j < 4 && nonzero && (!bitexact || exact_prod(uop.type, px, &inputs[j])))
                 exact |= SWS_MASK(i, j);
             if (k.num != 0)
                 nonzero = true;
@@ -519,7 +516,7 @@ static int translate_linear_op(SwsContext *ctx, SwsUOpList *ops,
 }

 static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
-                        const SwsOp *op, const SwsComps *input)
+                        const SwsOp *op, const SwsOpPad inputs[4])
 {
     switch (op->op) {
     case SWS_OP_FILTER_H:
@@ -533,7 +530,7 @@ static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
     case SWS_OP_DITHER:
         return translate_dither_op(uops, flags, op);
     case SWS_OP_LINEAR:
-        return translate_linear_op(ctx, uops, flags, op, input);
+        return translate_linear_op(ctx, uops, flags, op, inputs);
     default:
         break;
     }
@@ -618,17 +615,17 @@ static int translate_op(SwsContext *ctx, SwsUOpList *uops, SwsUOpFlags flags,
 int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops,
                          SwsUOpFlags flags, SwsUOpList *uops)
 {
-    SwsComps input = ops->comps_src;
+    const SwsOpPad *inputs = ops->inputs;
     for (int i = 0; i < ops->num_ops; i++) {
         const SwsOp *op = &ops->ops[i];
         const int pixel_size = ff_sws_pixel_type_size(op->type);
         if (pixel_size > uops->pixel_size_max)
             uops->pixel_size_max = pixel_size;

-        int ret = translate_op(ctx, uops, flags, op, &input);
+        int ret = translate_op(ctx, uops, flags, op, inputs);
         if (ret < 0)
             return ret;
-        input = ops->ops[i].comps;
+        inputs = op->outputs;
     }

     return ff_sws_uop_list_optimize(ctx, flags, uops);