This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit b97d65aae23e5184fdaf699e427feba96d796e4b
Author:     Niklas Haas <[email protected]>
AuthorDate: Wed Jun 10 17:21:33 2026 +0200
Commit:     Niklas Haas <[email protected]>
CommitDate: Fri Jul 17 13:23:21 2026 +0000

    swscale/uops: rewrite ff_sws_solve_shuffle() in terms of uops
    
    This allows the uop optimizer stage to directly generate RW_SHUFFLE
    uops, which will replace the original ff_sws_solve_shuffle() over the next
    couple of commits.
    
    This way of solving it has a number of additional upsides, including but not
    limited to:
    
    - The ability to move certain low-level optimizations (like expanding
      conversions) out of the higher-level abstraction and have them arise
      purely during uops translation / optimization
    
    - The ability to stop passing SwsOpList to backends at all
    
    Signed-off-by: Niklas Haas <[email protected]>
---
 libswscale/ops_optimizer.c | 147 +++++++++++++++++++++++++++++++++++++++++++++
 libswscale/uops_macros.h   |  48 ++++++++++++++-
 2 files changed, 193 insertions(+), 2 deletions(-)

diff --git a/libswscale/ops_optimizer.c b/libswscale/ops_optimizer.c
index 2c53e8e234..8c0035516e 100644
--- a/libswscale/ops_optimizer.c
+++ b/libswscale/ops_optimizer.c
@@ -972,8 +972,155 @@ int ff_sws_shuffle_mask(const SwsUOp *uop, int8_t 
shuffle[], int size)
     return num_groups;
 }
 
