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]
