PR #23590 opened by mkver URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23590 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23590.patch
>From 83d53ebb24a720d0471d66f2d79d47d0c7787817 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <[email protected]> Date: Wed, 24 Jun 2026 16:05:20 +0200 Subject: [PATCH 1/4] swscale/graph: Remove ff_sws_graph_create() Unused since 9fe0ff3d56618c24d95fa4e832148bab54928f18. Signed-off-by: Andreas Rheinhardt <[email protected]> --- doc/swscale-v2.txt | 5 +++-- libswscale/graph.c | 17 ----------------- libswscale/graph.h | 6 ------ 3 files changed, 3 insertions(+), 25 deletions(-) diff --git a/doc/swscale-v2.txt b/doc/swscale-v2.txt index aa2edceca7..a8bc71f426 100644 --- a/doc/swscale-v2.txt +++ b/doc/swscale-v2.txt @@ -13,8 +13,9 @@ threaded error diffusion pass following a multi-threaded scaling pass. SwsGraph is internally recreated whenever the image format, dimensions or settings change in any way. sws_scale_frame() is itself just a light-weight -wrapper that runs ff_sws_graph_create() whenever the format changes, splits -interlaced images into separate fields, and calls ff_sws_graph_run() on each. +wrapper that runs ff_sws_graph_reinit() initially and on format changes, +splits interlaced images into separate fields, and calls ff_sws_graph_run() +on each. From the point of view of SwsGraph itself, all inputs are progressive. diff --git a/libswscale/graph.c b/libswscale/graph.c index 019f9207fb..b9d5df3b48 100644 --- a/libswscale/graph.c +++ b/libswscale/graph.c @@ -880,23 +880,6 @@ error: return ret; } -int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src, - SwsGraph **out_graph) -{ - SwsGraph *graph = ff_sws_graph_alloc(); - if (!graph) - return AVERROR(ENOMEM); - - int ret = ff_sws_graph_init(graph, ctx, dst, src); - if (ret < 0) { - ff_sws_graph_free(&graph); - return ret; - } - - *out_graph = graph; - return 0; -} - void ff_sws_graph_rollback(SwsGraph *graph, int since_idx) { for (int i = since_idx; i < graph->num_passes; i++) diff --git a/libswscale/graph.h b/libswscale/graph.h index e467a43e45..68c6a0b9eb 100644 --- a/libswscale/graph.h +++ b/libswscale/graph.h @@ -167,12 +167,6 @@ SwsGraph *ff_sws_graph_alloc(void); int ff_sws_graph_init(SwsGraph *graph, SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src); -/** - * Allocate and initialize the filter graph. Returns 0 or a negative error. - */ -int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src, - SwsGraph **out_graph); - /** * Allocate and add a new pass to the filter graph. Takes over ownership of -- 2.52.0 >From eeb99c0b9491d7221179ea82b5c174eea374ee00 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <[email protected]> Date: Wed, 24 Jun 2026 16:10:48 +0200 Subject: [PATCH 2/4] swscale/ops_chain: Disable ff_sws_setup_{clear,clamp,scale} They are only used on aarch64. Signed-off-by: Andreas Rheinhardt <[email protected]> --- libswscale/ops_chain.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c index c3884a67ad..72b239172b 100644 --- a/libswscale/ops_chain.c +++ b/libswscale/ops_chain.c @@ -62,6 +62,7 @@ int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, #define q2pixel(type, q) ((q).den ? (type) (q).num / (q).den : 0) +#if ARCH_AARCH64 int ff_sws_setup_scale(const SwsImplParams *params, SwsImplResult *out) { const SwsOp *op = params->op; @@ -112,6 +113,7 @@ int ff_sws_setup_clear(const SwsImplParams *params, SwsImplResult *out) return 0; } +#endif int ff_sws_uop_lookup(SwsContext *ctx, const SwsUOpTable *const tables[], int num_tables, const SwsUOp *uop, const int block_size, -- 2.52.0 >From efff2c4ff0c8df445a9be18d68fdb5719a35122d Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <[email protected]> Date: Wed, 24 Jun 2026 16:15:24 +0200 Subject: [PATCH 3/4] swscale/uops: Move test-only stuff to sws_ops test Signed-off-by: Andreas Rheinhardt <[email protected]> --- libswscale/tests/sws_ops.c | 370 +++++++++++++++++++++++++++++++++- libswscale/uops.c | 394 +------------------------------------ libswscale/uops.h | 12 -- libswscale/uops_list.h | 60 ++++++ 4 files changed, 433 insertions(+), 403 deletions(-) create mode 100644 libswscale/uops_list.h diff --git a/libswscale/tests/sws_ops.c b/libswscale/tests/sws_ops.c index 0da59b473f..e2d01e76cf 100644 --- a/libswscale/tests/sws_ops.c +++ b/libswscale/tests/sws_ops.c @@ -20,9 +20,11 @@ #include "libavutil/mem.h" #include "libavutil/pixdesc.h" +#include "libavutil/tree.h" #include "libswscale/ops.h" #include "libswscale/ops_dispatch.h" #include "libswscale/ops_internal.h" +#include "libswscale/uops_list.h" #include "libswscale/format.h" #ifdef _WIN32 @@ -32,6 +34,372 @@ static int pass_idx; +static const struct { + char full[16]; + char prefix[8]; +} pixel_types[SWS_PIXEL_TYPE_NB] = { + [SWS_PIXEL_NONE] = { "SWS_PIXEL_NONE", "" }, + [SWS_PIXEL_U8] = { "SWS_PIXEL_U8", "U8_" }, + [SWS_PIXEL_U16] = { "SWS_PIXEL_U16", "U16_" }, + [SWS_PIXEL_U32] = { "SWS_PIXEL_U32", "U32_" }, + [SWS_PIXEL_F32] = { "SWS_PIXEL_F32", "F32_" }, +}; + +static const struct { + char full[32]; + char abbr[32]; + char macro[32]; +} uop_names[SWS_UOP_TYPE_NB] = { +#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { "SWS_UOP_" #OP, ABBR, #OP }, + UOPS_LIST(UOP_NAME) +}; + +static int generate_entry_struct(void *opaque, void *key) +{ + const SwsUOp *ref = opaque; + const SwsUOp *uop = key; + AVBPrint *bp = ref->data.opaque; + char name[SWS_UOP_NAME_MAX]; + ff_sws_uop_name(uop, name); + av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s", name); + av_bprintf(bp, ", .type = %-13s, .uop = %-24s, .mask = 0x%x", + pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask); + + const SwsUOpParams *par = &uop->par; + switch (uop->uop) { + case SWS_UOP_READ_PLANAR_FH: + case SWS_UOP_READ_PLANAR_FV: + case SWS_UOP_READ_PLANAR_FV_FMA: + av_bprintf(bp, ", .par.filter.type = %s", pixel_types[par->filter.type].full); + break; + case SWS_UOP_LSHIFT: + case SWS_UOP_RSHIFT: + av_bprintf(bp, ", .par.shift.amount = %u", par->shift.amount); + break; + case SWS_UOP_PERMUTE: + case SWS_UOP_COPY: + av_bprintf(bp, ", .par.swizzle.in = {%d, %d, %d, %d}", + par->swizzle.in[0], par->swizzle.in[1], + par->swizzle.in[2], par->swizzle.in[3]); + break; + case SWS_UOP_MOVE: + av_bprintf(bp, ", .par.move.num_moves = %d", par->move.num_moves); + av_bprintf(bp, ", .par.move.dst = {%d, %d, %d, %d, %d, %d}", + par->move.dst[0], par->move.dst[1], par->move.dst[2], + par->move.dst[3], par->move.dst[4], par->move.dst[5]); + av_bprintf(bp, ", .par.move.src = {%d, %d, %d, %d, %d, %d}", + par->move.src[0], par->move.src[1], par->move.src[2], + par->move.src[3], par->move.src[4], par->move.src[5]); + break; + case SWS_UOP_PACK: + case SWS_UOP_UNPACK: + av_bprintf(bp, ", .par.pack.pattern = {%d, %d, %d, %d}", + par->pack.pattern[0], par->pack.pattern[1], + par->pack.pattern[2], par->pack.pattern[3]); + break; + case SWS_UOP_CLEAR: + av_bprintf(bp, ", .par.clear.one = 0x%x, .par.clear.zero = 0x%x", + par->clear.one, par->clear.zero); + break; + case SWS_UOP_LINEAR: + case SWS_UOP_LINEAR_FMA: + av_bprintf(bp, ", .par.lin.one = 0x%x, .par.lin.zero = 0x%x", + par->lin.one, par->lin.zero); + if (uop->uop == SWS_UOP_LINEAR_FMA) + av_bprintf(bp, ", .par.lin.exact = 0x%x", par->lin.exact); + break; + case SWS_UOP_DITHER: + av_bprintf(bp, ", .par.dither = { .y_offset = {%u, %u, %u, %u}, .size_log2 = %u }", + par->dither.y_offset[0], par->dither.y_offset[1], + par->dither.y_offset[2], par->dither.y_offset[3], + par->dither.size_log2); + break; + } + + av_bprintf(bp, ")"); + return 0; +} + +static int generate_entry_args(void *opaque, void *key) +{ + const SwsUOp *ref = opaque; + const SwsUOp *uop = key; + AVBPrint *bp = ref->data.opaque; + char name[SWS_UOP_NAME_MAX]; + ff_sws_uop_name(uop, name); + av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s, %-13s, %-24s, 0x%x", + name, pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask); + + const SwsUOpParams *par = &uop->par; + switch (uop->uop) { + case SWS_UOP_READ_PLANAR_FH: + case SWS_UOP_READ_PLANAR_FV: + case SWS_UOP_READ_PLANAR_FV_FMA: + av_bprintf(bp, ", %s", pixel_types[par->filter.type].full); + break; + case SWS_UOP_LSHIFT: + case SWS_UOP_RSHIFT: + av_bprintf(bp, ", %u", par->shift.amount); + break; + case SWS_UOP_PERMUTE: + case SWS_UOP_COPY: + av_bprintf(bp, ", %d, %d, %d, %d", + par->swizzle.in[0], par->swizzle.in[1], + par->swizzle.in[2], par->swizzle.in[3]); + break; + case SWS_UOP_MOVE: + av_bprintf(bp, ", %d", par->move.num_moves); + av_bprintf(bp, ", %d, %d, %d, %d, %d, %d", + par->move.dst[0], par->move.dst[1], par->move.dst[2], + par->move.dst[3], par->move.dst[4], par->move.dst[5]); + av_bprintf(bp, ", %d, %d, %d, %d, %d, %d", + par->move.src[0], par->move.src[1], par->move.src[2], + par->move.src[3], par->move.src[4], par->move.src[5]); + break; + case SWS_UOP_PACK: + case SWS_UOP_UNPACK: + av_bprintf(bp, ", %d, %d, %d, %d", + par->pack.pattern[0], par->pack.pattern[1], + par->pack.pattern[2], par->pack.pattern[3]); + break; + case SWS_UOP_CLEAR: + av_bprintf(bp, ", 0x%05x, 0x%05x", par->clear.one, par->clear.zero); + break; + case SWS_UOP_LINEAR: + case SWS_UOP_LINEAR_FMA: + av_bprintf(bp, ", 0x%05x, 0x%05x", par->lin.one, par->lin.zero); + if (uop->uop == SWS_UOP_LINEAR_FMA) + av_bprintf(bp, ", 0x%05x", par->lin.exact); + break; + case SWS_UOP_DITHER: + av_bprintf(bp, ", %u, %u, %u, %u, %u", + par->dither.y_offset[0], par->dither.y_offset[1], + par->dither.y_offset[2], par->dither.y_offset[3], + par->dither.size_log2); + break; + } + + av_bprintf(bp, ")"); + return 0; +} + +static int register_uop(struct AVTreeNode **root, const SwsUOp *uop) +{ + SwsUOp *key = av_memdup(uop, sizeof(*uop)); + if (!key) + return AVERROR(ENOMEM); + memset(&key->data, 0, sizeof(key->data)); + + struct AVTreeNode *node = av_tree_node_alloc(); + if (!node) { + av_free(key); + return AVERROR(ENOMEM); + } + + av_tree_insert(root, key, ff_sws_uop_cmp_v, &node); + if (node) { + av_free(node); + av_free(key); + } + return 0; +} + +static int register_flags(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags flags) +{ + SwsUOpList *uops = ff_sws_uop_list_alloc(); + if (!uops) + return AVERROR(ENOMEM); + + int ret = ff_sws_ops_translate(ctx, ops, flags, uops); + if (ret < 0) + goto fail; + + struct AVTreeNode **root = ctx->opaque; + for (int i = 0; i < uops->num_ops; i++) { + ret = register_uop(root, &uops->ops[i]); + if (ret < 0) + goto fail; + } + +fail: + ff_sws_uop_list_free(&uops); + return ret; +} + +static const SwsUOpFlags uop_flags[] = { + 0, + SWS_UOP_FLAG_FMA | SWS_UOP_FLAG_MOVE, /* x86 backend */ +}; + +static int register_uops(SwsContext *ctx, const SwsOpList *ops, + SwsCompiledOp *out) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(uop_flags); i++) { + int ret = register_flags(ctx, ops, uop_flags[i]); + if (ret < 0) + return ret; + } + + *out = (SwsCompiledOp) {0}; /* dummy value, will be immediately freed */ + return 0; +} + +/* Dummy backend that just registers all seen uops */ +static const SwsOpBackend backend_uops = { + .name = "uops_gen", + .compile = register_uops, +}; + +static int register_all_uops(SwsContext *ctx, void *graph, SwsOpList *ops) +{ + /* ff_sws_compile_pass() takes over ownership of `ops` */ + SwsOpList *copy = ff_sws_op_list_duplicate(ops); + if (!copy) + return AVERROR(ENOMEM); + + const int flags = SWS_OP_FLAG_DRY_RUN | SWS_OP_FLAG_SPLIT_MEMCPY; + return ff_sws_compile_pass(graph, &backend_uops, ©, flags, NULL, NULL); +} + +static const SwsFlags flags_list[] = { + 0, + SWS_ACCURATE_RND, /* may insert extra 1x1 dither ops (for accurate rounding) */ + SWS_BITEXACT, /* prevents some FMA optimizations */ + SWS_ACCURATE_RND | SWS_BITEXACT, +}; + +/* Limit the range of av_tree_enumerate() to only matching uop and type */ +static int enum_type(void *opaque, void *elem) +{ + const SwsUOp *a = opaque, *b = elem; + if (a->type != b->type) + return (int) b->type - a->type; + if (a->uop != b->uop) + return (int) b->uop - a->uop; + return 0; +} + +static int free_uop_key(void *opaque, void *key) +{ + av_free(key); + return 0; +} + +/** + * Generate a set of boilerplate C preprocessor macros for describing and + * programmatically iterating over all possible SwsUOps. + * + * This function can be quite slow as it iterates over every possible + * combination of pixel formats and flags. + * + * Returns 0 or a negative error code. On success, an allocated string is + * returned via `out_str`, and must be av_free()'d by the caller. + */ +static int sws_uops_macros_gen(char **out_str) +{ + int ret; + struct AVTreeNode *root = NULL; + + AVBPrint bprint, *const bp = &bprint; + av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED); + + /* Allocate dummy graph and context for ff_sws_compile_pass() */ + SwsGraph *graph = ff_sws_graph_alloc(); + if (!graph) + return AVERROR(ENOMEM); + + SwsContext *ctx = graph->ctx = sws_alloc_context(); + if (!ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + + /* Use this to plumb the tree state through all the layers of abstraction */ + ctx->opaque = &root; + ctx->scaler = SWS_SCALE_BILINEAR; /* cheaper to generate filter kernels */ + + /* Register all unique uops over every relevant combination of flags */ + for (int i = 0; i < FF_ARRAY_ELEMS(flags_list); i++) { + ctx->flags = flags_list[i]; + ret = ff_sws_enum_op_lists(ctx, graph, AV_PIX_FMT_NONE, AV_PIX_FMT_NONE, + register_all_uops); + if (ret < 0) + goto fail; + } + + /** + * Additionally make sure planar reads/writes are always available for all + * formats, because checkasm depends on them to be able to verify the + * input/output of any other operations. + */ + for (enum SwsPixelType type = SWS_PIXEL_NONE+1; type < SWS_PIXEL_TYPE_NB; type++) { + if (!ff_sws_pixel_type_is_int(type)) + continue; + for (int elems = 1; elems <= 4; elems++) { + for (int rw = 0; rw < 2; rw++) { + SwsUOp uop = { + .type = type, + .uop = rw ? SWS_UOP_WRITE_PLANAR : SWS_UOP_READ_PLANAR, + .mask = SWS_COMP_ELEMS(elems), + }; + + ret = register_uop(&root, &uop); + if (ret < 0) + goto fail; + } + } + } + + #define BPRINT_STR(str) av_bprint_append_data(bp, str, strlen(str)) + BPRINT_STR( +"/**\n" +" * This file is automatically generated. Do not edit manually.\n" +" * To regenerate, run: make fate-sws-uops-macros GEN=1\n" +" */\n" +"\n" +"#ifndef SWSCALE_UOPS_MACROS_H\n" +"#define SWSCALE_UOPS_MACROS_H\n" +"\n" +"/**\n" +" * Boilerplate helper macros, for template-based backends. These will be\n" +" * instantiated like this, with parameters in struct order:\n" +" * MACRO(__VA_ARGS__, NAME, UOP, TYPE, MASK, [PARAMS,])\n" +" * The _STRUCT variants pass all arguments in C struct syntax, while the\n" +" * plain variants give them as separate C values (e.g. for use in calls)\n" +" */\n" +"#define SWS_GLUE3(x, y, z) x ## _ ## y ## _ ## z\n" +"#define SWS_FOR(TYPE, UOP, MACRO, ...) \\\n" +" SWS_GLUE3(SWS_FOR, TYPE, UOP)(MACRO, __VA_ARGS__)\n" +"#define SWS_FOR_STRUCT(TYPE, UOP, MACRO, ...) \\\n" +" SWS_GLUE3(SWS_FOR_STRUCT, TYPE, UOP)(MACRO, __VA_ARGS__)\n" +"\n"); + + SwsUOp key = { .data.opaque = bp }; + for (key.type = SWS_PIXEL_NONE + 1; key.type < SWS_PIXEL_TYPE_NB; key.type++) { + for (key.uop = SWS_UOP_INVALID + 1; key.uop < SWS_UOP_TYPE_NB; key.uop++) { + const char *macro = uop_names[key.uop].macro; + const char *prefix = pixel_types[key.type].prefix; + av_bprintf(bp, "#define SWS_FOR_%s%s(MACRO, ...)", prefix, macro); + av_tree_enumerate(root, &key, enum_type, generate_entry_args); + av_bprintf(bp, "\n"); + av_bprintf(bp, "#define SWS_FOR_STRUCT_%s%s(MACRO, ...)", prefix, macro); + av_tree_enumerate(root, &key, enum_type, generate_entry_struct); + av_bprintf(bp, "\n"); + } + } + + BPRINT_STR("\n#endif /* SWSCALE_UOPS_MACROS_H */"); + ret = av_bprint_finalize(bp, out_str); + +fail: + av_bprint_finalize(bp, NULL); + av_tree_enumerate(root, NULL, NULL, free_uop_key); + av_tree_destroy(root); + ff_sws_graph_free(&graph); + sws_free_context(&ctx); + return ret; +} + static int print_ops(SwsContext *ctx, const SwsOpList *ops, SwsCompiledOp *out) { SwsUOpList *uops = ff_sws_uop_list_alloc(); @@ -168,7 +536,7 @@ bad_option: if (macros_gen) { char *macros = NULL; - ret = ff_sws_uops_macros_gen(¯os); + ret = sws_uops_macros_gen(¯os); if (ret >= 0) puts(macros); av_free(macros); diff --git a/libswscale/uops.c b/libswscale/uops.c index 096621466c..f1e4267afa 100644 --- a/libswscale/uops.c +++ b/libswscale/uops.c @@ -23,11 +23,10 @@ #include "libavutil/avassert.h" #include "libavutil/mem.h" #include "libavutil/refstruct.h" -#include "libavutil/tree.h" #include "ops.h" -#include "ops_internal.h" #include "uops.h" +#include "uops_list.h" int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b) { @@ -41,59 +40,10 @@ int ff_sws_uop_cmp(const SwsUOp *a, const SwsUOp *b) } static const struct { - char full[32]; - char abbr[32]; - char macro[32]; + char abbr[19]; } uop_names[SWS_UOP_TYPE_NB] = { -#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { "SWS_UOP_" #OP, ABBR, #OP } - UOP_NAME(INVALID, "invalid"), - UOP_NAME(READ_PLANAR, "read_planar"), - UOP_NAME(READ_PLANAR_FH, "read_planar_fh"), - UOP_NAME(READ_PLANAR_FV, "read_planar_fv"), - UOP_NAME(READ_PLANAR_FV_FMA,"read_planar_fv_fma"), - UOP_NAME(READ_PACKED, "read_packed"), - UOP_NAME(READ_NIBBLE, "read_nibble"), - UOP_NAME(READ_BIT, "read_bit"), - UOP_NAME(READ_PALETTE, "read_palette"), - UOP_NAME(WRITE_PLANAR, "write_planar"), - UOP_NAME(WRITE_PACKED, "write_packed"), - UOP_NAME(WRITE_NIBBLE, "write_nibble"), - UOP_NAME(WRITE_BIT, "write_bit"), - UOP_NAME(PERMUTE, "permute"), - UOP_NAME(COPY, "copy"), - UOP_NAME(MOVE, "move"), - UOP_NAME(SWAP_BYTES, "swap_bytes"), - UOP_NAME(EXPAND_BIT, "expand_bit"), - UOP_NAME(EXPAND_PAIR, "expand_pair"), - UOP_NAME(EXPAND_QUAD, "expand_quad"), - UOP_NAME(TO_U8, "to_u8"), - UOP_NAME(TO_U16, "to_u16"), - UOP_NAME(TO_U32, "to_u32"), - UOP_NAME(TO_F32, "to_f32"), - UOP_NAME(SCALE, "scale"), - UOP_NAME(LINEAR, "linear"), - UOP_NAME(LINEAR_FMA, "linear_fma"), - UOP_NAME(ADD, "add"), - UOP_NAME(MIN, "min"), - UOP_NAME(MAX, "max"), - UOP_NAME(UNPACK, "unpack"), - UOP_NAME(PACK, "pack"), - UOP_NAME(LSHIFT, "lshift"), - UOP_NAME(RSHIFT, "rshift"), - UOP_NAME(CLEAR, "clear"), - UOP_NAME(DITHER, "dither"), -#undef UOP_NAME -}; - -static const struct { - char full[16]; - char prefix[8]; -} pixel_types[SWS_PIXEL_TYPE_NB] = { - [SWS_PIXEL_NONE] = { "SWS_PIXEL_NONE", "" }, - [SWS_PIXEL_U8] = { "SWS_PIXEL_U8", "U8_" }, - [SWS_PIXEL_U16] = { "SWS_PIXEL_U16", "U16_" }, - [SWS_PIXEL_U32] = { "SWS_PIXEL_U32", "U32_" }, - [SWS_PIXEL_F32] = { "SWS_PIXEL_F32", "F32_" }, +#define UOP_NAME(OP, ABBR) [SWS_UOP_##OP] = { ABBR }, + UOPS_LIST(UOP_NAME) }; static SwsPixel pixel_from_q(SwsPixelType type, AVRational val) @@ -220,135 +170,6 @@ void ff_sws_uop_name(const SwsUOp *op, char buf[SWS_UOP_NAME_MAX]) av_assert0(av_bprint_is_complete(&bp)); } -static int generate_entry_struct(void *opaque, void *key) -{ - const SwsUOp *ref = opaque; - const SwsUOp *uop = key; - AVBPrint *bp = ref->data.opaque; - char name[SWS_UOP_NAME_MAX]; - ff_sws_uop_name(uop, name); - av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s", name); - av_bprintf(bp, ", .type = %-13s, .uop = %-24s, .mask = 0x%x", - pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask); - - const SwsUOpParams *par = &uop->par; - switch (uop->uop) { - case SWS_UOP_READ_PLANAR_FH: - case SWS_UOP_READ_PLANAR_FV: - case SWS_UOP_READ_PLANAR_FV_FMA: - av_bprintf(bp, ", .par.filter.type = %s", pixel_types[par->filter.type].full); - break; - case SWS_UOP_LSHIFT: - case SWS_UOP_RSHIFT: - av_bprintf(bp, ", .par.shift.amount = %u", par->shift.amount); - break; - case SWS_UOP_PERMUTE: - case SWS_UOP_COPY: - av_bprintf(bp, ", .par.swizzle.in = {%d, %d, %d, %d}", - par->swizzle.in[0], par->swizzle.in[1], - par->swizzle.in[2], par->swizzle.in[3]); - break; - case SWS_UOP_MOVE: - av_bprintf(bp, ", .par.move.num_moves = %d", par->move.num_moves); - av_bprintf(bp, ", .par.move.dst = {%d, %d, %d, %d, %d, %d}", - par->move.dst[0], par->move.dst[1], par->move.dst[2], - par->move.dst[3], par->move.dst[4], par->move.dst[5]); - av_bprintf(bp, ", .par.move.src = {%d, %d, %d, %d, %d, %d}", - par->move.src[0], par->move.src[1], par->move.src[2], - par->move.src[3], par->move.src[4], par->move.src[5]); - break; - case SWS_UOP_PACK: - case SWS_UOP_UNPACK: - av_bprintf(bp, ", .par.pack.pattern = {%d, %d, %d, %d}", - par->pack.pattern[0], par->pack.pattern[1], - par->pack.pattern[2], par->pack.pattern[3]); - break; - case SWS_UOP_CLEAR: - av_bprintf(bp, ", .par.clear.one = 0x%x, .par.clear.zero = 0x%x", - par->clear.one, par->clear.zero); - break; - case SWS_UOP_LINEAR: - case SWS_UOP_LINEAR_FMA: - av_bprintf(bp, ", .par.lin.one = 0x%x, .par.lin.zero = 0x%x", - par->lin.one, par->lin.zero); - if (uop->uop == SWS_UOP_LINEAR_FMA) - av_bprintf(bp, ", .par.lin.exact = 0x%x", par->lin.exact); - break; - case SWS_UOP_DITHER: - av_bprintf(bp, ", .par.dither = { .y_offset = {%u, %u, %u, %u}, .size_log2 = %u }", - par->dither.y_offset[0], par->dither.y_offset[1], - par->dither.y_offset[2], par->dither.y_offset[3], - par->dither.size_log2); - break; - } - - av_bprintf(bp, ")"); - return 0; -} - -static int generate_entry_args(void *opaque, void *key) -{ - const SwsUOp *ref = opaque; - const SwsUOp *uop = key; - AVBPrint *bp = ref->data.opaque; - char name[SWS_UOP_NAME_MAX]; - ff_sws_uop_name(uop, name); - av_bprintf(bp, " \\\n MACRO(__VA_ARGS__, %-40s, %-13s, %-24s, 0x%x", - name, pixel_types[uop->type].full, uop_names[uop->uop].full, uop->mask); - - const SwsUOpParams *par = &uop->par; - switch (uop->uop) { - case SWS_UOP_READ_PLANAR_FH: - case SWS_UOP_READ_PLANAR_FV: - case SWS_UOP_READ_PLANAR_FV_FMA: - av_bprintf(bp, ", %s", pixel_types[par->filter.type].full); - break; - case SWS_UOP_LSHIFT: - case SWS_UOP_RSHIFT: - av_bprintf(bp, ", %u", par->shift.amount); - break; - case SWS_UOP_PERMUTE: - case SWS_UOP_COPY: - av_bprintf(bp, ", %d, %d, %d, %d", - par->swizzle.in[0], par->swizzle.in[1], - par->swizzle.in[2], par->swizzle.in[3]); - break; - case SWS_UOP_MOVE: - av_bprintf(bp, ", %d", par->move.num_moves); - av_bprintf(bp, ", %d, %d, %d, %d, %d, %d", - par->move.dst[0], par->move.dst[1], par->move.dst[2], - par->move.dst[3], par->move.dst[4], par->move.dst[5]); - av_bprintf(bp, ", %d, %d, %d, %d, %d, %d", - par->move.src[0], par->move.src[1], par->move.src[2], - par->move.src[3], par->move.src[4], par->move.src[5]); - break; - case SWS_UOP_PACK: - case SWS_UOP_UNPACK: - av_bprintf(bp, ", %d, %d, %d, %d", - par->pack.pattern[0], par->pack.pattern[1], - par->pack.pattern[2], par->pack.pattern[3]); - break; - case SWS_UOP_CLEAR: - av_bprintf(bp, ", 0x%05x, 0x%05x", par->clear.one, par->clear.zero); - break; - case SWS_UOP_LINEAR: - case SWS_UOP_LINEAR_FMA: - av_bprintf(bp, ", 0x%05x, 0x%05x", par->lin.one, par->lin.zero); - if (uop->uop == SWS_UOP_LINEAR_FMA) - av_bprintf(bp, ", 0x%05x", par->lin.exact); - break; - case SWS_UOP_DITHER: - av_bprintf(bp, ", %u, %u, %u, %u, %u", - par->dither.y_offset[0], par->dither.y_offset[1], - par->dither.y_offset[2], par->dither.y_offset[3], - par->dither.size_log2); - break; - } - - av_bprintf(bp, ")"); - return 0; -} - static void uop_uninit(SwsUOp *uop) { switch (uop->uop) { @@ -872,210 +693,3 @@ int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops, } return 0; } - -static int register_uop(struct AVTreeNode **root, const SwsUOp *uop) -{ - SwsUOp *key = av_memdup(uop, sizeof(*uop)); - if (!key) - return AVERROR(ENOMEM); - memset(&key->data, 0, sizeof(key->data)); - - struct AVTreeNode *node = av_tree_node_alloc(); - if (!node) { - av_free(key); - return AVERROR(ENOMEM); - } - - av_tree_insert(root, key, ff_sws_uop_cmp_v, &node); - if (node) { - av_free(node); - av_free(key); - } - return 0; -} - -static int register_flags(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags flags) -{ - SwsUOpList *uops = ff_sws_uop_list_alloc(); - if (!uops) - return AVERROR(ENOMEM); - - int ret = ff_sws_ops_translate(ctx, ops, flags, uops); - if (ret < 0) - goto fail; - - struct AVTreeNode **root = ctx->opaque; - for (int i = 0; i < uops->num_ops; i++) { - ret = register_uop(root, &uops->ops[i]); - if (ret < 0) - goto fail; - } - -fail: - ff_sws_uop_list_free(&uops); - return ret; -} - -static const SwsUOpFlags uop_flags[] = { - 0, - SWS_UOP_FLAG_FMA | SWS_UOP_FLAG_MOVE, /* x86 backend */ -}; - -static int register_uops(SwsContext *ctx, const SwsOpList *ops, - SwsCompiledOp *out) -{ - for (int i = 0; i < FF_ARRAY_ELEMS(uop_flags); i++) { - int ret = register_flags(ctx, ops, uop_flags[i]); - if (ret < 0) - return ret; - } - - *out = (SwsCompiledOp) {0}; /* dummy value, will be immediately freed */ - return 0; -} - -/* Dummy backend that just registers all seen uops */ -static const SwsOpBackend backend_uops = { - .name = "uops_gen", - .compile = register_uops, -}; - -static int register_all_uops(SwsContext *ctx, void *graph, SwsOpList *ops) -{ - /* ff_sws_compile_pass() takes over ownership of `ops` */ - SwsOpList *copy = ff_sws_op_list_duplicate(ops); - if (!copy) - return AVERROR(ENOMEM); - - const int flags = SWS_OP_FLAG_DRY_RUN | SWS_OP_FLAG_SPLIT_MEMCPY; - return ff_sws_compile_pass(graph, &backend_uops, ©, flags, NULL, NULL); -} - -static const SwsFlags flags[] = { - 0, - SWS_ACCURATE_RND, /* may insert extra 1x1 dither ops (for accurate rounding) */ - SWS_BITEXACT, /* prevents some FMA optimizations */ - SWS_ACCURATE_RND | SWS_BITEXACT, -}; - -/* Limit the range of av_tree_enumerate() to only matching uop and type */ -static int enum_type(void *opaque, void *elem) -{ - const SwsUOp *a = opaque, *b = elem; - if (a->type != b->type) - return (int) b->type - a->type; - if (a->uop != b->uop) - return (int) b->uop - a->uop; - return 0; -} - -static int free_uop_key(void *opaque, void *key) -{ - av_free(key); - return 0; -} - -int ff_sws_uops_macros_gen(char **out_str) -{ - int ret; - struct AVTreeNode *root = NULL; - - AVBPrint bprint, *const bp = &bprint; - av_bprint_init(bp, 0, AV_BPRINT_SIZE_UNLIMITED); - - /* Allocate dummy graph and context for ff_sws_compile_pass() */ - SwsGraph *graph = ff_sws_graph_alloc(); - if (!graph) - return AVERROR(ENOMEM); - - SwsContext *ctx = graph->ctx = sws_alloc_context(); - if (!ctx) { - ret = AVERROR(ENOMEM); - goto fail; - } - - /* Use this to plumb the tree state through all the layers of abstraction */ - ctx->opaque = &root; - ctx->scaler = SWS_SCALE_BILINEAR; /* cheaper to generate filter kernels */ - - /* Register all unique uops over every relevant combination of flags */ - for (int i = 0; i < FF_ARRAY_ELEMS(flags); i++) { - ctx->flags = flags[i]; - ret = ff_sws_enum_op_lists(ctx, graph, AV_PIX_FMT_NONE, AV_PIX_FMT_NONE, - register_all_uops); - if (ret < 0) - goto fail; - } - - /** - * Additionally make sure planar reads/writes are always available for all - * formats, because checkasm depends on them to be able to verify the - * input/output of any other operations. - */ - for (enum SwsPixelType type = SWS_PIXEL_NONE+1; type < SWS_PIXEL_TYPE_NB; type++) { - if (!ff_sws_pixel_type_is_int(type)) - continue; - for (int elems = 1; elems <= 4; elems++) { - for (int rw = 0; rw < 2; rw++) { - SwsUOp uop = { - .type = type, - .uop = rw ? SWS_UOP_WRITE_PLANAR : SWS_UOP_READ_PLANAR, - .mask = SWS_COMP_ELEMS(elems), - }; - - ret = register_uop(&root, &uop); - if (ret < 0) - goto fail; - } - } - } - - #define BPRINT_STR(str) av_bprint_append_data(bp, str, strlen(str)) - BPRINT_STR( -"/**\n" -" * This file is automatically generated. Do not edit manually.\n" -" * To regenerate, run: make fate-sws-uops-macros GEN=1\n" -" */\n" -"\n" -"#ifndef SWSCALE_UOPS_MACROS_H\n" -"#define SWSCALE_UOPS_MACROS_H\n" -"\n" -"/**\n" -" * Boilerplate helper macros, for template-based backends. These will be\n" -" * instantiated like this, with parameters in struct order:\n" -" * MACRO(__VA_ARGS__, NAME, UOP, TYPE, MASK, [PARAMS,])\n" -" * The _STRUCT variants pass all arguments in C struct syntax, while the\n" -" * plain variants give them as separate C values (e.g. for use in calls)\n" -" */\n" -"#define SWS_GLUE3(x, y, z) x ## _ ## y ## _ ## z\n" -"#define SWS_FOR(TYPE, UOP, MACRO, ...) \\\n" -" SWS_GLUE3(SWS_FOR, TYPE, UOP)(MACRO, __VA_ARGS__)\n" -"#define SWS_FOR_STRUCT(TYPE, UOP, MACRO, ...) \\\n" -" SWS_GLUE3(SWS_FOR_STRUCT, TYPE, UOP)(MACRO, __VA_ARGS__)\n" -"\n"); - - SwsUOp key = { .data.opaque = bp }; - for (key.type = SWS_PIXEL_NONE + 1; key.type < SWS_PIXEL_TYPE_NB; key.type++) { - for (key.uop = SWS_UOP_INVALID + 1; key.uop < SWS_UOP_TYPE_NB; key.uop++) { - const char *macro = uop_names[key.uop].macro; - const char *prefix = pixel_types[key.type].prefix; - av_bprintf(bp, "#define SWS_FOR_%s%s(MACRO, ...)", prefix, macro); - av_tree_enumerate(root, &key, enum_type, generate_entry_args); - av_bprintf(bp, "\n"); - av_bprintf(bp, "#define SWS_FOR_STRUCT_%s%s(MACRO, ...)", prefix, macro); - av_tree_enumerate(root, &key, enum_type, generate_entry_struct); - av_bprintf(bp, "\n"); - } - } - - BPRINT_STR("\n#endif /* SWSCALE_UOPS_MACROS_H */"); - ret = av_bprint_finalize(bp, out_str); - -fail: - av_bprint_finalize(bp, NULL); - av_tree_enumerate(root, NULL, NULL, free_uop_key); - av_tree_destroy(root); - ff_sws_graph_free(&graph); - sws_free_context(&ctx); - return ret; -} diff --git a/libswscale/uops.h b/libswscale/uops.h index 284f74042b..1ad14efc58 100644 --- a/libswscale/uops.h +++ b/libswscale/uops.h @@ -272,16 +272,4 @@ int ff_sws_uop_list_append(SwsUOpList *uops, SwsUOp *uop); int ff_sws_ops_translate(SwsContext *ctx, const SwsOpList *ops, SwsUOpFlags flags, SwsUOpList *uops); -/** - * Generate a set of boilerplate C preprocessor macros for describing and - * programmatically iterating over all possible SwsUOps. - * - * This function can be quite slow as it iterates over every possible - * combination of pixel formats and flags. - * - * Returns 0 or a negative error code. On success, an allocated string is - * returned via `out_str`, and must be av_free()'d by the caller. - */ -int ff_sws_uops_macros_gen(char **out_str); - #endif diff --git a/libswscale/uops_list.h b/libswscale/uops_list.h new file mode 100644 index 0000000000..a4db78b3c5 --- /dev/null +++ b/libswscale/uops_list.h @@ -0,0 +1,60 @@ +/** + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWSCALE_UOPS_LIST_H +#define SWSCALE_UOPS_LIST_H + +#define UOPS_LIST(ENTRY) \ + ENTRY(INVALID, "invalid") \ + ENTRY(READ_PLANAR, "read_planar") \ + ENTRY(READ_PLANAR_FH, "read_planar_fh") \ + ENTRY(READ_PLANAR_FV, "read_planar_fv") \ + ENTRY(READ_PLANAR_FV_FMA, "read_planar_fv_fma") \ + ENTRY(READ_PACKED, "read_packed") \ + ENTRY(READ_NIBBLE, "read_nibble") \ + ENTRY(READ_BIT, "read_bit") \ + ENTRY(READ_PALETTE, "read_palette") \ + ENTRY(WRITE_PLANAR, "write_planar") \ + ENTRY(WRITE_PACKED, "write_packed") \ + ENTRY(WRITE_NIBBLE, "write_nibble") \ + ENTRY(WRITE_BIT, "write_bit") \ + ENTRY(PERMUTE, "permute") \ + ENTRY(COPY, "copy") \ + ENTRY(MOVE, "move") \ + ENTRY(SWAP_BYTES, "swap_bytes") \ + ENTRY(EXPAND_BIT, "expand_bit") \ + ENTRY(EXPAND_PAIR, "expand_pair") \ + ENTRY(EXPAND_QUAD, "expand_quad") \ + ENTRY(TO_U8, "to_u8") \ + ENTRY(TO_U16, "to_u16") \ + ENTRY(TO_U32, "to_u32") \ + ENTRY(TO_F32, "to_f32") \ + ENTRY(SCALE, "scale") \ + ENTRY(LINEAR, "linear") \ + ENTRY(LINEAR_FMA, "linear_fma") \ + ENTRY(ADD, "add") \ + ENTRY(MIN, "min") \ + ENTRY(MAX, "max") \ + ENTRY(UNPACK, "unpack") \ + ENTRY(PACK, "pack") \ + ENTRY(LSHIFT, "lshift") \ + ENTRY(RSHIFT, "rshift") \ + ENTRY(CLEAR, "clear") \ + ENTRY(DITHER, "dither") \ + +#endif /* SWSCALE_UOPS_LIST_H */ -- 2.52.0 >From a50b490998ba79de51f77d1e59940f077b475963 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt <[email protected]> Date: Wed, 24 Jun 2026 16:51:27 +0200 Subject: [PATCH 4/4] swscale/ops: Move test-only stuff to sws_ops tests Signed-off-by: Andreas Rheinhardt <[email protected]> --- libswscale/ops.c | 76 ------------------- libswscale/ops.h | 15 ---- libswscale/tests/enum_op_lists.h | 117 +++++++++++++++++++++++++++++ libswscale/tests/sws_ops.c | 2 + libswscale/tests/sws_ops_aarch64.c | 2 + 5 files changed, 121 insertions(+), 91 deletions(-) create mode 100644 libswscale/tests/enum_op_lists.h diff --git a/libswscale/ops.c b/libswscale/ops.c index 26e6a6171c..beaffb5ccf 100644 --- a/libswscale/ops.c +++ b/libswscale/ops.c @@ -1003,79 +1003,3 @@ void ff_sws_op_list_print(void *log, int lev, int lev_extra, av_log(log, lev, " ('X' unused, 'z' byteswapped, '=' copied, '$' const, '+' integer, '0' zero)\n"); } - -#define DUMMY_SIZE 16 - -static int enum_ops_fmt(SwsContext *ctx, void *opaque, - enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, - int (*cb)(SwsContext *ctx, void *opaque, SwsOpList *ops)) -{ - int ret = 0; - SwsOpList *ops = NULL; - SwsFormat src, dst; - ff_fmt_from_pixfmt(src_fmt, &src); - ff_fmt_from_pixfmt(dst_fmt, &dst); - bool incomplete = ff_infer_colors(&src.color, &dst.color); - src.width = src.height = DUMMY_SIZE; - - static const int dst_sizes[][2] = { - { DUMMY_SIZE, DUMMY_SIZE }, - { DUMMY_SIZE, DUMMY_SIZE * 2 }, - { DUMMY_SIZE * 2, DUMMY_SIZE }, - { DUMMY_SIZE * 2, DUMMY_SIZE * 2 }, - }; - - for (int i = 0; i < FF_ARRAY_ELEMS(dst_sizes); i++) { - dst.width = dst_sizes[i][0]; - dst.height = dst_sizes[i][1]; - - ret = ff_sws_op_list_generate(ctx, &src, &dst, &ops, &incomplete); - if (ret == AVERROR(ENOTSUP)) - return 0; /* silently skip unsupported formats */ - else if (ret < 0) - return ret; - - ret = ff_sws_op_list_optimize(ops); - if (ret < 0) - goto fail; - - ret = cb(ctx, opaque, ops); - if (ret < 0) - goto fail; - - ff_sws_op_list_free(&ops); - } - -fail: - ff_sws_op_list_free(&ops); - return ret; -} - -int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque, - enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, - int (*cb)(SwsContext *ctx, void *opaque, SwsOpList *ops)) -{ - const AVPixFmtDescriptor *src_start = av_pix_fmt_desc_next(NULL); - const AVPixFmtDescriptor *dst_start = src_start; - if (src_fmt != AV_PIX_FMT_NONE) - src_start = av_pix_fmt_desc_get(src_fmt); - if (dst_fmt != AV_PIX_FMT_NONE) - dst_start = av_pix_fmt_desc_get(dst_fmt); - - const AVPixFmtDescriptor *src, *dst; - for (src = src_start; src; src = av_pix_fmt_desc_next(src)) { - const enum AVPixelFormat src_f = av_pix_fmt_desc_get_id(src); - for (dst = dst_start; dst; dst = av_pix_fmt_desc_next(dst)) { - const enum AVPixelFormat dst_f = av_pix_fmt_desc_get_id(dst); - int ret = enum_ops_fmt(ctx, opaque, src_f, dst_f, cb); - if (ret < 0) - return ret; - if (dst_fmt != AV_PIX_FMT_NONE) - break; - } - if (src_fmt != AV_PIX_FMT_NONE) - break; - } - - return 0; -} diff --git a/libswscale/ops.h b/libswscale/ops.h index f2a57612b4..06820b3681 100644 --- a/libswscale/ops.h +++ b/libswscale/ops.h @@ -343,19 +343,4 @@ void ff_sws_op_list_update_comps(SwsOpList *ops); */ int ff_sws_op_list_optimize(SwsOpList *ops); -/** - * Helper function to enumerate over all possible (optimized) operation lists, - * under the current set of options in `ctx`, and run the given callback on - * each list. - * - * @param src_fmt If set (not AV_PIX_FMT_NONE), constrain the source format - * @param dst_fmt If set (not AV_PIX_FMT_NONE), constrain the destination format - * @return 0 on success, the return value if cb() < 0, or a negative error code - * - * @note `ops` belongs to ff_sws_enum_op_lists(), but may be mutated by `cb`. - */ -int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque, - enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, - int (*cb)(SwsContext *ctx, void *opaque, SwsOpList *ops)); - #endif diff --git a/libswscale/tests/enum_op_lists.h b/libswscale/tests/enum_op_lists.h new file mode 100644 index 0000000000..d6b55357af --- /dev/null +++ b/libswscale/tests/enum_op_lists.h @@ -0,0 +1,117 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef SWSCALE_TESTS_ENUM_OP_LISTS_H +#define SWSCALE_TESTS_ENUM_OP_LISTS_H + +#include "libswscale/format.h" +#include "libswscale/ops.h" +#include "libswscale/swscale.h" + +#include "libavutil/error.h" +#include "libavutil/pixdesc.h" +#include "libavutil/pixfmt.h" + +#define DUMMY_SIZE 16 + +static int enum_ops_fmt(SwsContext *ctx, void *opaque, + enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, + int (*cb)(SwsContext *ctx, void *opaque, SwsOpList *ops)) +{ + int ret = 0; + SwsOpList *ops = NULL; + SwsFormat src, dst; + ff_fmt_from_pixfmt(src_fmt, &src); + ff_fmt_from_pixfmt(dst_fmt, &dst); + bool incomplete = ff_infer_colors(&src.color, &dst.color); + src.width = src.height = DUMMY_SIZE; + + static const int dst_sizes[][2] = { + { DUMMY_SIZE, DUMMY_SIZE }, + { DUMMY_SIZE, DUMMY_SIZE * 2 }, + { DUMMY_SIZE * 2, DUMMY_SIZE }, + { DUMMY_SIZE * 2, DUMMY_SIZE * 2 }, + }; + + for (int i = 0; i < FF_ARRAY_ELEMS(dst_sizes); i++) { + dst.width = dst_sizes[i][0]; + dst.height = dst_sizes[i][1]; + + ret = ff_sws_op_list_generate(ctx, &src, &dst, &ops, &incomplete); + if (ret == AVERROR(ENOTSUP)) + return 0; /* silently skip unsupported formats */ + else if (ret < 0) + return ret; + + ret = ff_sws_op_list_optimize(ops); + if (ret < 0) + goto fail; + + ret = cb(ctx, opaque, ops); + if (ret < 0) + goto fail; + + ff_sws_op_list_free(&ops); + } + +fail: + ff_sws_op_list_free(&ops); + return ret; +} + +/** + * Helper function to enumerate over all possible (optimized) operation lists, + * under the current set of options in `ctx`, and run the given callback on + * each list. + * + * @param src_fmt If set (not AV_PIX_FMT_NONE), constrain the source format + * @param dst_fmt If set (not AV_PIX_FMT_NONE), constrain the destination format + * @return 0 on success, the return value if cb() < 0, or a negative error code + * + * @note `ops` belongs to sws_enum_op_lists(), but may be mutated by `cb`. + */ +static inline +int ff_sws_enum_op_lists(SwsContext *ctx, void *opaque, + enum AVPixelFormat src_fmt, enum AVPixelFormat dst_fmt, + int (*cb)(SwsContext *ctx, void *opaque, SwsOpList *ops)) +{ + const AVPixFmtDescriptor *src_start = av_pix_fmt_desc_next(NULL); + const AVPixFmtDescriptor *dst_start = src_start; + if (src_fmt != AV_PIX_FMT_NONE) + src_start = av_pix_fmt_desc_get(src_fmt); + if (dst_fmt != AV_PIX_FMT_NONE) + dst_start = av_pix_fmt_desc_get(dst_fmt); + + const AVPixFmtDescriptor *src, *dst; + for (src = src_start; src; src = av_pix_fmt_desc_next(src)) { + const enum AVPixelFormat src_f = av_pix_fmt_desc_get_id(src); + for (dst = dst_start; dst; dst = av_pix_fmt_desc_next(dst)) { + const enum AVPixelFormat dst_f = av_pix_fmt_desc_get_id(dst); + int ret = enum_ops_fmt(ctx, opaque, src_f, dst_f, cb); + if (ret < 0) + return ret; + if (dst_fmt != AV_PIX_FMT_NONE) + break; + } + if (src_fmt != AV_PIX_FMT_NONE) + break; + } + + return 0; +} +#endif diff --git a/libswscale/tests/sws_ops.c b/libswscale/tests/sws_ops.c index e2d01e76cf..868a4837b6 100644 --- a/libswscale/tests/sws_ops.c +++ b/libswscale/tests/sws_ops.c @@ -27,6 +27,8 @@ #include "libswscale/uops_list.h" #include "libswscale/format.h" +#include "enum_op_lists.h" + #ifdef _WIN32 #include <io.h> #include <fcntl.h> diff --git a/libswscale/tests/sws_ops_aarch64.c b/libswscale/tests/sws_ops_aarch64.c index 3dc68bf5c1..9ef2bb9fc4 100644 --- a/libswscale/tests/sws_ops_aarch64.c +++ b/libswscale/tests/sws_ops_aarch64.c @@ -28,6 +28,8 @@ #include "libswscale/aarch64/ops_impl.c" #include "libswscale/aarch64/ops_impl_conv.c" +#include "enum_op_lists.h" + #ifdef _WIN32 #include <io.h> #include <fcntl.h> -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