+static bool pixel_is_repeating(SwsPixelType type, SwsPixel val)
+{
+    switch (ff_sws_pixel_type_size(type)) {
+    case 1: return true;
+    case 2: return val.u16 == val.u8 * 0x101ul;
+    case 4: return val.u32 == val.u8 * 0x1010101ul;
+    default: break;
+    }
+
+    av_unreachable("Invalid pixel type!");
+    return false;
+}
+
+static int solve_shuffle(const SwsUOpList *const uops, SwsUOp *out)
+{
+    if (!uops->num_ops)
+        return AVERROR(EINVAL);
+    const SwsUOp *read = &uops->ops[0];
+    switch (read->uop) {
+    case SWS_UOP_READ_PACKED:
+        break;
+    case SWS_UOP_READ_PLANAR:
+        if (read->mask != SWS_COMP_ELEMS(1))
+             return AVERROR(ENOTSUP);
+        break;
+    default:
+        return AVERROR(ENOTSUP);
+    }
+
+    const int read_size = ff_sws_pixel_type_size(read->type);
+    uint32_t mask[4] = {0};
+    int clear_val = -1;
+    int read_elems = 0;
+    for (int i = 0; i < 4; i++) {
+        if (SWS_COMP_TEST(read->mask, i)) {
+            mask[i] = 0x01010101 * i * read_size + 0x03020100;
+            read_elems++;
+        }
+    }
+
+    for (int opidx = 1; opidx < uops->num_ops; opidx++) {
+        const SwsUOp *uop = &uops->ops[opidx];
+        const SwsUOpParams *par = &uop->par;
+        switch (uop->uop) {
+        case SWS_UOP_COPY:
+        case SWS_UOP_PERMUTE: {
+            uint32_t tmp;
+            for (int i = 0; i < par->move.num_moves; i++) {
+                const int dst_idx = par->move.dst[i];
+                const int src_idx = par->move.src[i];
+                uint32_t *src = src_idx < 0 ? &tmp : &mask[src_idx];
+                uint32_t *dst = dst_idx < 0 ? &tmp : &mask[dst_idx];
+                *dst = *src;
+            }
+            break;
+        }
+
+        case SWS_UOP_SWAP_BYTES:
+            for (int i = 0; i < 4; i++) {
+                switch (ff_sws_pixel_type_size(uop->type)) {
+                case 2: mask[i] = av_bswap16(mask[i]); break;
+                case 4: mask[i] = av_bswap32(mask[i]); break;
+                }
+            }
+            break;
+
+        case SWS_UOP_CLEAR:
+            for (int i = 0; i < 4; i++) {
+                if (!SWS_COMP_TEST(uop->mask, i))
+                    continue;
+                SwsPixel val = uop->data.vec4[i];
+                if (!pixel_is_repeating(uop->type, val) ||
+                    (clear_val >= 0 && clear_val != val.u8))
+                    return AVERROR(ENOTSUP); /* would require different bytes 
*/
+                mask[i] = 0xFFFFFFFFul; /* (uint8_t[4]) { -1, -1, -1, -1 } */
+                clear_val = val.u8;
+            }
+            break;
+
+        case SWS_UOP_EXPAND_PAIR:
+        case SWS_UOP_EXPAND_QUAD:
+            for (int i = 0; i < 4; i++)
+                mask[i] = 0x01010101 * (mask[i] & 0xFF);
+            break;
+
+        case SWS_UOP_WRITE_PLANAR:
+            if (uop->mask != SWS_COMP_ELEMS(1))
+                return AVERROR(ENOTSUP);
+            av_fallthrough;
+        case SWS_UOP_WRITE_PACKED: {
+            const int write_elems = av_popcount(uop->mask);
+            const int write_size  = ff_sws_pixel_type_size(uop->type);
+            *out = (SwsUOp) {
+                .uop  = SWS_UOP_RW_SHUFFLE,
+                .type = SWS_PIXEL_U8,
+                .mask = SWS_COMP_ELEMS(1), /* single plane for now */
+            };
+
+            SwsShuffleUOp *par = &out->par.shuffle;
+            SwsShuffleMask *data = &out->data.shuffle;
+            *par = (SwsShuffleUOp) {
+                .read_size   = read_elems * read_size,
+                .write_size  = write_elems * write_size,
+                .clear_value = clear_val >= 0 ? clear_val : 0,
+            };
+
+            /* Generate baseline shuffle for a single pixel */
+            data->pixels = 1;
+            for (int i = 0; i < write_elems; i++) {
+                const int offset = i * write_size;
+                for (int b = 0; b < write_size; b++)
+                    data->mask[offset + b] = mask[i] >> (b * 8);
+            }
+
+            /* Expand as many times as needed to round up to the size of the
+             * shuffle uop data mask */
+            int8_t tmp[FF_ARRAY_ELEMS(data->mask)];
+            const int num_groups = ff_sws_shuffle_mask(out, tmp, sizeof(tmp));
+            if (num_groups < 0)
+                return num_groups;
+            memcpy(data->mask, tmp, sizeof(tmp));
+            par->read_size  *= num_groups;
+            par->write_size *= num_groups;
+            data->pixels = num_groups;
+            return 0;
+        }
+
+        default:
+            return AVERROR(ENOTSUP);
+        }
+    }
+
+    return AVERROR(EINVAL);
+}
+
 int ff_sws_uop_list_optimize(SwsContext *ctx, SwsUOpFlags flags, SwsUOpList 
*uops)
 {
+    /* Try promoting the entire uop list to a packed shuffle operation */
+    if (flags & SWS_UOP_FLAG_PSHUFB) {
+        SwsUOp shuffle;
+        int ret = solve_shuffle(uops, &shuffle);
+        if (ret >= 0) {
+            ff_sws_uop_list_remove_at(uops, 0, uops->num_ops);
+            return ff_sws_uop_list_append(uops, &shuffle);
+        } else if (ret < 0 && ret != AVERROR(ENOTSUP)) {
+            return ret;
+        }
+    }
+
 #if 0
     static const SwsUOp dummy = {0};
 
diff --git a/libswscale/uops_macros.h b/libswscale/uops_macros.h
index 5cf28945e1..6d5c8facf9 100644
--- a/libswscale/uops_macros.h
+++ b/libswscale/uops_macros.h
@@ -105,8 +105,52 @@
     MACRO(__VA_ARGS__, u8_write_bit_x                          , SWS_PIXEL_U8 
, SWS_UOP_WRITE_BIT       , 0x1)
 #define SWS_FOR_STRUCT_U8_WRITE_BIT(MACRO, ...) \
     MACRO(__VA_ARGS__, u8_write_bit_x                          , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_WRITE_BIT       , .mask = 0x1)
-#define SWS_FOR_U8_RW_SHUFFLE(MACRO, ...)
-#define SWS_FOR_STRUCT_U8_RW_SHUFFLE(MACRO, ...)
+#define SWS_FOR_U8_RW_SHUFFLE(MACRO, ...) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_2_12                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 2, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_4_12                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 4, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_4_16                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 4, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_5_15                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 5, 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_6_12                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 6, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_8_12                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 8, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_8_16                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 8, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_10_15                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 10, 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_12_12                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 12, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_12_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 12, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_15_5                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 15, 5) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_15_15                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 15, 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_4                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 16, 4) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_8                  , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 16, 8) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_12                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 16, 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0x0, 16, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_2_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 2, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_4_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 4, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_6_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 6, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_8_16                 , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 8, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_12_16                , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 12, 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_16_16                , SWS_PIXEL_U8 
, SWS_UOP_RW_SHUFFLE      , 0x1, 0xff, 16, 16)
+#define SWS_FOR_STRUCT_U8_RW_SHUFFLE(MACRO, ...) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_2_12                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 2, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_4_12                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 4, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_4_16                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 4, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_5_15                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 5, 
.par.shuffle.write_size = 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_6_12                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 6, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_8_12                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 8, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_8_16                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 8, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_10_15                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 10, 
.par.shuffle.write_size = 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_12_12                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 12, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_12_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 12, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_15_5                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 15, 
.par.shuffle.write_size = 5) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_15_15                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 15, 
.par.shuffle.write_size = 15) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_4                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 16, 
.par.shuffle.write_size = 4) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_8                  , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 16, 
.par.shuffle.write_size = 8) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_12                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 16, 
.par.shuffle.write_size = 12) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_0_16_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0x0, .par.shuffle.read_size = 16, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_2_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 2, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_4_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 4, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_6_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 6, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_8_16                 , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 8, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_12_16                , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 12, 
.par.shuffle.write_size = 16) \
+    MACRO(__VA_ARGS__, u8_rw_shuffle_x_ff_16_16                , .type = 
SWS_PIXEL_U8 , .uop = SWS_UOP_RW_SHUFFLE      , .mask = 0x1, 
.par.shuffle.clear_value = 0xff, .par.shuffle.read_size = 16, 
.par.shuffle.write_size = 16)
 #define SWS_FOR_U8_PERMUTE(MACRO, ...) \
     MACRO(__VA_ARGS__, u8_permute_xy_xy_zw                     , SWS_PIXEL_U8 
, SWS_UOP_PERMUTE         , 0x3, 2, 0, 1, 0, 0, 0, 0, 2, 3, 0, 0, 0, 0) \
     MACRO(__VA_ARGS__, u8_permute_xyz_x_w                      , SWS_PIXEL_U8 
, SWS_UOP_PERMUTE         , 0x7, 1, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0) \

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

Reply via email to