Commit d971771eea for ffmpeg
commit d971771eea20420709df4d931bfeb9684817a25b
Author: Mark Thompson <sw@jkqxz.net>
Date: Sun Oct 25 13:32:52 2026 +0000
checkasm/apv_dsp: Improve test coverage
Test all bit depths, using random QP, qmatrix, and coefficients.
This makes the coefficients by sending a random residual through a
forward transform and quantisation in order to keep all of the
arithmetic within the encoder range. One coefficient is sometimes set
to a large value to exercise the dequantisation clipping, which is
otherwise never hit here.
Benchmarking continues to use a dense block similar to the old test to
ensure that results are comparable and represent the worst case.
diff --git a/tests/checkasm/apv_dsp.c b/tests/checkasm/apv_dsp.c
index 149c463763..efd79fa9d4 100644
--- a/tests/checkasm/apv_dsp.c
+++ b/tests/checkasm/apv_dsp.c
@@ -21,15 +21,96 @@
#include "checkasm.h"
+#include "libavutil/common.h"
#include "libavutil/mem_internal.h"
#include "libavcodec/apv_dsp.h"
-static void check_decode_transquant_10(void)
+static const int8_t apv_trans_matrix[8][8] = {
+ { 64, 64, 64, 64, 64, 64, 64, 64 },
+ { 89, 75, 50, 18, -18, -50, -75, -89 },
+ { 84, 35, -35, -84, -84, -35, 35, 84 },
+ { 75, -18, -89, -50, 50, 89, 18, -75 },
+ { 64, -64, -64, 64, 64, -64, -64, 64 },
+ { 50, -89, 18, 75, -75, -18, 89, -50 },
+ { 35, -84, 84, -35, -35, 84, -84, 35 },
+ { 18, -50, 75, -89, 89, -75, 50, -18 },
+};
+
+static const uint8_t apv_level_scale[6] = { 40, 45, 51, 57, 64, 71 };
+
+// Make a set of quantised coefficients by sending a random residual
+// through a forward transform and quantisation: this keeps every
+// intermediate in the ranges a real encoder would produce (completely
+// random coefficients overflow the use of 16x16->32 multiplies in the
+// SIMD paths).
+static void generate_block(int16_t *input, const int16_t *qmatrix,
+ int bit_depth, int qp_shift, int dense)
{
- LOCAL_ALIGNED_16( int16_t, input, [64]);
- LOCAL_ALIGNED_16( int16_t, qmatrix, [64]);
- LOCAL_ALIGNED_16(uint16_t, new_output, [64]);
- LOCAL_ALIGNED_16(uint16_t, ref_output, [64]);
+ // 64-bit intermediates here avoid rounding issues.
+ int64_t residual[8][8], tmp[8][8];
+ int amplitude;
+
+ if (dense) {
+ // Dense blocks for benchmarking are fully random and uniform
+ // over the range.
+ amplitude = (1 << bit_depth) - 1;
+ } else {
+ // Random blocks for testing bias the amplitude, both to get
+ // smaller typical coefficients (including many zeroes) and to
+ // make clipping on the ends of the range more likely.
+ amplitude = (3 << (bit_depth - 2)) >> (rnd() % 8);
+ }
+ for (int y = 0; y < 8; y++) {
+ for (int x = 0; x < 8; x++)
+ residual[y][x] =
+ (int64_t)(rnd() % (2 * amplitude + 1)) - amplitude;
+ }
+
+ // Forward transform rows.
+ for (int y = 0; y < 8; y++) {
+ for (int i = 0; i < 8; i++) {
+ int64_t sum = 0;
+ for (int j = 0; j < 8; j++)
+ sum += apv_trans_matrix[i][j] * residual[y][j];
+ tmp[y][i] = sum;
+ }
+ }
+ // Forward transform columns, renormalise, and apply quantisation
+ // to make the coefficient level.
+ for (int x = 0; x < 8; x++) {
+ for (int i = 0; i < 8; i++) {
+ int64_t coeff, step, level;
+
+ coeff = 0;
+ for (int j = 0; j < 8; j++)
+ coeff += apv_trans_matrix[i][j] * tmp[j][x];
+ coeff = ROUNDED_DIV(coeff,
+ INT64_C(1) << (bit_depth + 3));
+
+ step = qmatrix[i * 8 + x] << qp_shift;
+ level = ROUNDED_DIV(coeff * (INT64_C(1) << (bit_depth - 2)),
+ step);
+
+ input[i * 8 + x] = av_clip_int16(level);
+ }
+ }
+
+ // Levels made as above never reach the clip after dequantisation,
+ // so sometimes replace one coefficient with a huge value. A single
+ // coefficient like this does not overflow the intermediates.
+ if (!dense && rnd() % 8 == 0) {
+ int pos = rnd() % 64;
+ int level = 16384 + (rnd() % 16384);
+ input[pos] = rnd() % 2 ? level : -level;
+ }
+}
+
+static void check_decode_transquant(int bit_depth)
+{
+ LOCAL_ALIGNED_32( int16_t, input, [64]);
+ LOCAL_ALIGNED_32( int16_t, qmatrix, [64]);
+ LOCAL_ALIGNED_32(uint16_t, new_output, [64]);
+ LOCAL_ALIGNED_32(uint16_t, ref_output, [64]);
declare_func(void,
void *output,
@@ -39,23 +120,51 @@ static void check_decode_transquant_10(void)
int bit_depth,
int qp_shift);
- for (int i = 0; i < 64; i++) {
- // Any signed 14-bit integer.
- input[i] = rnd() % 16384 - 8192;
+ for (int n = 0; n < 256; n++) {
+ // Fully random QP for each block.
+ int qp = rnd() % (3 + 6 * bit_depth + 1);
+ int level_scale = apv_level_scale[qp % 6];
+ int qp_shift = qp / 6;
- // qmatrix input is premultiplied by level_scale, so
- // range is 1 to 255 * 71. Interesting values are all
- // at the low end of that, though.
- qmatrix[i] = 16; //rnd() % 16 + 16;
+ // The reference encoder uses a flat 16-everywhere matrix by
+ // default, while any other matrix must be supplied manually by
+ // the user. Use the default matrix half the time, and a
+ // completely random matrix otherwise.
+ int flat = rnd() % 2;
+ if (flat) {
+ for (int i = 0; i < 64; i++)
+ qmatrix[i] = level_scale * 16;
+ } else {
+ for (int i = 0; i < 64; i++)
+ qmatrix[i] = level_scale * (1 + rnd() % 255);
+ }
+
+ generate_block(input, qmatrix, bit_depth, qp_shift, 0);
+
+ call_ref(ref_output, 16, input, qmatrix, bit_depth, qp_shift);
+ call_new(new_output, 16, input, qmatrix, bit_depth, qp_shift);
+
+ if (memcmp(new_output, ref_output, 64 * sizeof(*ref_output))) {
+ fail();
+ break;
+ }
}
- call_ref(ref_output, 16, input, qmatrix, 10, 4);
- call_new(new_output, 16, input, qmatrix, 10, 4);
+ {
+ // Benchmark on a dense block with QP = 12 and a flat matrix.
+ // Almost all coefficients will be nonzero.
+ int qp_shift = 2;
+ for (int i = 0; i < 64; i++)
+ qmatrix[i] = apv_level_scale[0] * 16;
+ generate_block(input, qmatrix, bit_depth, qp_shift, 1);
- if (memcmp(new_output, ref_output, 64 * sizeof(*ref_output)))
- fail();
+ call_ref(ref_output, 16, input, qmatrix, bit_depth, qp_shift);
+ call_new(new_output, 16, input, qmatrix, bit_depth, qp_shift);
+ if (memcmp(new_output, ref_output, 64 * sizeof(*ref_output)))
+ fail();
- bench_new(new_output, 16, input, qmatrix, 10, 4);
+ bench_new(new_output, 16, input, qmatrix, bit_depth, qp_shift);
+ }
}
void checkasm_check_apv_dsp(void)
@@ -64,8 +173,11 @@ void checkasm_check_apv_dsp(void)
ff_apv_dsp_init(&dsp);
- if (check_func(dsp.decode_transquant, "decode_transquant_10"))
- check_decode_transquant_10();
+ for (int bit_depth = 10; bit_depth <= 16; bit_depth += 2) {
+ if (check_func(dsp.decode_transquant,
+ "decode_transquant_%d", bit_depth))
+ check_decode_transquant(bit_depth);
+ }
report("decode_transquant");
}