From 4ef673590295a1012f0e22b37c5c45b9e35a33dc Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Fri, 24 Jul 2026 04:23:20 +0000 Subject: [PATCH 10/38] tests/checkasm: add swr_resample (resample_common/resample_linear) New tests/checkasm/swr_resample.c -- swresample previously had NO checkasm coverage (FATE only), blocking any future PPC/other-arch resample kernel work from having an oracle. Builds a real ResampleContext via the public swr_alloc/swr_init path (48000->44100 mono, forcing the actual resample path rather than the same-rate passthrough) for all 4 sample formats (S16P/S32P/FLTP/DBLP) x both linear_interp modes (0 selects resample_common, 1 selects resample_linear, the default). No synthetic filter_bank construction: reusing real swr_init gets genuine filter coefficients for free and matches how every other checkasm test relies on the library's own init-time CPU dispatch to distinguish ref vs. new, rather than building two separately-flagged contexts by hand. Wired into tests/checkasm/Makefile (new SWRESAMPLEOBJS section, CONFIG_SWRESAMPLE-gated, mirroring SWSCALEOBJS), checkasm.h/.c, and tests/fate/checkasm.mak per the existing "new tests must be listed there too" convention. VALIDATED: runs cleanly under the cross-PPC/qemu harness (qemu-ppc-static -cpu 7400) across multiple seeds; --bench confirms real dispatch and distinct timings across all 8 format/mode combinations. No PPC resample kernels exist yet, so this currently only exercises/benches the C reference -- that's expected, this commit is test infrastructure only (unblocks O6). Also surfaced (out of scope, not fixed here): running the full checkasm suite under this harness for the first time found 44 pre-existing failures in vp9dsp.mc (vertical MC filters, all _altivec) and 4 in vp8dsp.mc (put_pixels16/epel16_h6/v6/h6v6, _altivec) -- both from the earlier AltiVec VP8/VP9 decoder work, unrelated to this session's changes. Flagged in project memory for a follow-up session. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_0194bzLUrg6cVCQ7u4NNDpaB --- tests/checkasm/Makefile | 5 ++ tests/checkasm/checkasm.c | 3 + tests/checkasm/checkasm.h | 1 + tests/checkasm/swr_resample.c | 155 ++++++++++++++++++++++++++++++++++ tests/fate/checkasm.mak | 1 + 5 files changed, 165 insertions(+) create mode 100644 tests/checkasm/swr_resample.c diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile index c154d19..5c520e8 100644 --- a/tests/checkasm/Makefile +++ b/tests/checkasm/Makefile @@ -97,6 +97,11 @@ SWSCALEOBJS += sw_gbrp.o \ CHECKASMOBJS-$(CONFIG_SWSCALE) += $(SWSCALEOBJS) +# swresample tests +SWRESAMPLEOBJS += swr_resample.o + +CHECKASMOBJS-$(CONFIG_SWRESAMPLE) += $(SWRESAMPLEOBJS) + # libavutil tests AVUTILOBJS += aes.o AVUTILOBJS += av_tx.o diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c index a8cad59..2445439 100644 --- a/tests/checkasm/checkasm.c +++ b/tests/checkasm/checkasm.c @@ -298,6 +298,9 @@ static const CheckasmTest tests[] = { { "sw_yuv2yuv", checkasm_check_sw_yuv2yuv }, { "sw_ops", checkasm_check_sw_ops }, #endif +#if CONFIG_SWRESAMPLE + { "swr_resample", checkasm_check_swr_resample }, +#endif #if CONFIG_AVUTIL { "aes", checkasm_check_aes }, { "crc", checkasm_check_crc }, diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index b473c5d..17169cd 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -110,6 +110,7 @@ void checkasm_check_sw_xyz2rgb(void); void checkasm_check_sw_yuv2rgb(void); void checkasm_check_sw_yuv2yuv(void); void checkasm_check_sw_ops(void); +void checkasm_check_swr_resample(void); void checkasm_check_takdsp(void); void checkasm_check_utvideodsp(void); void checkasm_check_v210dec(void); diff --git a/tests/checkasm/swr_resample.c b/tests/checkasm/swr_resample.c new file mode 100644 index 0000000..abb701f --- /dev/null +++ b/tests/checkasm/swr_resample.c @@ -0,0 +1,155 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + * You should have received a copy of the GNU 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. + */ + +#include "checkasm.h" + +#include "libavutil/avassert.h" +#include "libavutil/channel_layout.h" +#include "libavutil/mem_internal.h" +#include "libavutil/opt.h" +#include "libavutil/samplefmt.h" + +#include "libswresample/swresample.h" +#include "libswresample/swresample_internal.h" +#include "libswresample/resample.h" + +#define SRC_LEN 256 +#define DST_LEN 256 + +/* checkasm re-invokes this whole test once per simulated CPU flag mask (see + * ext/src/checkasm.c's cpuflags loop) and distinguishes ref vs. new purely + * by comparing the dsp.resample_* function pointer each swr_init() call + * lands on -- exactly like every other checkasm test's init-time dispatch. + * So a single, real SwrContext per format/mode is enough; no need to force + * two separately-flagged contexts by hand. */ +static ResampleContext *make_resample_ctx(enum AVSampleFormat fmt, + int linear_interp) +{ + struct SwrContext *s = swr_alloc(); + AVChannelLayout mono = AV_CHANNEL_LAYOUT_MONO; + ResampleContext *c; + + if (!s) + return NULL; + + /* 48000 -> 44100: a real, non-integer rate ratio forces the resample + * path (not the passthrough taken when in/out rates match). */ + av_opt_set_chlayout(s, "in_chlayout", &mono, 0); + av_opt_set_chlayout(s, "out_chlayout", &mono, 0); + av_opt_set_sample_fmt(s, "in_sample_fmt", fmt, 0); + av_opt_set_sample_fmt(s, "out_sample_fmt", fmt, 0); + av_opt_set_int(s, "in_sample_rate", 48000, 0); + av_opt_set_int(s, "out_sample_rate", 44100, 0); + av_opt_set_int(s, "linear_interp", linear_interp, 0); + + if (swr_init(s) < 0) { + swr_free(&s); + return NULL; + } + + /* Detach the ResampleContext so it outlives this SwrContext: swr_free() + * would otherwise tear it down too, but call_ref/call_new need it (and + * its filter_bank) alive across the whole check_func block below. */ + c = s->resample; + s->resample = NULL; + swr_free(&s); + + return c; +} + +static void free_resample_ctx(ResampleContext *c) +{ + if (!c) + return; + av_freep(&c->filter_bank); + av_free(c); +} + +#define RANDOMIZE(TYPE, MAXVAL) do { \ + TYPE *p = (TYPE *)buf; \ + for (int i = 0; i < SRC_LEN; i++) \ + p[i] = (TYPE)((rnd() / (double)UINT_MAX - 0.5) * (MAXVAL)); \ + } while (0) + +static void check_one(enum AVSampleFormat fmt, const char *fmt_name, + int linear_interp, size_t elem_size) +{ + ResampleContext *c; + DECLARE_ALIGNED(32, uint8_t, src)[SRC_LEN * 8]; + DECLARE_ALIGNED(32, uint8_t, dst0)[DST_LEN * 8]; + DECLARE_ALIGNED(32, uint8_t, dst1)[DST_LEN * 8]; + void *buf = src; + declare_func(int, ResampleContext *c, void *dst, const void *src, + int n, int update_ctx); + + c = make_resample_ctx(fmt, linear_interp); + if (!c) + return; + + switch (fmt) { + case AV_SAMPLE_FMT_S16P: RANDOMIZE(int16_t, INT16_MAX); break; + case AV_SAMPLE_FMT_S32P: RANDOMIZE(int32_t, INT32_MAX); break; + case AV_SAMPLE_FMT_FLTP: RANDOMIZE(float, 1.0); break; + case AV_SAMPLE_FMT_DBLP: RANDOMIZE(double, 1.0); break; + default: av_assert0(0); + } + + { + void *fn = linear_interp ? (void *)c->dsp.resample_linear + : (void *)c->dsp.resample_common; + + if (check_func(fn, "swr_resample_%s_%s", fmt_name, + linear_interp ? "linear" : "common")) { + int consumed_ref, consumed_new; + + memset(dst0, 0, sizeof(dst0)); + memset(dst1, 0, sizeof(dst1)); + + consumed_ref = call_ref(c, dst0, src, SRC_LEN / 2, 0); + consumed_new = call_new(c, dst1, src, SRC_LEN / 2, 0); + + if (consumed_ref != consumed_new || + memcmp(dst0, dst1, DST_LEN * elem_size)) + fail(); + + bench_new(c, dst1, src, SRC_LEN / 2, 0); + } + } + + free_resample_ctx(c); +} + +void checkasm_check_swr_resample(void) +{ + static const struct { + enum AVSampleFormat fmt; + const char *name; + size_t elem_size; + } formats[] = { + { AV_SAMPLE_FMT_S16P, "s16p", 2 }, + { AV_SAMPLE_FMT_S32P, "s32p", 4 }, + { AV_SAMPLE_FMT_FLTP, "fltp", 4 }, + { AV_SAMPLE_FMT_DBLP, "dblp", 8 }, + }; + + for (int i = 0; i < FF_ARRAY_ELEMS(formats); i++) { + check_one(formats[i].fmt, formats[i].name, 0, formats[i].elem_size); + check_one(formats[i].fmt, formats[i].name, 1, formats[i].elem_size); + } + report("swr_resample"); +} diff --git a/tests/fate/checkasm.mak b/tests/fate/checkasm.mak index 249fb91..3456974 100644 --- a/tests/fate/checkasm.mak +++ b/tests/fate/checkasm.mak @@ -68,6 +68,7 @@ FATE_CHECKASM = fate-checkasm-aacencdsp \ fate-checkasm-sw_xyz2rgb \ fate-checkasm-sw_yuv2rgb \ fate-checkasm-sw_yuv2yuv \ + fate-checkasm-swr_resample \ fate-checkasm-takdsp \ fate-checkasm-utvideodsp \ fate-checkasm-v210dec \ -- 2.43.0