From e0ed3fa834a8d7854e58190146d930ec824043d2 Mon Sep 17 00:00:00 2001 From: Andreas Rheinhardt Date: Fri, 10 Apr 2026 10:25:22 +0200 Subject: [PATCH] tests/checkasm: Add snowdsp test Only inner_add_yblock for now. Hint: Said function uses a pointer to an array of pointers as parameter. The MMX version clobbers the array in such a way that calling the function repeatedly with the same arguments (as happens inside bench_new()) leads to buffer overflows and segfaults. Therefore CALL4 had to be overridden to restore the original pointers. This workaround will be removed soon when the MMX version is removed. Signed-off-by: Andreas Rheinhardt --- tests/checkasm/Makefile | 1 + tests/checkasm/checkasm.c | 3 + tests/checkasm/checkasm.h | 1 + tests/checkasm/snowdsp.c | 159 ++++++++++++++++++++++++++++++++++++++ tests/fate/checkasm.mak | 1 + 5 files changed, 165 insertions(+) create mode 100644 tests/checkasm/snowdsp.c diff --git a/tests/checkasm/Makefile b/tests/checkasm/Makefile index bf2d6b7ec2..55d2527047 100644 --- a/tests/checkasm/Makefile +++ b/tests/checkasm/Makefile @@ -50,6 +50,7 @@ AVCODECOBJS-$(CONFIG_PNG_DECODER) += png.o AVCODECOBJS-$(CONFIG_RV34DSP) += rv34dsp.o AVCODECOBJS-$(CONFIG_RV40_DECODER) += rv40dsp.o AVCODECOBJS-$(CONFIG_SBC_ENCODER) += sbcdsp.o +AVCODECOBJS-$(CONFIG_SNOW_DECODER) += snowdsp.o AVCODECOBJS-$(CONFIG_SVQ1_ENCODER) += svq1enc.o AVCODECOBJS-$(CONFIG_TAK_DECODER) += takdsp.o AVCODECOBJS-$(CONFIG_UTVIDEO_DECODER) += utvideodsp.o diff --git a/tests/checkasm/checkasm.c b/tests/checkasm/checkasm.c index 8629f26788..e863ff6eed 100644 --- a/tests/checkasm/checkasm.c +++ b/tests/checkasm/checkasm.c @@ -253,6 +253,9 @@ static const struct { #if CONFIG_SBC_ENCODER { "sbcdsp", checkasm_check_sbcdsp }, #endif + #if CONFIG_SNOW_DECODER + { "snowdsp", checkasm_check_snowdsp }, + #endif #if CONFIG_SVQ1_ENCODER { "svq1enc", checkasm_check_svq1enc }, #endif diff --git a/tests/checkasm/checkasm.h b/tests/checkasm/checkasm.h index 43c158e97b..72a1404163 100644 --- a/tests/checkasm/checkasm.h +++ b/tests/checkasm/checkasm.h @@ -141,6 +141,7 @@ void checkasm_check_sbrdsp(void); void checkasm_check_rv34dsp(void); void checkasm_check_rv40dsp(void); void checkasm_check_scene_sad(void); +void checkasm_check_snowdsp(void); void checkasm_check_svq1enc(void); void checkasm_check_synth_filter(void); void checkasm_check_sw_gbrp(void); diff --git a/tests/checkasm/snowdsp.c b/tests/checkasm/snowdsp.c new file mode 100644 index 0000000000..2edad643ad --- /dev/null +++ b/tests/checkasm/snowdsp.c @@ -0,0 +1,159 @@ +/* + * 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 +#include +#include +#include + +#include "libavutil/intreadwrite.h" +#include "libavutil/macros.h" +#include "libavutil/mem_internal.h" + +#include "libavcodec/snow.h" +#include "libavcodec/snow_dwt.h" + +#include "checkasm.h" + +#define randomize_buffer(buf) \ +do { \ + for (size_t k = 0; k < (sizeof(buf) & ~3); k += 4) \ + AV_WN32A((char*)buf + k, rnd()); \ + for (size_t k = sizeof(buf) & ~3; k < sizeof(buf); ++k) \ + ((char*)buf)[k] = rnd(); \ +} while (0) + +static void checkasm_check_inner_add_yblock(const SnowDWTContext *const snowdsp) +{ + enum { + LOG2_MAX_BLOCKSIZE = 4, + MAX_BLOCKSIZE = 1 << LOG2_MAX_BLOCKSIZE, + LOG2_MIN_BLOCKSIZE = 1, + MAX_STRIDE = 256, + }; + declare_func_emms(AV_CPU_FLAG_MMX, void, const uint8_t *obmc, const int obmc_stride, + uint8_t **block, int b_w, int b_h, int src_x, + int src_stride, IDWTELEM * const *lines, + int add, uint8_t *dst8); + DECLARE_ALIGNED(16, uint8_t, dst8_ref)[MAX_STRIDE * MAX_BLOCKSIZE]; + DECLARE_ALIGNED(16, uint8_t, dst8_new)[MAX_STRIDE * MAX_BLOCKSIZE]; + DECLARE_ALIGNED(16, uint8_t, block)[4][MAX_STRIDE * (MAX_BLOCKSIZE - 1) + MAX_BLOCKSIZE]; + DECLARE_ALIGNED(16, IDWTELEM, linebuf)[MAX_BLOCKSIZE][MAX_STRIDE]; + int inited = 0; + + for (int i = 0; i < 2; ++i) { + for (int j = LOG2_MIN_BLOCKSIZE; j <= LOG2_MAX_BLOCKSIZE; ++j) { + int b_w = 1 << j; + + if (!check_func(snowdsp->inner_add_yblock, "inner_add_yblock_%d%s", b_w, i ? "_border" : "")) + continue; + + const uint8_t *obmc = ff_obmc_tab[LOG2_MAX_BLOCKSIZE - j]; + int obmc_stride = 2 << j; + int b_h = b_w, mb_x; + int width = 1 + rnd() % MAX_STRIDE; + + if (!i) { + // Test the ordinary case of a complete block. + width = FFMAX(width, 2 * b_w); + int nb_complete_blocks = (width - b_w / 2) / b_w; + mb_x = 1 + rnd() % nb_complete_blocks; + } else { + // Test a boundary block. If width == b_w/2 mod b_w, + // there is no right boundary block, so use the left one. + mb_x = (width + b_w/2) % b_w && rnd() & 1 ? (width + b_w/2) / b_w : 0; + } + ptrdiff_t src_stride = FFALIGN(width + rnd() % (MAX_STRIDE - width + 1), 16); + int src_x = b_w*mb_x - b_w/2; + + if (src_x < 0) { + obmc -= src_x; + b_w += src_x; + src_x = 0; + } + if (src_x + b_w > width) { + b_w = width - src_x; + } + + uint8_t *dst8p_ref = dst8_ref + src_x; + uint8_t *dst8p_new = dst8_new + src_x; + unsigned rand = rnd(); + uint8_t *blocks[4] = { block[rand % 4], block[rand / 4 % 4], + block[rand / 16 % 4], block[rand / 64 % 4] }; + if (rnd() & 1) { // negate stride + dst8p_ref += (b_h - 1) * src_stride; + dst8p_new += (b_h - 1) * src_stride; + blocks[0] += (b_h - 1) * src_stride; + blocks[1] += (b_h - 1) * src_stride; + blocks[2] += (b_h - 1) * src_stride; + blocks[3] += (b_h - 1) * src_stride; + src_stride = -src_stride; + } + uint8_t *blocks_backup[4] = { blocks[0], blocks[1], + blocks[2], blocks[3] }; + IDWTELEM *lines[MAX_BLOCKSIZE]; + + for (int k = 0; k < b_h; ++k) + lines[k] = linebuf[rnd() % MAX_BLOCKSIZE]; + + if (!inited) { + inited = 1; + randomize_buffer(block); + randomize_buffer(dst8_ref); + for (size_t k = 0; k < FF_ARRAY_ELEMS(linebuf); ++k) { + for (size_t l = 0; l < FF_ARRAY_ELEMS(linebuf[0]); ++l) + linebuf[k][l] = sign_extend(rnd(), 15); + } + } + + memcpy(dst8_new, dst8_ref, sizeof(dst8_new)); + + call_ref(obmc, obmc_stride, blocks, b_w, b_h, src_x, src_stride, lines, 1, dst8p_ref); + memcpy(blocks, blocks_backup, sizeof(blocks));\ + call_new(obmc, obmc_stride, blocks, b_w, b_h, src_x, src_stride, lines, 1, dst8p_new); + + if (memcmp(dst8_ref, dst8_new, sizeof(dst8_new))) + fail(); + +#undef CALL4 +#define CALL4(...)\ + do {\ + memcpy(blocks, blocks_backup, sizeof(blocks));\ + tfunc(__VA_ARGS__); \ + memcpy(blocks, blocks_backup, sizeof(blocks));\ + tfunc(__VA_ARGS__); \ + memcpy(blocks, blocks_backup, sizeof(blocks));\ + tfunc(__VA_ARGS__); \ + memcpy(blocks, blocks_backup, sizeof(blocks));\ + tfunc(__VA_ARGS__); \ + } while (0) + + bench_new(obmc, obmc_stride, blocks, b_w, b_h, src_x, src_stride, lines, 1, dst8p_new); + } + } + report("inner_add_yblock"); +} + +void checkasm_check_snowdsp(void) +{ + SnowDWTContext snowdsp; + + ff_dwt_init(&snowdsp); + + checkasm_check_inner_add_yblock(&snowdsp); +} diff --git a/tests/fate/checkasm.mak b/tests/fate/checkasm.mak index 2fb1f693b8..b7392fa745 100644 --- a/tests/fate/checkasm.mak +++ b/tests/fate/checkasm.mak @@ -56,6 +56,7 @@ FATE_CHECKASM = fate-checkasm-aacencdsp \ fate-checkasm-rv40dsp \ fate-checkasm-sbcdsp \ fate-checkasm-scene_sad \ + fate-checkasm-snowdsp \ fate-checkasm-svq1enc \ fate-checkasm-synth_filter \ fate-checkasm-sw_gbrp \