This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit e65dab2b5919ede3a4f308d723e46a6f65643ca8 Author: Niklas Haas <[email protected]> AuthorDate: Mon Jul 13 21:31:23 2026 +0200 Commit: Niklas Haas <[email protected]> CommitDate: Tue Jul 14 12:24:01 2026 +0000 swscale/tests/sws_ops: rewrite to test uops directly Instead of awkwardly reverse-engineering each uop to reflect it back to the corresponding SwsOp, this leverages the new compile_uops() API to directly test the uop in question. This is not only vastly simpler but also fixes several major shortcomings with the previous design, such as always having to keep the reflection code in sync for any newly added uop, or not testing uops that get translated to a packed shuffle fast path. The major downside is that arch-specific uops can now no longer be tested against the C reference unless we specifically add a C reference function for each relevant uop; and also, that the packed shuffle fast path is currently no longer tested by checkasm. (But this will be fixed by my upcoming pshufb uop series) Another downside is that this effectively disables testing for e.g. aarch64, since it does not yet use the uops architecture. $ time checkasm --test=sw_ops --repeat=10 # 1.85s -> 1.39s Signed-off-by: Niklas Haas <[email protected]> --- libswscale/uops.h | 1 + tests/checkasm/sw_ops.c | 711 ++++++++++++++++++++---------------------------- 2 files changed, 294 insertions(+), 418 deletions(-) diff --git a/libswscale/uops.h b/libswscale/uops.h index 1c4d9b36fc..923b7743b5 100644 --- a/libswscale/uops.h +++ b/libswscale/uops.h @@ -71,6 +71,7 @@ enum { #define SWS_COMP_TEST(mask, X) (!!((mask) & SWS_COMP(X))) #define SWS_COMP_INV(mask) ((mask) ^ SWS_COMP_ALL) #define SWS_COMP_ELEMS(N) ((1 << (N)) - 1) +#define SWS_COMP_COUNT(mask) (av_popcount((mask) & SWS_COMP_ALL)) #define SWS_COMP_MASK(X, Y, Z, W) \ (((X) ? SWS_COMP(0) : 0) | \ ((Y) ? SWS_COMP(1) : 0) | \ diff --git a/tests/checkasm/sw_ops.c b/tests/checkasm/sw_ops.c index d577663c9c..e6c28062ed 100644 --- a/tests/checkasm/sw_ops.c +++ b/tests/checkasm/sw_ops.c @@ -24,7 +24,7 @@ #include "libavutil/mem_internal.h" #include "libavutil/refstruct.h" -#include "libswscale/ops.h" +#include "libswscale/filters.h" #include "libswscale/ops_dispatch.h" #include "libswscale/uops.h" #include "libswscale/uops_macros.h" @@ -32,17 +32,26 @@ #include "checkasm.h" enum { + MAX_UOPS = 3, /* read, op, write */ NB_PLANES = 4, PIXELS = 64, LINES = 16, }; -enum { - U8 = SWS_PIXEL_U8, - U16 = SWS_PIXEL_U16, - U32 = SWS_PIXEL_U32, - F32 = SWS_PIXEL_F32, -}; +typedef struct Test { + const char *name; + SwsUOp uops[MAX_UOPS]; + int num_uops; + + /* Extra metadata for the test harness */ + SwsPixelType type_in; /* data format to fill */ + SwsPixelType type_out; /* data format to compare */ + SwsCompMask planes_in; /* planes to fill */ + SwsCompMask planes_out; /* planes to compare */ + int pixel_bits_in; /* read pixel stride */ + int pixel_bits_out; /* write pixel stride */ + unsigned ranges[NB_PLANES]; /* pixel range to fill */ +} Test; #define FMT(fmt, ...) tprintf((char[256]) {0}, 256, fmt, __VA_ARGS__) static const char *tprintf(char buf[], size_t size, const char *fmt, ...) @@ -54,23 +63,6 @@ static const char *tprintf(char buf[], size_t size, const char *fmt, ...) return buf; } -static int rw_pixel_bits(const SwsOp *op) -{ - if (op->rw.mode == SWS_RW_PALETTE) - return 8; /* index size */ - - int elems = 0; - switch (op->rw.mode) { - case SWS_RW_PLANAR: elems = 1; break; - case SWS_RW_PACKED: elems = op->rw.elems; break; - } - - const int size = ff_sws_pixel_type_size(op->type); - const int bits = 8 >> op->rw.frac; - av_assert1(bits >= 1); - return elems * size * bits; -} - static float rndf(void) { union { uint32_t u; float f; } x; @@ -80,6 +72,28 @@ static float rndf(void) return x.f; } +static SwsPixel constpx(SwsPixelType type, int val) +{ + switch (type) { + case SWS_PIXEL_U8: return (SwsPixel) { .u8 = val }; + case SWS_PIXEL_U16: return (SwsPixel) { .u16 = val }; + case SWS_PIXEL_U32: return (SwsPixel) { .u32 = val }; + case SWS_PIXEL_F32: return (SwsPixel) { .f32 = val }; + default: return (SwsPixel) {0}; + } +} + +static SwsPixel rndpx(SwsPixelType type) +{ + switch (type) { + case SWS_PIXEL_U8: return (SwsPixel) { .u8 = rnd() & 0xFF }; + case SWS_PIXEL_U16: return (SwsPixel) { .u16 = rnd() & 0xFFFF }; + case SWS_PIXEL_U32: return (SwsPixel) { .u32 = rnd() }; + case SWS_PIXEL_F32: return (SwsPixel) { .f32 = rndf() }; + default: return (SwsPixel) {0}; + } +} + static void fill32f(float *line, int num, unsigned range) { const float scale = (float) range / UINT32_MAX; @@ -113,17 +127,20 @@ static void fill8(uint8_t *line, int num, unsigned range) } } -static void set_range(AVRational64 *rangeq, unsigned range, unsigned range_def) +static SwsPixelType pixel_type_to_int(const SwsPixelType type) { - if (!range) - range = range_def; - if (range) - *rangeq = (AVRational64) { range, 1 }; + switch (ff_sws_pixel_type_size(type)) { + case 1: return SWS_PIXEL_U8; + case 2: return SWS_PIXEL_U16; + case 4: return SWS_PIXEL_U32; + default: break; + } + + av_unreachable("Invalid pixel type!"); + return SWS_PIXEL_NONE; } -static void check_compiled(const char *name, - const SwsOp *read_op, const SwsOp *write_op, - const int ranges[NB_PLANES], +static void check_compiled(const Test *test, const SwsCompiledOp *comp_ref, const SwsCompiledOp *comp_new) { @@ -134,7 +151,7 @@ static void check_compiled(const char *name, */ uintptr_t id = (uintptr_t) comp_new->func; id ^= (id << 6) + (id >> 2) + 0x9e3779b97f4a7c15 + comp_new->cpu_flags; - if (!check_key(id, "%s", name)) + if (!check_key(id, "%s", test->name)) return; declare_func(void, const SwsOpExec *, const void *, int bx, int y, int bx_end, int y_end); @@ -147,18 +164,23 @@ static void check_compiled(const char *name, av_assert0(PIXELS % comp_new->block_size == 0); for (int p = 0; p < NB_PLANES; p++) { void *plane = src0[p]; - switch (read_op->type) { - case U8: - fill8(plane, sizeof(src0[p]) / sizeof(uint8_t), ranges[p]); + if (!SWS_COMP_TEST(test->planes_in, p)) { + memset(plane, 0, sizeof(src0[p])); + continue; + } + + switch (test->type_in) { + case SWS_PIXEL_U8: + fill8(plane, sizeof(src0[p]) / sizeof(uint8_t), test->ranges[p]); break; - case U16: - fill16(plane, sizeof(src0[p]) / sizeof(uint16_t), ranges[p]); + case SWS_PIXEL_U16: + fill16(plane, sizeof(src0[p]) / sizeof(uint16_t), test->ranges[p]); break; - case U32: - fill32(plane, sizeof(src0[p]) / sizeof(uint32_t), ranges[p]); + case SWS_PIXEL_U32: + fill32(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]); break; - case F32: - fill32f(plane, sizeof(src0[p]) / sizeof(uint32_t), ranges[p]); + case SWS_PIXEL_F32: + fill32f(plane, sizeof(src0[p]) / sizeof(uint32_t), test->ranges[p]); break; } } @@ -167,8 +189,8 @@ static void check_compiled(const char *name, memset(dst0, 0, sizeof(dst0)); memset(dst1, 0, sizeof(dst1)); - const int read_size = PIXELS * rw_pixel_bits(read_op) >> 3; - const int write_size = PIXELS * rw_pixel_bits(write_op) >> 3; + const int read_size = PIXELS * test->pixel_bits_in >> 3; + const int write_size = PIXELS * test->pixel_bits_out >> 3; SwsOpExec exec = {0}; exec.width = PIXELS; @@ -180,123 +202,100 @@ static void check_compiled(const char *name, exec.out_bump[i] = exec.out_stride[i] - write_size; } - if (read_op->rw.mode == SWS_RW_PALETTE) { + int32_t in_bump_y[LINES]; + int32_t in_offset_x[PIXELS]; + const SwsUOp *read_op = &test->uops[0]; + switch (read_op->uop) { + case SWS_UOP_READ_PALETTE: static_assert(sizeof(src0[1]) >= sizeof(uint32_t[256]), "palette plane too small"); exec.in_bump[1] = exec.in_stride[1] = 0; - } - - int32_t in_bump_y[LINES]; - if (read_op->rw.filter.op == SWS_OP_FILTER_V) { - const int *offsets = read_op->rw.filter.kernel->offsets; + break; + case SWS_UOP_READ_PLANAR_FV: { + const int *offsets = read_op->data.kernel->offsets; for (int y = 0; y < LINES - 1; y++) in_bump_y[y] = offsets[y + 1] - offsets[y] - 1; in_bump_y[LINES - 1] = 0; exec.in_bump_y = in_bump_y; + break; } - - int32_t in_offset_x[PIXELS]; - if (read_op->rw.filter.op == SWS_OP_FILTER_H) { - const int *offsets = read_op->rw.filter.kernel->offsets; - const int rw_bits = rw_pixel_bits(read_op); + case SWS_UOP_READ_PLANAR_FH: { + const int *offsets = read_op->data.kernel->offsets; for (int x = 0; x < PIXELS; x++) - in_offset_x[x] = offsets[x] * rw_bits >> 3; + in_offset_x[x] = offsets[x] * test->pixel_bits_in >> 3; exec.in_offset_x = in_offset_x; } + } for (int i = 0; i < NB_PLANES; i++) { exec.in[i] = (void *) src0[i]; exec.out[i] = (void *) dst0[i]; - exec.block_size_in[i] = comp_ref->block_size * rw_pixel_bits(read_op) >> 3; - exec.block_size_out[i] = comp_ref->block_size * rw_pixel_bits(write_op) >> 3; + exec.block_size_in[i] = comp_ref->block_size * test->pixel_bits_in >> 3; + exec.block_size_out[i] = comp_ref->block_size * test->pixel_bits_out >> 3; } checkasm_call(comp_ref->func, &exec, comp_ref->priv, 0, 0, PIXELS / comp_ref->block_size, LINES); for (int i = 0; i < NB_PLANES; i++) { exec.in[i] = (void *) src1[i]; exec.out[i] = (void *) dst1[i]; - exec.block_size_in[i] = comp_new->block_size * rw_pixel_bits(read_op) >> 3; - exec.block_size_out[i] = comp_new->block_size * rw_pixel_bits(write_op) >> 3; + exec.block_size_in[i] = comp_new->block_size * test->pixel_bits_in >> 3; + exec.block_size_out[i] = comp_new->block_size * test->pixel_bits_out >> 3; } checkasm_call_checked(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES); for (int i = 0; i < NB_PLANES; i++) { - const char *desc = FMT("%s[%d]", name, i); - const int stride = sizeof(dst0[i][0]); + if (!SWS_COMP_TEST(test->planes_out, i)) + continue; - switch (write_op->type) { - case U8: + const char *desc = FMT("%s[%d]", test->name, i); + const int stride = sizeof(dst0[i][0]); + switch (test->type_out) { + case SWS_PIXEL_U8: checkasm_check(uint8_t, (void *) dst0[i], stride, (void *) dst1[i], stride, write_size, LINES, desc); break; - case U16: + case SWS_PIXEL_U16: checkasm_check(uint16_t, (void *) dst0[i], stride, (void *) dst1[i], stride, write_size >> 1, LINES, desc); break; - case U32: + case SWS_PIXEL_U32: checkasm_check(uint32_t, (void *) dst0[i], stride, (void *) dst1[i], stride, write_size >> 2, LINES, desc); break; - case F32: + case SWS_PIXEL_F32: checkasm_check(float_ulp, (void *) dst0[i], stride, (void *) dst1[i], stride, write_size >> 2, LINES, desc, 0); break; } - - if (write_op->rw.mode == SWS_RW_PACKED) - break; } bench(comp_new->func, &exec, comp_new->priv, 0, 0, PIXELS / comp_new->block_size, LINES); } -static void check_ops(const char *name, const unsigned ranges[NB_PLANES], - const SwsOp *ops) +static void run_test(const Test *test) { SwsContext *ctx = sws_alloc_context(); if (!ctx) return; ctx->flags = SWS_BITEXACT; - static const unsigned def_ranges[4] = {0}; - if (!ranges) - ranges = def_ranges; + /* Generate dummy uop list on-stack */ + SwsUOpList oplist = { + .ops = (SwsUOp *) test->uops, + .num_ops = test->num_uops, - const SwsOp *read_op, *write_op; - SwsOpList oplist = { - .ops = (SwsOp *) ops, - .plane_src = {0, 1, 2, 3}, - .plane_dst = {0, 1, 2, 3}, + /* Less efficient, but works universally */ + .planes_in = SWS_COMP_ALL, + .planes_out = SWS_COMP_ALL, }; - read_op = &ops[0]; - for (oplist.num_ops = 0; ops[oplist.num_ops].op; oplist.num_ops++) - write_op = &ops[oplist.num_ops]; - - for (int p = 0; p < NB_PLANES; p++) { - switch (read_op->type) { - case U8: - set_range(&oplist.comps_src.max[p], ranges[p], UINT8_MAX); - oplist.comps_src.min[p] = (AVRational64) { 0, 1 }; - break; - case U16: - set_range(&oplist.comps_src.max[p], ranges[p], UINT16_MAX); - oplist.comps_src.min[p] = (AVRational64) { 0, 1 }; - break; - case U32: - set_range(&oplist.comps_src.max[p], ranges[p], UINT32_MAX); - oplist.comps_src.min[p] = (AVRational64) { 0, 1 }; - break; - case F32: - if (ranges[p]) { - oplist.comps_src.max[p] = (AVRational64) { ranges[p], 1 }; - oplist.comps_src.min[p] = (AVRational64) { 0, 1 }; - } - break; - } + for (int i = 0; i < test->num_uops; i++) { + const int pixel_size = ff_sws_pixel_type_size(test->uops[i].type); + if (pixel_size > oplist.pixel_size_max) + oplist.pixel_size_max = pixel_size; } static const SwsOpBackend *backend_ref; @@ -312,7 +311,7 @@ static void check_ops(const char *name, const unsigned ranges[NB_PLANES], /* Always compile `ops` using the C backend as a reference */ SwsCompiledOp comp_ref = {0}; - int ret = ff_sws_ops_compile(ctx, backend_ref, &oplist, &comp_ref); + int ret = backend_ref->compile_uops(ctx, &oplist, &comp_ref); if (ret < 0) { av_assert0(ret != AVERROR(ENOTSUP)); fail(); @@ -320,16 +319,18 @@ static void check_ops(const char *name, const unsigned ranges[NB_PLANES], } /* Check with the C backend to establish a reference */ - check_compiled(name, read_op, write_op, ranges, &comp_ref, &comp_ref); + check_compiled(test, &comp_ref, &comp_ref); /* Iterate over every other backend, and test it against the C reference */ for (int n = 0; ff_sws_op_backends[n]; n++) { const SwsOpBackend *backend = ff_sws_op_backends[n]; if (backend->hw_format != AV_PIX_FMT_NONE || backend == backend_ref) continue; + if (!backend->compile_uops) + continue; SwsCompiledOp comp_new = {0}; - int ret = ff_sws_ops_compile(ctx, backend, &oplist, &comp_new); + int ret = backend->compile_uops(ctx, &oplist, &comp_new); if (ret == AVERROR(ENOTSUP)) { continue; } else if (ret < 0) { @@ -339,7 +340,7 @@ static void check_ops(const char *name, const unsigned ranges[NB_PLANES], /* Distinguish backends from each other even with same CPU flags */ checkasm_set_func_variant("%s_%s", backend->name, checkasm_get_cpu_suffix()); - check_compiled(name, read_op, write_op, ranges, &comp_ref, &comp_new); + check_compiled(test, &comp_ref, &comp_new); ff_sws_compiled_op_unref(&comp_new); } @@ -348,127 +349,149 @@ done: sws_free_context(&ctx); } -#define CHECK_RANGES(NAME, RANGES, N_IN, N_OUT, IN, OUT, ...) \ - do { \ - check_ops(NAME, RANGES, (SwsOp[]) { \ - { \ - .op = SWS_OP_READ, \ - .type = IN, \ - .rw.elems = N_IN, \ - }, \ - __VA_ARGS__, \ - { \ - .op = SWS_OP_WRITE, \ - .type = OUT, \ - .rw.elems = N_OUT, \ - }, {0} \ - }); \ - } while (0) +static void check_uop(const char *name, const SwsUOp *uop, + SwsCompMask mask_in, SwsCompMask mask_out, + const unsigned ranges[NB_PLANES]) +{ + Test t = { + .name = name, + .type_in = uop->type, + .type_out = uop->type, + }; -#define MK_RANGES(R) ((const unsigned[]) { R, R, R, R }) -#define CHECK_RANGE(NAME, RANGE, N_IN, N_OUT, IN, OUT, ...) \ - CHECK_RANGES(NAME, MK_RANGES(RANGE), N_IN, N_OUT, IN, OUT, __VA_ARGS__) + for (int i = 0; ranges && i < NB_PLANES; i++) + t.ranges[i] = ranges[i]; -#define CHECK(NAME, N_IN, N_OUT, IN, OUT, ...) \ - CHECK_RANGE(NAME, 0, N_IN, N_OUT, IN, OUT, __VA_ARGS__) + /* uops with non-standard output type conversions */ + switch (uop->uop) { + case SWS_UOP_TO_U8: t.type_out = SWS_PIXEL_U8; break; + case SWS_UOP_TO_U16: t.type_out = SWS_PIXEL_U16; break; + case SWS_UOP_TO_U32: t.type_out = SWS_PIXEL_U32; break; + case SWS_UOP_TO_F32: t.type_out = SWS_PIXEL_F32; break; + case SWS_UOP_EXPAND_PAIR: t.type_out = SWS_PIXEL_U16; break; + case SWS_UOP_EXPAND_QUAD: t.type_out = SWS_PIXEL_U32; break; + case SWS_UOP_READ_PLANAR_FH: + case SWS_UOP_READ_PLANAR_FV: + t.type_out = uop->par.filter.type; + break; + } -static inline int mask_num(const SwsCompMask mask) -{ - switch (mask) { - case SWS_COMP_ELEMS(1): return 1; - case SWS_COMP_ELEMS(2): return 2; - case SWS_COMP_ELEMS(3): return 3; - case SWS_COMP_ELEMS(4): return 4; - default: return 0; + /* Set up read/write metadata for rw uops */ + const int bits_in = ff_sws_pixel_type_size(t.type_in) * 8; + const int bits_out = ff_sws_pixel_type_size(t.type_out) * 8; + switch (uop->uop) { + case SWS_UOP_READ_PLANAR: + case SWS_UOP_READ_PLANAR_FH: + case SWS_UOP_READ_PLANAR_FV: + t.planes_in = uop->mask; + t.pixel_bits_in = bits_in; + break; + case SWS_UOP_WRITE_PLANAR: + t.planes_out = uop->mask; + t.pixel_bits_out = bits_out; + break; + case SWS_UOP_READ_PACKED: + t.planes_in = SWS_COMP_ELEMS(1); + t.pixel_bits_in = bits_in * SWS_COMP_COUNT(uop->mask); + break; + case SWS_UOP_WRITE_PACKED: + t.planes_out = SWS_COMP_ELEMS(1); + t.pixel_bits_out = bits_out * SWS_COMP_COUNT(uop->mask); + break; + case SWS_UOP_READ_NIBBLE: + t.planes_in = SWS_COMP_ELEMS(1); + t.pixel_bits_in = 4; + break; + case SWS_UOP_WRITE_NIBBLE: + t.planes_out = SWS_COMP_ELEMS(1); + t.pixel_bits_out = 4; + break; + case SWS_UOP_READ_BIT: + t.planes_in = SWS_COMP_ELEMS(1); + t.pixel_bits_in = 1; + break; + case SWS_UOP_WRITE_BIT: + t.planes_out = SWS_COMP_ELEMS(1); + t.pixel_bits_out = 1; + break; + case SWS_UOP_READ_PALETTE: + t.planes_in = SWS_COMP_ELEMS(2); + t.pixel_bits_in = 8; /* size of index */ + break; + } + + /* Add read uop if needed */ + if (!t.planes_in) { + t.planes_in = mask_in; + t.pixel_bits_in = bits_in; + t.uops[t.num_uops++] = (SwsUOp) { + .type = pixel_type_to_int(t.type_in), + .uop = SWS_UOP_READ_PLANAR, + .mask = SWS_COMP_ALL, /* extra planes are zero'd */ + }; + } + + /* Add the UOp itself */ + t.uops[t.num_uops++] = *uop; + + /* Add write uop if needed */ + if (!t.planes_out) { + t.planes_out = mask_out; + t.pixel_bits_out = bits_out; + t.uops[t.num_uops++] = (SwsUOp) { + .type = pixel_type_to_int(t.type_out), + .uop = SWS_UOP_WRITE_PLANAR, + .mask = SWS_COMP_ALL, /* extra planes are ignored */ + }; } + + run_test(&t); } -#define CHECK_MASK(NAME, MASK, RANGES, IN, OUT, ...) \ -do { \ - const SwsCompMask mask = (MASK); \ - const int num = mask_num(mask); \ - if (!num) \ - break; /* can't test these with current infrastructure */ \ - CHECK_RANGES(NAME, RANGES, 4, num, IN, OUT, __VA_ARGS__); \ -} while (0) +static void check_range(const char *name, const SwsUOp *uop, + const unsigned ranges[NB_PLANES]) +{ + /* Test all planes to ensure data remains untouched */ + return check_uop(name, uop, SWS_COMP_ALL, SWS_COMP_ALL, ranges); +} -static AVRational64 rndq(SwsPixelType t) +static void check_simple(const char *name, const SwsUOp *uop) { - const unsigned num = rnd(); - if (ff_sws_pixel_type_is_int(t)) { - const int bits = ff_sws_pixel_type_size(t) * 8; - const unsigned mask = UINT_MAX >> (32 - bits); - return (AVRational64) { num & mask, 1 }; - } else { - const unsigned den = rnd(); - return (AVRational64) { num, den ? den : 1 }; - } + return check_range(name, uop, NULL); } -static void check_read(const char *name, const SwsUOp *uop) +static void check_scalar(const char *name, SwsUOp *uop) { - SwsReadWriteMode mode; - switch (uop->uop) { - case SWS_UOP_READ_PACKED: - case SWS_UOP_READ_BIT: - case SWS_UOP_READ_NIBBLE: mode = SWS_RW_PACKED; break; - case SWS_UOP_READ_PLANAR: mode = SWS_RW_PLANAR; break; - case SWS_UOP_READ_PALETTE: mode = SWS_RW_PALETTE; break; - default: return; - } + uop->data.scalar = rndpx(uop->type); + check_simple(name, uop); +} - const int num = mask_num(uop->mask); - check_ops(name, NULL, (SwsOp[]) { - { - .op = SWS_OP_READ, - .type = uop->type, - .rw.elems = num, - .rw.mode = mode, - .rw.frac = uop->uop == SWS_UOP_READ_BIT ? 3 : - uop->uop == SWS_UOP_READ_NIBBLE ? 1 : 0, - }, { - .op = SWS_OP_WRITE, - .type = uop->type, - .rw.elems = num, - }, {0} - }); +static void check_vec4(const char *name, SwsUOp *uop) +{ + for (int i = 0; i < 4; i++) + uop->data.vec4[i] = rndpx(uop->type); + + return check_simple(name, uop); } -static void check_write(const char *name, const SwsUOp *uop) +#define MK_RANGES(R) ((const unsigned[]) { R, R, R, R }) + +static void check_read(const char *name, SwsUOp *uop) { - SwsReadWriteMode mode; - switch (uop->uop) { - case SWS_UOP_WRITE_BIT: - case SWS_UOP_WRITE_NIBBLE: - case SWS_UOP_WRITE_PACKED: mode = SWS_RW_PACKED; break; - case SWS_UOP_WRITE_PLANAR: mode = SWS_RW_PLANAR; break; - default: return; - } + check_uop(name, uop, uop->mask, uop->mask, NULL); +} +static void check_write(const char *name, const SwsUOp *uop) +{ const int frac = uop->uop == SWS_UOP_WRITE_BIT ? 3 : uop->uop == SWS_UOP_WRITE_NIBBLE ? 1 : 0; - const int num = mask_num(uop->mask); const int bits = 8 >> frac; const unsigned range = (1 << bits) - 1; - - check_ops(name, MK_RANGES(range), (SwsOp[]) { - { - .op = SWS_OP_READ, - .type = uop->type, - .rw.elems = num, - }, { - .op = SWS_OP_WRITE, - .type = uop->type, - .rw.elems = num, - .rw.mode = mode, - .rw.frac = frac, - }, {0} - }); + check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range)); } -static void check_filter(const char *name, const SwsUOp *uop) +static void check_filter(const char *name, SwsUOp *uop) { - const int num = mask_num(uop->mask); const bool is_vert = uop->uop == SWS_UOP_READ_PLANAR_FV; SwsFilterParams par = { @@ -485,36 +508,33 @@ static void check_filter(const char *name, const SwsUOp *uop) par.scaler = scalers[s]; for (par.src_size = 1; par.src_size <= par.dst_size; par.src_size <<= 1) { - SwsFilterWeights *filter; - if (ff_sws_filter_generate(NULL, &par, &filter) < 0) { + if (ff_sws_filter_generate(NULL, &par, &uop->data.kernel) < 0) { fail(); return; } char desc[256]; - snprintf(desc, sizeof(desc), "%s_%s_%d", name, filter->name, par.src_size); - check_ops(desc, NULL, (SwsOp[]) { - { - .op = SWS_OP_READ, - .type = uop->type, - .rw.elems = num, - .rw.filter = { - .op = is_vert ? SWS_OP_FILTER_V : SWS_OP_FILTER_H, - .kernel = filter, - .type = SWS_PIXEL_F32, - }, - }, { - .op = SWS_OP_WRITE, - .type = SWS_PIXEL_F32, - .rw.elems = num, - }, {0} - }); - - av_refstruct_unref(&filter); + snprintf(desc, sizeof(desc), "%s_%s_%d", name, + uop->data.kernel->name, par.src_size); + + check_read(desc, uop); + av_refstruct_unref(&uop->data.kernel); } } } +static void check_swizzle(const char *name, const SwsUOp *uop) +{ + /* Only check data equality in needed components; since the others + * could either remain untouched or contain garbage */ + check_uop(name, uop, SWS_COMP_ALL, uop->mask, NULL); +} + +static void check_expand_bit(const char *name, const SwsUOp *uop) +{ + check_range(name, uop, MK_RANGES(1)); +} + static void check_cast(const char *name, const SwsUOp *uop) { SwsPixelType dst; @@ -523,6 +543,8 @@ static void check_cast(const char *name, const SwsUOp *uop) case SWS_UOP_TO_U16: dst = SWS_PIXEL_U16; break; case SWS_UOP_TO_U32: dst = SWS_PIXEL_U32; break; case SWS_UOP_TO_F32: dst = SWS_PIXEL_F32; break; + case SWS_UOP_EXPAND_PAIR: dst = SWS_PIXEL_U16; break; + case SWS_UOP_EXPAND_QUAD: dst = SWS_PIXEL_U32; break; default: return; } @@ -532,115 +554,33 @@ static void check_cast(const char *name, const SwsUOp *uop) if (isize < osize || !ff_sws_pixel_type_is_int(dst)) range = 0; - CHECK_MASK(name, uop->mask, MK_RANGES(range), uop->type, dst, { - .op = SWS_OP_CONVERT, - .type = uop->type, - .convert.to = dst, - }); + check_uop(name, uop, uop->mask, uop->mask, MK_RANGES(range)); } -static void check_expand_bit(const char *name, const SwsUOp *uop) -{ - AVRational64 factor = { .den = 1 }; - switch (uop->type) { - case SWS_PIXEL_U8: factor.num = UINT8_MAX; break; - case SWS_PIXEL_U16: factor.num = UINT16_MAX; break; - case SWS_PIXEL_U32: factor.num = UINT32_MAX; break; - default: return; - } - - CHECK_MASK(name, uop->mask, MK_RANGES(1), uop->type, uop->type, { - .op = SWS_OP_SCALE, - .type = uop->type, - .scale.factor = factor, - }); -} - -static void check_expand(const char *name, const SwsUOp *uop) -{ - SwsPixelType dst = SWS_PIXEL_NONE; - switch (uop->uop) { - case SWS_UOP_EXPAND_PAIR: dst = SWS_PIXEL_U16; break; - case SWS_UOP_EXPAND_QUAD: dst = SWS_PIXEL_U32; break; - } - - av_assert0(uop->type == SWS_PIXEL_U8); - CHECK_MASK(name, uop->mask, NULL, uop->type, dst, { - .op = SWS_OP_CONVERT, - .type = uop->type, - .convert = { - .to = dst, - .expand = true, - }, - }); -} - -static void check_swizzle(const char *name, const SwsUOp *uop) -{ - const SwsMoveUOp *move = &uop->par.move; - int8_t idx[4 + 1] = { -1, 0, 1, 2, 3 }; - for (int n = 0; n < move->num_moves; n++) - idx[move->dst[n] + 1] = idx[move->src[n] + 1]; - - CHECK_MASK(name, uop->mask, NULL, uop->type, uop->type, { - .op = SWS_OP_SWIZZLE, - .type = uop->type, - .swizzle.in = { idx[1], idx[2], idx[3], idx[4] }, - }); -} - -static void check_scale(const char *name, const SwsUOp *uop) +static void check_scale(const char *name, SwsUOp *uop) { + const int bits = ff_sws_pixel_type_size(uop->type) * 8; + const unsigned max = UINT32_MAX >> (32 - bits); + SwsPixel scale = uop->data.scalar = rndpx(uop->type); unsigned range = 0; - AVRational64 scale; - - if (ff_sws_pixel_type_is_int(uop->type)) { - /* Ensure the result won't exceed the value range */ - const int bits = ff_sws_pixel_type_size(uop->type) * 8; - const unsigned max = UINT32_MAX >> (32 - bits); - scale = (AVRational64) { rnd() & (max >> 1), 1 }; - range = max / (scale.num ? scale.num : 1); - } else { - scale = rndq(uop->type); - } - - CHECK_MASK(name, uop->mask, MK_RANGES(range), uop->type, uop->type, { - .op = SWS_OP_SCALE, - .type = uop->type, - .scale.factor = scale, - }); -} -static void check_clamp(const char *name, const SwsUOp *uop) -{ - const SwsPixelType t = uop->type; - CHECK_MASK(name, uop->mask, NULL, t, t, { - .op = uop->uop == SWS_UOP_MIN ? SWS_OP_MIN : SWS_OP_MAX, - .type = t, - .clamp.limit = { rndq(t), rndq(t), rndq(t), rndq(t) }, - }); -} + /* Ensure the result won't exceed the value range */ + switch (uop->type) { + case SWS_PIXEL_U8: range = max / (scale.u8 ? scale.u8 : 1); break; + case SWS_PIXEL_U16: range = max / (scale.u16 ? scale.u16 : 1); break; + case SWS_PIXEL_U32: range = max / (scale.u32 ? scale.u32 : 1); break; + } -static void check_swap_bytes(const char *name, const SwsUOp *uop) -{ - CHECK_MASK(name, uop->mask, NULL, uop->type, uop->type, { - .op = SWS_OP_SWAP_BYTES, - .type = uop->type, - }); + check_range(name, uop, MK_RANGES(range)); } static void check_unpack(const char *name, const SwsUOp *uop) { const uint8_t *pat = uop->par.pack.pattern; - const int num = pat[3] ? 4 : 3; const int total = pat[0] + pat[1] + pat[2] + pat[3]; const unsigned range = UINT32_MAX >> (32 - total); - CHECK_RANGE(name, range, 1, num, uop->type, uop->type, { - .op = SWS_OP_UNPACK, - .type = uop->type, - .pack.pattern = { pat[0], pat[1], pat[2], pat[3] }, - }); + check_uop(name, uop, SWS_COMP(0), uop->mask, MK_RANGES(range)); } static void check_pack(const char *name, const SwsUOp *uop) @@ -651,112 +591,47 @@ static void check_pack(const char *name, const SwsUOp *uop) (1 << pat[2]) - 1, (1 << pat[3]) - 1, }; - CHECK_RANGES(name, ranges, 4, 1, uop->type, uop->type, { - .op = SWS_OP_PACK, - .type = uop->type, - .pack.pattern = { pat[0], pat[1], pat[2], pat[3] }, - }); -} - -static void check_shift(const char *name, const SwsUOp *uop) -{ - CHECK_MASK(name, uop->mask, NULL, uop->type, uop->type, { - .op = uop->uop == SWS_UOP_LSHIFT ? SWS_OP_LSHIFT : SWS_OP_RSHIFT, - .type = uop->type, - .shift.amount = uop->par.shift.amount, - }); + check_uop(name, uop, uop->mask, SWS_COMP(0), ranges); } -static void check_clear(const char *name, const SwsUOp *uop) +static void check_linear(const char *name, SwsUOp *uop) { - const SwsPixelType type = uop->type; - const int bits = ff_sws_pixel_type_size(type) * 8; - const unsigned range = UINT32_MAX >> (32 - bits); - const AVRational64 one = (AVRational64) { (int) range, 1}; - const AVRational64 zero = (AVRational64) { 0, 1}; - const AVRational64 val = { (rand() & 0x7F) | 1, 1 }; - - SwsClearOp clear = { .mask = uop->mask }; - for (int i = 0; i < 4; i++) { - if (SWS_COMP_TEST(uop->par.clear.one, i)) - clear.value[i] = one; - else if (SWS_COMP_TEST(uop->par.clear.zero, i)) - clear.value[i] = zero; - else - clear.value[i] = val; - } - - CHECK(name, 4, 4, type, type, { - .op = SWS_OP_CLEAR, - .type = type, - .clear = clear, - }); -} - -static void check_linear(const char *name, const SwsUOp *uop) -{ - const SwsPixelType type = uop->type; - av_assert0(!ff_sws_pixel_type_is_int(type)); - - SwsLinearOp lin; + const SwsLinearUOp *par = &uop->par.lin; for (int i = 0; i < 4; i++) { for (int j = 0; j < 5; j++) { - if (uop->par.lin.one & SWS_MASK(i, j)) - lin.m[i][j] = (AVRational64) { 1, 1 }; - else if (uop->par.lin.zero & SWS_MASK(i, j)) - lin.m[i][j] = (AVRational64) { 0, 1 }; + if (par->zero & SWS_MASK(i, j)) + uop->data.mat4[i][j] = constpx(uop->type, 0); + else if (par->one & SWS_MASK(i, j)) + uop->data.mat4[i][j] = constpx(uop->type, 1); else - lin.m[i][j] = rndq(type); + uop->data.mat4[i][j] = rndpx(uop->type); } } - lin.mask = ff_sws_linear_mask(&lin); - CHECK(name, 4, 4, type, type, { - .op = SWS_OP_LINEAR, - .type = type, - .lin = lin, - }); + return check_simple(name, uop); } -static void check_dither(const char *name, const SwsUOp *uop) +static void check_dither(const char *name, SwsUOp *uop) { - const SwsPixelType type = uop->type; - av_assert0(!ff_sws_pixel_type_is_int(type)); - - SwsDitherOp dither = { .size_log2 = uop->par.dither.size_log2 }; - const int size = 1 << dither.size_log2; - const uint8_t *y_offset = uop->par.dither.y_offset; - for (int i = 0; i < 4; i++) - dither.y_offset[i] = SWS_COMP_TEST(uop->mask, i) ? y_offset[i] : -1; + av_assert0(!ff_sws_pixel_type_is_int(uop->type)); - dither.matrix = av_refstruct_allocz(size * size * sizeof(*dither.matrix)); - if (!dither.matrix) { + const int size = 1 << uop->par.dither.size_log2; + const int height = ff_sws_dither_height(&uop->par.dither); + SwsPixel *matrix = av_refstruct_allocz(size * height * sizeof(*matrix)); + if (!matrix) { fail(); return; } for (int i = 0; i < size * size; i++) - dither.matrix[i] = rndq(type); - - CHECK(name, 4, 4, type, type, { - .op = SWS_OP_DITHER, - .type = type, - .dither = dither, - }); + matrix[i] = rndpx(uop->type); + memcpy(matrix + size * size, matrix, + size * (height - size) * sizeof(*matrix)); - av_refstruct_unref(&dither.matrix); -} + uop->data.ptr = matrix; + check_simple(name, uop); -static void check_add(const char *name, const SwsUOp *uop) -{ - /* SwsOp has no concept of SWS_OP_ADD; this is only used for - * SWS_OP_DITHER with a 1x1 dither matrix; so translate the uop */ - check_dither(name, &(SwsUOp) { - .uop = SWS_UOP_DITHER, - .type = uop->type, - .mask = uop->mask, - .par.dither.size_log2 = 0, - }); + av_refstruct_unref(&matrix); } #define CHECK_FUNCTION(CHECK, NAME, ...) \ @@ -784,23 +659,23 @@ void checkasm_check_sw_ops(void) CHECK_FOR(WRITE_BIT, check_write); CHECK_FOR(PERMUTE, check_swizzle); CHECK_FOR(COPY, check_swizzle); + CHECK_FOR(SWAP_BYTES, check_simple); CHECK_FOR(EXPAND_BIT, check_expand_bit); - CHECK_FOR(EXPAND_PAIR, check_expand); - CHECK_FOR(EXPAND_QUAD, check_expand); - CHECK_FOR(SWAP_BYTES, check_swap_bytes); + CHECK_FOR(EXPAND_PAIR, check_cast); + CHECK_FOR(EXPAND_QUAD, check_cast); CHECK_FOR(TO_U8, check_cast); CHECK_FOR(TO_U16, check_cast); CHECK_FOR(TO_U32, check_cast); CHECK_FOR(TO_F32, check_cast); CHECK_FOR(SCALE, check_scale); - CHECK_FOR(ADD, check_add); - CHECK_FOR(MIN, check_clamp); - CHECK_FOR(MAX, check_clamp); + CHECK_FOR(ADD, check_scalar); + CHECK_FOR(MIN, check_vec4); + CHECK_FOR(MAX, check_vec4); CHECK_FOR(UNPACK, check_unpack); CHECK_FOR(PACK, check_pack); - CHECK_FOR(LSHIFT, check_shift); - CHECK_FOR(RSHIFT, check_shift); - CHECK_FOR(CLEAR, check_clear); + CHECK_FOR(LSHIFT, check_simple); + CHECK_FOR(RSHIFT, check_simple); + CHECK_FOR(CLEAR, check_vec4); CHECK_FOR(LINEAR, check_linear); CHECK_FOR(DITHER, check_dither); } _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
