Commit b2347b8175 for aom
commit b2347b8175a5b7d62cf9189fc298475a0d0d495b
Author: Frank Galligan <fgalligan@google.com>
Date: Fri Nov 6 11:48:17 2026 -0800
av1: Optimize txb_init_levels and block_error_qm
Improve AVX2 transform level initialization, QM block error gathering,
HoG cache accumulation, and R-D candidate sorting:
- Specialize av1_txb_init_levels_avx2 for each transform height with
compile-time padded strides and inline trailing TX_PAD_BOTTOM zero
stores, and fix unaligned 32-bit zero padding stores in SSE4.1/NEON.
- Pack 8 QM weights into two 32-bit halves in load_qm_weights_avx2
(vmovd + vpinsrd + vpmovzxbd) and share the squared coefficient
calculation in av1_block_error_qm_avx2.
- Accumulate cached HoG gradients in uint32_t before converting to
float and skip the unit luma scale loop.
- Simplify sort_rd to standard single-loop insertion sort.
Benchmarking results (incremental vs prev CL / cumulative vs baseline):
- Stock avifenc still-image (Default 1T): +0.00% / -18.41% encode time
- Stock avifenc still-image (Default 8T): -0.57% / -14.63% encode time
- 48-image multi-rate corpus (Full Suite 1T): +0.05% / -18.28% time
- 48-image multi-rate corpus (Full Suite 8T): -0.92% / -15.05% time
- Bit-exact output (1T and 8T)
TAG=agy
CONV=b929a630-a6cd-45a0-979c-1767c30178ee
Change-Id: I13870f94b27033f5033e6d5336136e3c9b989da8
diff --git a/av1/encoder/arm/encodetxb_neon.c b/av1/encoder/arm/encodetxb_neon.c
index 45e117bdb8..1b5a656c96 100644
--- a/av1/encoder/arm/encodetxb_neon.c
+++ b/av1/encoder/arm/encodetxb_neon.c
@@ -12,6 +12,7 @@
#include <arm_neon.h>
#include <assert.h>
#include <math.h>
+#include <string.h>
#include "config/aom_config.h"
@@ -80,7 +81,7 @@ void av1_txb_init_levels_neon(const tran_low_t *const coeff, const int width,
j += 16;
cf += 16;
} while (j < height);
- *(int32_t *)(ls + height) = 0;
+ memset(ls + height, 0, sizeof(*levels) * TX_PAD_HOR);
ls += stride;
i += 1;
} while (i < width);
diff --git a/av1/encoder/intra_mode_search_utils.h b/av1/encoder/intra_mode_search_utils.h
index c7f07dbdd5..d64cc8c831 100644
--- a/av1/encoder/intra_mode_search_utils.h
+++ b/av1/encoder/intra_mode_search_utils.h
@@ -442,24 +442,29 @@ static inline void generate_hog_using_gradient_cache(const MACROBLOCK *x,
plane * MAX_SB_SQUARE +
block_offset_in_grad_cache;
- // Retrieve the cached gradient information and generate the histogram.
+ // Accumulate cached gradients in integers first to avoid per-pixel
+ // int-to-float conversions inside the inner loop.
+ uint32_t int_hist[HOG_BINS] = { 0 };
for (int r = 1; r < rows - 1; ++r) {
+ const PixelLevelGradientInfo *const row_info = grad_info_blk + r * sb_width;
for (int c = 1; c < cols - 1; ++c) {
- const uint16_t abs_dx_abs_dy_sum =
- grad_info_blk[r * sb_width + c].abs_dx_abs_dy_sum;
+ const uint16_t abs_dx_abs_dy_sum = row_info[c].abs_dx_abs_dy_sum;
if (!abs_dx_abs_dy_sum) continue;
total += abs_dx_abs_dy_sum;
- const bool is_dx_zero = grad_info_blk[r * sb_width + c].is_dx_zero;
- if (is_dx_zero) {
- hist[0] += abs_dx_abs_dy_sum >> 1;
- hist[HOG_BINS - 1] += abs_dx_abs_dy_sum >> 1;
+ if (row_info[c].is_dx_zero) {
+ const uint32_t half = abs_dx_abs_dy_sum >> 1;
+ int_hist[0] += half;
+ int_hist[HOG_BINS - 1] += half;
} else {
- const int8_t idx = grad_info_blk[r * sb_width + c].hist_bin_idx;
+ const int8_t idx = row_info[c].hist_bin_idx;
assert(idx >= 0 && idx < HOG_BINS);
- hist[idx] += abs_dx_abs_dy_sum;
+ int_hist[idx] += abs_dx_abs_dy_sum;
}
}
}
+ for (int b = 0; b < HOG_BINS; ++b) {
+ hist[b] = (float)int_hist[b];
+ }
normalize_hog(total, hist);
}
@@ -489,8 +494,11 @@ static inline void collect_hog_data(const MACROBLOCK *x, BLOCK_SIZE bsize,
}
// Scale the hog so the luma and chroma are on the same scale
- for (int b = 0; b < HOG_BINS; ++b) {
- hog[b] *= (1 + ss_x) * (1 + ss_y);
+ if (ss_x | ss_y) {
+ const float scale = (float)((1 + ss_x) * (1 + ss_y));
+ for (int b = 0; b < HOG_BINS; ++b) {
+ hog[b] *= scale;
+ }
}
}
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c
index 05aef376cd..68505e483d 100644
--- a/av1/encoder/tx_search.c
+++ b/av1/encoder/tx_search.c
@@ -1067,27 +1067,17 @@ static const int mul_factors[5] = { 80, 80, 70, 50, 30 }; // scale 100
// R-D costs are sorted in ascending order.
static inline void sort_rd(int64_t rds[], int txk[], int len) {
- int i, j, k;
-
- for (i = 1; i <= len - 1; ++i) {
- for (j = 0; j < i; ++j) {
- if (rds[j] > rds[i]) {
- int64_t temprd;
- int tempi;
-
- temprd = rds[i];
- tempi = txk[i];
-
- for (k = i; k > j; k--) {
- rds[k] = rds[k - 1];
- txk[k] = txk[k - 1];
- }
-
- rds[j] = temprd;
- txk[j] = tempi;
- break;
- }
- }
+ for (int i = 1; i < len; ++i) {
+ const int64_t temprd = rds[i];
+ const int tempi = txk[i];
+ int j = i;
+ while (j > 0 && rds[j - 1] > temprd) {
+ rds[j] = rds[j - 1];
+ txk[j] = txk[j - 1];
+ --j;
+ }
+ rds[j] = temprd;
+ txk[j] = tempi;
}
}
diff --git a/av1/encoder/x86/encodetxb_avx2.c b/av1/encoder/x86/encodetxb_avx2.c
index ce61df7ffa..9bc652fbe0 100644
--- a/av1/encoder/x86/encodetxb_avx2.c
+++ b/av1/encoder/x86/encodetxb_avx2.c
@@ -23,24 +23,21 @@
void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
const int height, uint8_t *const levels) {
- const int stride = height + TX_PAD_HOR;
+ assert(TX_PAD_HOR == 4 && TX_PAD_BOTTOM == 4);
const __m256i y_zeros = _mm256_setzero_si256();
+ const __m128i zero = _mm256_castsi256_si128(y_zeros);
- // Zero bottom padding using up to five unrolled stores.
- const int32_t bottom_len = sizeof(*levels) * (TX_PAD_BOTTOM * stride);
- uint8_t *const bottom_buf_end = levels + (width + TX_PAD_BOTTOM) * stride;
- const int num_stores = (bottom_len + 31) >> 5;
- assert(num_stores >= 1 && num_stores <= 5);
- yy_storeu_256(bottom_buf_end - 32, y_zeros);
- if (num_stores > 1) yy_storeu_256(bottom_buf_end - 64, y_zeros);
- if (num_stores > 2) yy_storeu_256(bottom_buf_end - 96, y_zeros);
- if (num_stores > 3) yy_storeu_256(bottom_buf_end - 128, y_zeros);
- if (num_stores > 4) yy_storeu_256(bottom_buf_end - 160, y_zeros);
-
- int i = 0;
uint8_t *ls = levels;
const tran_low_t *cf = coeff;
+
+ // Specialize each transform height with its constant padded stride
+ // (height + TX_PAD_HOR) and zero the TX_PAD_BOTTOM (4) trailing rows directly
+ // after the loop.
if (height == 4) {
+ assert(width > 0 && width % 4 == 0);
+ // Padded stride = 4 + 4 = 8; 4 rows of 4 coeffs + 4 zero-pad bytes per
+ // iter.
+ uint8_t *const ls_end = levels + width * 8;
do {
const __m256i c0 = yy_loadu_256(cf);
const __m256i c1 = yy_loadu_256(cf + 8);
@@ -51,9 +48,13 @@ void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
yy_storeu_256(ls, res);
ls += 32;
cf += 16;
- i += 4;
- } while (i < width);
+ } while (ls < ls_end);
+ // Zero 4 bottom padding rows (4 * 8 = 32 bytes).
+ yy_storeu_256(ls, y_zeros);
} else if (height == 8) {
+ assert(width > 0 && width % 4 == 0);
+ // Padded stride = 8 + 4 = 12; 4 rows per iteration (4 * 12 = 48 bytes).
+ uint8_t *const ls_end = levels + width * 12;
do {
const __m256i coeffA = yy_loadu_256(cf);
const __m256i coeffB = yy_loadu_256(cf + 8);
@@ -68,19 +69,25 @@ void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
const __m256i res = _mm256_shuffle_epi32(res_, 0xd8);
const __m128i res0 = _mm256_castsi256_si128(res);
const __m128i res1 = _mm256_extracti128_si256(res, 1);
+
xx_storel_64(ls, res0);
- *(int32_t *)(ls + height) = 0;
- xx_storel_64(ls + stride, _mm_srli_si128(res0, 8));
- *(int32_t *)(ls + height + stride) = 0;
- xx_storel_64(ls + stride * 2, res1);
- *(int32_t *)(ls + height + stride * 2) = 0;
- xx_storel_64(ls + stride * 3, _mm_srli_si128(res1, 8));
- *(int32_t *)(ls + height + stride * 3) = 0;
+ xx_storel_32(ls + 8, zero);
+ xx_storel_64(ls + 12, _mm_srli_si128(res0, 8));
+ xx_storel_32(ls + 20, zero);
+ xx_storel_64(ls + 24, res1);
+ xx_storel_32(ls + 32, zero);
+ xx_storel_64(ls + 36, _mm_srli_si128(res1, 8));
+ xx_storel_32(ls + 44, zero);
cf += 32;
- ls += stride << 2;
- i += 4;
- } while (i < width);
+ ls += 48;
+ } while (ls < ls_end);
+ // Zero 4 bottom padding rows (4 * 12 = 48 bytes).
+ yy_storeu_256(ls, y_zeros);
+ xx_storeu_128(ls + 32, zero);
} else if (height == 16) {
+ assert(width > 0 && width % 2 == 0);
+ // Padded stride = 16 + 4 = 20; 2 rows per iteration (2 * 20 = 40 bytes).
+ uint8_t *const ls_end = levels + width * 20;
do {
const __m256i coeffA = yy_loadu_256(cf);
const __m256i coeffB = yy_loadu_256(cf + 8);
@@ -93,15 +100,22 @@ void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
const __m256i absABCD = _mm256_packs_epi16(absAB, absCD);
const __m256i res_ = _mm256_permute4x64_epi64(absABCD, 0xd8);
const __m256i res = _mm256_shuffle_epi32(res_, 0xd8);
+
xx_storeu_128(ls, _mm256_castsi256_si128(res));
- xx_storeu_128(ls + stride, _mm256_extracti128_si256(res, 1));
+ xx_storeu_128(ls + 20, _mm256_extracti128_si256(res, 1));
cf += 32;
- *(int32_t *)(ls + height) = 0;
- *(int32_t *)(ls + stride + height) = 0;
- ls += stride << 1;
- i += 2;
- } while (i < width);
+ xx_storel_32(ls + 16, zero);
+ xx_storel_32(ls + 36, zero);
+ ls += 40;
+ } while (ls < ls_end);
+ // Zero 4 bottom padding rows (4 * 20 = 80 bytes).
+ yy_storeu_256(ls, y_zeros);
+ yy_storeu_256(ls + 32, y_zeros);
+ xx_storeu_128(ls + 64, zero);
} else {
+ assert(height == 32 && width > 0);
+ // Padded stride = 32 + 4 = 36; 1 row per iteration (36 bytes).
+ uint8_t *const ls_end = levels + width * 36;
do {
const __m256i coeffA = yy_loadu_256(cf);
const __m256i coeffB = yy_loadu_256(cf + 8);
@@ -114,11 +128,17 @@ void av1_txb_init_levels_avx2(const tran_low_t *const coeff, const int width,
const __m256i absABCD = _mm256_packs_epi16(absAB, absCD);
const __m256i res_ = _mm256_permute4x64_epi64(absABCD, 0xd8);
const __m256i res = _mm256_shuffle_epi32(res_, 0xd8);
+
yy_storeu_256(ls, res);
cf += 32;
- *(int32_t *)(ls + height) = 0;
- ls += stride;
- i += 1;
- } while (i < width);
+ xx_storel_32(ls + 32, zero);
+ ls += 36;
+ } while (ls < ls_end);
+ // Zero 4 bottom padding rows (4 * 36 = 144 bytes).
+ yy_storeu_256(ls, y_zeros);
+ yy_storeu_256(ls + 32, y_zeros);
+ yy_storeu_256(ls + 64, y_zeros);
+ yy_storeu_256(ls + 96, y_zeros);
+ xx_storeu_128(ls + 128, zero);
}
}
diff --git a/av1/encoder/x86/encodetxb_sse4.c b/av1/encoder/x86/encodetxb_sse4.c
index 081e8265b2..48f3a45c0d 100644
--- a/av1/encoder/x86/encodetxb_sse4.c
+++ b/av1/encoder/x86/encodetxb_sse4.c
@@ -76,7 +76,7 @@ void av1_txb_init_levels_sse4_1(const tran_low_t *const coeff, const int width,
j += 16;
cf += 16;
} while (j < height);
- *(int32_t *)(ls + height) = 0;
+ xx_storel_32(ls + height, zeros);
ls += stride;
i += 1;
} while (i < width);
diff --git a/av1/encoder/x86/error_intrin_avx2.c b/av1/encoder/x86/error_intrin_avx2.c
index 6b514718d0..e5c8c0651a 100644
--- a/av1/encoder/x86/error_intrin_avx2.c
+++ b/av1/encoder/x86/error_intrin_avx2.c
@@ -210,6 +210,27 @@ int64_t av1_block_error_avx2(const tran_low_t *coeff, const tran_low_t *dqcoeff,
return sse;
}
+// Gather 8 quantization matrix weights indexed by scan_ptr[0..7] into two
+// 32-bit halves via _mm_cvtsi32_si128 + _mm_insert_epi32 and zero-extend to 8
+// 32-bit lanes with _mm256_cvtepu8_epi32 (preserving 32-bit x86 builds).
+static inline __m256i load_qm_weights_avx2(const qm_val_t *qmatrix,
+ const int16_t *scan_ptr) {
+ const uint32_t w01 =
+ (uint32_t)qmatrix[scan_ptr[0]] | ((uint32_t)qmatrix[scan_ptr[1]] << 8);
+ const uint32_t w23 = ((uint32_t)qmatrix[scan_ptr[2]] << 16) |
+ ((uint32_t)qmatrix[scan_ptr[3]] << 24);
+ const uint32_t lo = w01 | w23;
+
+ const uint32_t w45 =
+ (uint32_t)qmatrix[scan_ptr[4]] | ((uint32_t)qmatrix[scan_ptr[5]] << 8);
+ const uint32_t w67 = ((uint32_t)qmatrix[scan_ptr[6]] << 16) |
+ ((uint32_t)qmatrix[scan_ptr[7]] << 24);
+ const uint32_t hi = w45 | w67;
+
+ return _mm256_cvtepu8_epi32(
+ _mm_insert_epi32(_mm_cvtsi32_si128((int)lo), (int)hi, 1));
+}
+
int64_t av1_block_error_qm_avx2(const tran_low_t *coeff,
const tran_low_t *dqcoeff, intptr_t block_size,
const qm_val_t *qmatrix, const int16_t *scan,
@@ -219,45 +240,37 @@ int64_t av1_block_error_qm_avx2(const tran_low_t *coeff,
__m256i sse_reg = _mm256_setzero_si256();
__m256i ssz_reg = _mm256_setzero_si256();
const __m256i round_qm = _mm256_set1_epi64x(1 << (2 * AOM_QM_BITS - 1));
+ const int16_t *const scan_end = scan + block_size;
- for (intptr_t i = 0; i < block_size; i += 8) {
+ for (; scan < scan_end; coeff += 8, dqcoeff += 8, scan += 8) {
// Load 8 transform coefficients and dequantized coefficients.
- const __m256i coeff_reg = _mm256_loadu_si256((const __m256i *)(coeff + i));
- const __m256i dqcoeff_reg =
- _mm256_loadu_si256((const __m256i *)(dqcoeff + i));
+ const __m256i coeff_reg = _mm256_loadu_si256((const __m256i *)coeff);
+ const __m256i dqcoeff_reg = _mm256_loadu_si256((const __m256i *)dqcoeff);
+ if (_mm256_testz_si256(dqcoeff_reg, dqcoeff_reg) &&
+ _mm256_testz_si256(coeff_reg, coeff_reg)) {
+ continue;
+ }
- // When all 8 dequantized coefficients are zero, sse and ssz accumulate the
- // same weighted squared coefficient energy.
+ // Compute weighted squared coefficient energy coeff^2 in 64-bit lanes.
+ const __m256i wt_reg = load_qm_weights_avx2(qmatrix, scan);
+ const __m256i cc = _mm256_mullo_epi32(coeff_reg, wt_reg);
+ const __m256i cc_hi = _mm256_srli_epi64(cc, 32);
+ const __m256i cc_lo_sq = _mm256_mul_epi32(cc, cc);
+ const __m256i cc_hi_sq = _mm256_mul_epi32(cc_hi, cc_hi);
+ const __m256i sq_lo = _mm256_srli_epi64(
+ _mm256_add_epi64(cc_lo_sq, round_qm), 2 * AOM_QM_BITS);
+ const __m256i sq_hi = _mm256_srli_epi64(
+ _mm256_add_epi64(cc_hi_sq, round_qm), 2 * AOM_QM_BITS);
+ const __m256i sq = _mm256_add_epi64(sq_lo, sq_hi);
+ ssz_reg = _mm256_add_epi64(ssz_reg, sq);
+
+ // When all 8 dequantized coefficients are zero, sse equals ssz for these 8
+ // lanes; otherwise compute weighted squared error (coeff - dqcoeff)^2.
if (_mm256_testz_si256(dqcoeff_reg, dqcoeff_reg)) {
- if (_mm256_testz_si256(coeff_reg, coeff_reg)) continue;
-
- const __m256i wt_reg = _mm256_setr_epi32(
- qmatrix[scan[i + 0]], qmatrix[scan[i + 1]], qmatrix[scan[i + 2]],
- qmatrix[scan[i + 3]], qmatrix[scan[i + 4]], qmatrix[scan[i + 5]],
- qmatrix[scan[i + 6]], qmatrix[scan[i + 7]]);
- const __m256i cc = _mm256_mullo_epi32(coeff_reg, wt_reg);
- const __m256i cc_hi = _mm256_srli_epi64(cc, 32);
- const __m256i cc_lo_sq = _mm256_mul_epi32(cc, cc);
- const __m256i cc_hi_sq = _mm256_mul_epi32(cc_hi, cc_hi);
-
- const __m256i sq_lo = _mm256_srli_epi64(
- _mm256_add_epi64(cc_lo_sq, round_qm), 2 * AOM_QM_BITS);
- const __m256i sq_hi = _mm256_srli_epi64(
- _mm256_add_epi64(cc_hi_sq, round_qm), 2 * AOM_QM_BITS);
- const __m256i sq = _mm256_add_epi64(sq_lo, sq_hi);
-
sse_reg = _mm256_add_epi64(sse_reg, sq);
- ssz_reg = _mm256_add_epi64(ssz_reg, sq);
} else {
- const __m256i wt_reg = _mm256_setr_epi32(
- qmatrix[scan[i + 0]], qmatrix[scan[i + 1]], qmatrix[scan[i + 2]],
- qmatrix[scan[i + 3]], qmatrix[scan[i + 4]], qmatrix[scan[i + 5]],
- qmatrix[scan[i + 6]], qmatrix[scan[i + 7]]);
const __m256i diff = _mm256_sub_epi32(coeff_reg, dqcoeff_reg);
const __m256i dd = _mm256_mullo_epi32(diff, wt_reg);
- const __m256i cc = _mm256_mullo_epi32(coeff_reg, wt_reg);
-
- // Compute weighted squared error (coeff - dqcoeff)^2 in 64-bit lanes.
const __m256i dd_hi = _mm256_srli_epi64(dd, 32);
const __m256i dd_lo_sq = _mm256_mul_epi32(dd, dd);
const __m256i dd_hi_sq = _mm256_mul_epi32(dd_hi, dd_hi);
@@ -266,16 +279,6 @@ int64_t av1_block_error_qm_avx2(const tran_low_t *coeff,
const __m256i err_hi = _mm256_srli_epi64(
_mm256_add_epi64(dd_hi_sq, round_qm), 2 * AOM_QM_BITS);
sse_reg = _mm256_add_epi64(sse_reg, _mm256_add_epi64(err_lo, err_hi));
-
- // Compute weighted squared coefficient energy coeff^2 in 64-bit lanes.
- const __m256i cc_hi = _mm256_srli_epi64(cc, 32);
- const __m256i cc_lo_sq = _mm256_mul_epi32(cc, cc);
- const __m256i cc_hi_sq = _mm256_mul_epi32(cc_hi, cc_hi);
- const __m256i sq_lo = _mm256_srli_epi64(
- _mm256_add_epi64(cc_lo_sq, round_qm), 2 * AOM_QM_BITS);
- const __m256i sq_hi = _mm256_srli_epi64(
- _mm256_add_epi64(cc_hi_sq, round_qm), 2 * AOM_QM_BITS);
- ssz_reg = _mm256_add_epi64(ssz_reg, _mm256_add_epi64(sq_lo, sq_hi));
}
}
@@ -291,14 +294,16 @@ int64_t av1_block_error_qm_avx2(const tran_low_t *coeff,
const __m128i ssz_reg128 = _mm_add_epi64(
_mm256_castsi256_si128(ssz_reg), _mm256_extracti128_si256(ssz_reg, 1));
- // Scale down for high bit-depth and store the results.
+ // Scale down for high bit-depth when bd > 8 and store the results.
int64_t sse, sqcoeff;
_mm_storel_epi64((__m128i *)&sse, sse_reg128);
_mm_storel_epi64((__m128i *)&sqcoeff, ssz_reg128);
- const int shift = 2 * (bd - 8);
- const int rounding = (1 << shift) >> 1;
- sse = (sse + rounding) >> shift;
- sqcoeff = (sqcoeff + rounding) >> shift;
+ if (bd > 8) {
+ const int shift = 2 * (bd - 8);
+ const int rounding = (1 << shift) >> 1;
+ sse = (sse + rounding) >> shift;
+ sqcoeff = (sqcoeff + rounding) >> shift;
+ }
*ssz = sqcoeff;
return sse;
diff --git a/test/encodetxb_test.cc b/test/encodetxb_test.cc
index 2fd3efd2a7..cfa7467e92 100644
--- a/test/encodetxb_test.cc
+++ b/test/encodetxb_test.cc
@@ -223,16 +223,23 @@ void EncodeTxbInitLevelTest::RunTest(av1_txb_init_levels_func test_func,
tran_low_t coeff[MAX_TX_SQUARE];
uint8_t levels_buf[2][TX_PAD_2D];
+ alignas(4) uint8_t levels_unaligned_buf[TX_PAD_2D + 1];
uint8_t *const levels0 = set_levels(levels_buf[0], height);
uint8_t *const levels1 = set_levels(levels_buf[1], height);
+ uint8_t *const levels_unaligned =
+ set_levels(levels_unaligned_buf + 1, height);
ACMRandom rnd(ACMRandom::DeterministicSeed());
for (int i = 0; i < width * height; i++) {
coeff[i] = rnd.Rand16Signed();
}
+ const uint8_t canary = rnd.Rand8();
+ levels_unaligned_buf[0] = canary;
for (int i = 0; i < TX_PAD_2D; i++) {
- levels_buf[0][i] = rnd.Rand8();
- levels_buf[1][i] = rnd.Rand8();
+ const uint8_t init_byte = rnd.Rand8();
+ levels_buf[0][i] = init_byte;
+ levels_buf[1][i] = init_byte;
+ levels_unaligned_buf[i + 1] = init_byte;
}
const int run_times = is_speed ? (width * height) * 10000 : 1;
aom_usec_timer timer;
@@ -249,6 +256,9 @@ void EncodeTxbInitLevelTest::RunTest(av1_txb_init_levels_func test_func,
if (is_speed) {
printf("init %3dx%-3d:%7.2f/%7.2fns", width, height, t1, t2);
printf("(%3.2f)\n", t1 / t2);
+ } else {
+ API_REGISTER_STATE_CHECK(test_func(coeff, width, height, levels_unaligned));
+ ASSERT_EQ(canary, levels_unaligned_buf[0]);
}
const int stride = width + TX_PAD_HOR;
for (int r = 0; r < height + TX_PAD_VER; ++r) {
@@ -256,6 +266,11 @@ void EncodeTxbInitLevelTest::RunTest(av1_txb_init_levels_func test_func,
ASSERT_EQ(levels_buf[0][c + r * stride], levels_buf[1][c + r * stride])
<< "[" << r << "," << c << "] " << run_times << width << "x"
<< height;
+ if (!is_speed) {
+ ASSERT_EQ(levels_buf[0][c + r * stride],
+ levels_unaligned_buf[1 + c + r * stride])
+ << "unaligned [" << r << "," << c << "] " << width << "x" << height;
+ }
}
}
}
diff --git a/test/error_block_test.cc b/test/error_block_test.cc
index 62856603a7..49cb2af327 100644
--- a/test/error_block_test.cc
+++ b/test/error_block_test.cc
@@ -75,10 +75,11 @@ int64_t BlockErrorQmWrapper(const tran_low_t *coeff, const tran_low_t *dqcoeff,
static qm_val_t qmatrix[4096];
static int16_t scan[4096];
static const bool initialized = [&]() {
+ // Exercise full qm_val_t range [16, 242] (up to iwt_matrix_ref max) and a
+ // non-contiguous bijection within each 16-coefficient slice so every
+ // block_size prefix gathers permuted weights via scan_ptr[0..7].
for (int i = 0; i < 4096; ++i) {
- qmatrix[i] = static_cast<qm_val_t>(16 + ((i * 37) & 0x7f));
- // Permute scan within each 16-element chunk to exercise non-identity
- // gathered qmatrix indexing for all valid block sizes >= 16.
+ qmatrix[i] = static_cast<qm_val_t>(16 + ((i * 37) % 227));
scan[i] = static_cast<int16_t>((i & ~15) | ((i * 5 + 3) & 15));
}
return true;