Commit 0ab25b5217 for aom
commit 0ab25b52179e747593899b7e2cc392a4ccb199dd
Author: Lin Zheng <linzhen@google.com>
Date: Tue Nov 3 19:19:31 2026 +0000
Fix nightly build failures
Fix unguarded AVX2 target attributes causing SIGILL on non-AVX2 hosts
Change-Id: I093693984924d89c82df7590fa72a6e64f0956c4
diff --git a/av1/common/av1_rtcd_defs.pl b/av1/common/av1_rtcd_defs.pl
index 10e611ab12..b906ba03fd 100644
--- a/av1/common/av1_rtcd_defs.pl
+++ b/av1/common/av1_rtcd_defs.pl
@@ -486,6 +486,9 @@ if (aom_config("CONFIG_AV1_ENCODER") eq "yes") {
add_proto qw/void av1_get_horver_correlation_full/, "const int16_t *diff, int stride, int w, int h, float *hcorr, float *vcorr";
specialize qw/av1_get_horver_correlation_full sse4_1 avx2 neon/;
+ add_proto qw/void av1_get_energy_distribution_finer/, "const int16_t *diff, int stride, int bw, int bh, float *hordist, float *verdist";
+ specialize qw/av1_get_energy_distribution_finer avx2/;
+
add_proto qw/void av1_interp_cubic_rate_dist/, "const double *p1, const double *p2, double x, double rate_dist_f[2]";
specialize qw/av1_interp_cubic_rate_dist sse2/;
diff --git a/av1/encoder/rd.c b/av1/encoder/rd.c
index cb5dfd4813..1263d35dcd 100644
--- a/av1/encoder/rd.c
+++ b/av1/encoder/rd.c
@@ -1460,9 +1460,6 @@ void av1_set_rd_speed_thresholds(AV1_COMP *cpi) {
rd->thresh_mult[THR_D45_PRED] = 2500;
}
-#if HAVE_AVX2 && (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
static inline void update_thr_fact(int (*factor_buf)[MAX_MODES],
THR_MODES best_mode_index,
THR_MODES mode_start, THR_MODES mode_end,
@@ -1483,9 +1480,6 @@ static inline void update_thr_fact(int (*factor_buf)[MAX_MODES],
}
}
-#if HAVE_AVX2 && (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
void av1_update_rd_thresh_fact(
const AV1_COMMON *const cm, int (*factor_buf)[MAX_MODES],
int use_adaptive_rd_thresh, BLOCK_SIZE bsize, THR_MODES best_mode_index,
diff --git a/av1/encoder/tx_search.c b/av1/encoder/tx_search.c
index e07ee5884f..3f31343f4c 100644
--- a/av1/encoder/tx_search.c
+++ b/av1/encoder/tx_search.c
@@ -11,6 +11,8 @@
#include <inttypes.h>
+#include "config/av1_rtcd.h"
+
#include "av1/common/cfl.h"
#include "av1/common/reconintra.h"
#include "av1/encoder/block.h"
@@ -23,9 +25,6 @@
#include "av1/encoder/tx_prune_model_weights.h"
#include "av1/encoder/tx_search.h"
#include "av1/encoder/txb_rdopt.h"
-#if HAVE_AVX2 && (defined(__x86_64__) || defined(_M_X64))
-#include <immintrin.h>
-#endif
#define PROB_THRESH_OFFSET_TX_TYPE 100
@@ -1457,169 +1456,24 @@ static inline float get_adaptive_thresholds(
return prune_2D_adaptive_thresholds[tx_size][pruning_aggressiveness];
}
-#if HAVE_AVX2 && defined(__x86_64__) && \
- (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
-static inline void get_energy_distribution_finer(const int16_t *diff,
- int stride, int bw, int bh,
- float *hordist,
- float *verdist) {
+void av1_get_energy_distribution_finer_c(const int16_t *diff, int stride,
+ int bw, int bh, float *hordist,
+ float *verdist) {
const int w_shift = bw <= 8 ? 0 : 1;
const int h_shift = bh <= 8 ? 0 : 1;
const int esq_w = bw >> w_shift;
const int esq_h = bh >> h_shift;
+ assert(esq_w <= 16);
+ assert(esq_h <= 16);
uint32_t col_sum[16] = { 0 };
uint32_t row_sum[16] = { 0 };
uint64_t total = 0;
- int i, j;
-
-#if HAVE_AVX2 && defined(__x86_64__) && \
- (defined(__GNUC__) || defined(__clang__))
- if (bw == 4) {
- __m128i v_col = _mm_setzero_si128();
- if (h_shift == 0) {
- for (i = 0; i < esq_h; i++) {
- const __m128i d = _mm_cvtepi16_epi32(
- _mm_loadl_epi64((const __m128i *)(diff + i * stride)));
- const __m128i sq = _mm_mullo_epi32(d, d);
- v_col = _mm_add_epi32(v_col, sq);
- const __m128i s1 = _mm_hadd_epi32(sq, sq);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- } else {
- for (i = 0; i < esq_h; i++) {
- const __m128i d0 =
- _mm_loadl_epi64((const __m128i *)(diff + (2 * i) * stride));
- const __m128i d1 =
- _mm_loadl_epi64((const __m128i *)(diff + (2 * i + 1) * stride));
- const __m128i d01 = _mm_unpacklo_epi16(d0, d1);
- const __m128i sq = _mm_madd_epi16(d01, d01);
- v_col = _mm_add_epi32(v_col, sq);
- const __m128i s1 = _mm_hadd_epi32(sq, sq);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- }
- _mm_storeu_si128((__m128i *)col_sum, v_col);
- const __m128i t1 = _mm_hadd_epi32(v_col, v_col);
- const __m128i t2 = _mm_hadd_epi32(t1, t1);
- total = (uint32_t)_mm_cvtsi128_si32(t2);
- } else if (bw == 8) {
- __m256i v_col = _mm256_setzero_si256();
- if (h_shift == 0) {
- for (i = 0; i < esq_h; i++) {
- const __m256i d = _mm256_cvtepi16_epi32(
- _mm_loadu_si128((const __m128i *)(diff + i * stride)));
- const __m256i sq = _mm256_mullo_epi32(d, d);
- v_col = _mm256_add_epi32(v_col, sq);
- const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
- _mm256_extracti128_si256(sq, 1));
- const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- } else {
- for (i = 0; i < esq_h; i++) {
- const __m128i d0 =
- _mm_loadu_si128((const __m128i *)(diff + (2 * i) * stride));
- const __m128i d1 =
- _mm_loadu_si128((const __m128i *)(diff + (2 * i + 1) * stride));
- const __m256i d0_256 = _mm256_cvtepi16_epi32(d0);
- const __m256i d1_256 = _mm256_cvtepi16_epi32(d1);
- const __m256i sq = _mm256_add_epi32(_mm256_mullo_epi32(d0_256, d0_256),
- _mm256_mullo_epi32(d1_256, d1_256));
- v_col = _mm256_add_epi32(v_col, sq);
- const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
- _mm256_extracti128_si256(sq, 1));
- const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- }
- _mm256_storeu_si256((__m256i *)col_sum, v_col);
- const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col),
- _mm256_extracti128_si256(v_col, 1));
- const __m128i t1 = _mm_hadd_epi32(c128, c128);
- const __m128i t2 = _mm_hadd_epi32(t1, t1);
- total = (uint32_t)_mm_cvtsi128_si32(t2);
- } else if (bw == 16) {
- __m256i v_col = _mm256_setzero_si256();
- if (h_shift == 0) {
- for (i = 0; i < esq_h; i++) {
- const __m256i d =
- _mm256_loadu_si256((const __m256i *)(diff + i * stride));
- const __m256i sq = _mm256_madd_epi16(d, d);
- v_col = _mm256_add_epi32(v_col, sq);
- const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
- _mm256_extracti128_si256(sq, 1));
- const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- } else {
- for (i = 0; i < esq_h; i++) {
- const __m256i d0 =
- _mm256_loadu_si256((const __m256i *)(diff + (2 * i) * stride));
- const __m256i d1 =
- _mm256_loadu_si256((const __m256i *)(diff + (2 * i + 1) * stride));
- const __m256i sq = _mm256_add_epi32(_mm256_madd_epi16(d0, d0),
- _mm256_madd_epi16(d1, d1));
- v_col = _mm256_add_epi32(v_col, sq);
- const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
- _mm256_extracti128_si256(sq, 1));
- const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- }
- _mm256_storeu_si256((__m256i *)col_sum, v_col);
- const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col),
- _mm256_extracti128_si256(v_col, 1));
- const __m128i t1 = _mm_hadd_epi32(c128, c128);
- const __m128i t2 = _mm_hadd_epi32(t1, t1);
- total = (uint32_t)_mm_cvtsi128_si32(t2);
- } else {
- __m256i v_col_lo = _mm256_setzero_si256();
- __m256i v_col_hi = _mm256_setzero_si256();
- for (i = 0; i < esq_h; i++) {
- const int16_t *r0 = diff + (2 * i) * stride;
- const int16_t *r1 = diff + (2 * i + 1) * stride;
- const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
- const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
- const __m256i d1_lo = _mm256_loadu_si256((const __m256i *)r1);
- const __m256i d1_hi = _mm256_loadu_si256((const __m256i *)(r1 + 16));
- const __m256i sq_lo = _mm256_add_epi32(_mm256_madd_epi16(d0_lo, d0_lo),
- _mm256_madd_epi16(d1_lo, d1_lo));
- const __m256i sq_hi = _mm256_add_epi32(_mm256_madd_epi16(d0_hi, d0_hi),
- _mm256_madd_epi16(d1_hi, d1_hi));
- v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
- v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
- const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
- const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
- _mm256_extracti128_si256(sq_sum, 1));
- const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
- const __m128i s2 = _mm_hadd_epi32(s1, s1);
- row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
- }
- _mm256_storeu_si256((__m256i *)col_sum, v_col_lo);
- _mm256_storeu_si256((__m256i *)(col_sum + 8), v_col_hi);
- const __m256i v_col_all = _mm256_add_epi32(v_col_lo, v_col_hi);
- const __m128i c128 = _mm_add_epi32(_mm256_castsi256_si128(v_col_all),
- _mm256_extracti128_si256(v_col_all, 1));
- const __m128i t1 = _mm_hadd_epi32(c128, c128);
- const __m128i t2 = _mm_hadd_epi32(t1, t1);
- total = (uint32_t)_mm_cvtsi128_si32(t2);
- }
-#else
- memset(col_sum, 0, esq_w * sizeof(col_sum[0]));
- memset(row_sum, 0, esq_h * sizeof(row_sum[0]));
- for (i = 0; i < bh; i++) {
+
+ for (int i = 0; i < bh; i++) {
const int16_t *cur_diff_row = diff + i * stride;
uint32_t r_sum = 0;
if (w_shift) {
- for (j = 0; j < bw; j += 2) {
+ for (int j = 0; j < bw; j += 2) {
const uint32_t sq =
(uint32_t)(cur_diff_row[j] * cur_diff_row[j] +
cur_diff_row[j + 1] * cur_diff_row[j + 1]);
@@ -1627,7 +1481,7 @@ static inline void get_energy_distribution_finer(const int16_t *diff,
r_sum += sq;
}
} else {
- for (j = 0; j < bw; j++) {
+ for (int j = 0; j < bw; j++) {
const uint32_t sq = (uint32_t)(cur_diff_row[j] * cur_diff_row[j]);
col_sum[j] += sq;
r_sum += sq;
@@ -1636,20 +1490,19 @@ static inline void get_energy_distribution_finer(const int16_t *diff,
row_sum[i >> h_shift] += r_sum;
total += r_sum;
}
-#endif
// Output hordist and verdist arrays are normalized 1D projections of esq
if (total == 0) {
- float hor_val = 1.0f / esq_w;
- for (j = 0; j < esq_w - 1; j++) hordist[j] = hor_val;
- float ver_val = 1.0f / esq_h;
- for (i = 0; i < esq_h - 1; i++) verdist[i] = ver_val;
+ const float hor_val = 1.0f / esq_w;
+ for (int j = 0; j < esq_w - 1; j++) hordist[j] = hor_val;
+ const float ver_val = 1.0f / esq_h;
+ for (int i = 0; i < esq_h - 1; i++) verdist[i] = ver_val;
return;
}
const float e_recip = 1.0f / (float)total;
- for (j = 0; j < esq_w - 1; j++) hordist[j] = (float)col_sum[j] * e_recip;
- for (i = 0; i < esq_h - 1; i++) verdist[i] = (float)row_sum[i] * e_recip;
+ for (int j = 0; j < esq_w - 1; j++) hordist[j] = (float)col_sum[j] * e_recip;
+ for (int i = 0; i < esq_h - 1; i++) verdist[i] = (float)row_sum[i] * e_recip;
}
static inline bool check_bit_mask(uint16_t mask, int val) {
@@ -1664,10 +1517,6 @@ static inline void unset_bit_mask(uint16_t *mask, int val) {
*mask &= ~(1 << val);
}
-#if HAVE_AVX2 && defined(__x86_64__) && \
- (defined(__GNUC__) || defined(__clang__))
-__attribute__((target("avx2")))
-#endif
static void prune_tx_2D(MACROBLOCK *x, BLOCK_SIZE bsize, TX_SIZE tx_size,
int blk_row, int blk_col, TxSetType tx_set_type,
TX_TYPE_PRUNE_MODE prune_2d_txfm_mode, int *txk_map,
@@ -1705,8 +1554,8 @@ static void prune_tx_2D(MACROBLOCK *x, BLOCK_SIZE bsize, TX_SIZE tx_size,
const struct macroblock_plane *const p = &x->plane[0];
const int diff_stride = block_size_wide[bsize];
const int16_t *diff = p->src_diff + 4 * blk_row * diff_stride + 4 * blk_col;
- get_energy_distribution_finer(diff, diff_stride, bw, bh, hfeatures,
- vfeatures);
+ av1_get_energy_distribution_finer(diff, diff_stride, bw, bh, hfeatures,
+ vfeatures);
av1_get_horver_correlation_full(diff, diff_stride, bw, bh,
&hfeatures[hfeatures_num - 1],
diff --git a/av1/encoder/x86/rdopt_avx2.c b/av1/encoder/x86/rdopt_avx2.c
index 71dd0ea16a..b7498d1a7b 100644
--- a/av1/encoder/x86/rdopt_avx2.c
+++ b/av1/encoder/x86/rdopt_avx2.c
@@ -252,3 +252,201 @@ void av1_get_horver_correlation_full_avx2(const int16_t *diff, int stride,
*vcorr = 1.0;
}
}
+
+static inline uint64_t xx_cvtsi128_si64(__m128i a) {
+#if AOM_ARCH_X86_64
+ return (uint64_t)_mm_cvtsi128_si64(a);
+#else
+ uint64_t tmp;
+ _mm_storel_epi64((__m128i *)&tmp, a);
+ return tmp;
+#endif
+}
+
+static inline uint64_t sum_u32x4_to_u64(__m128i v) {
+ const __m256i v64 = _mm256_cvtepu32_epi64(v);
+ const __m128i sum2 = _mm_add_epi64(_mm256_castsi256_si128(v64),
+ _mm256_extracti128_si256(v64, 1));
+ const __m128i sum1 = _mm_add_epi64(sum2, _mm_srli_si128(sum2, 8));
+ return xx_cvtsi128_si64(sum1);
+}
+
+static inline uint64_t sum_u32x8_to_u64(__m256i v) {
+ const __m256i lo64 = _mm256_cvtepu32_epi64(_mm256_castsi256_si128(v));
+ const __m256i hi64 = _mm256_cvtepu32_epi64(_mm256_extracti128_si256(v, 1));
+ const __m256i sum4 = _mm256_add_epi64(lo64, hi64);
+ const __m128i sum2 = _mm_add_epi64(_mm256_castsi256_si128(sum4),
+ _mm256_extracti128_si256(sum4, 1));
+ const __m128i sum1 = _mm_add_epi64(sum2, _mm_srli_si128(sum2, 8));
+ return xx_cvtsi128_si64(sum1);
+}
+
+void av1_get_energy_distribution_finer_avx2(const int16_t *diff, int stride,
+ int bw, int bh, float *hordist,
+ float *verdist) {
+ const int w_shift = bw <= 8 ? 0 : 1;
+ const int h_shift = bh <= 8 ? 0 : 1;
+ const int esq_w = bw >> w_shift;
+ const int esq_h = bh >> h_shift;
+ assert(esq_w <= 16);
+ assert(esq_h <= 16);
+ uint32_t col_sum[16] = { 0 };
+ uint32_t row_sum[16] = { 0 };
+ uint64_t total = 0;
+
+ if (bw == 4) {
+ __m128i v_col = _mm_setzero_si128();
+ if (h_shift == 0) {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m128i d = _mm_cvtepi16_epi32(
+ _mm_loadl_epi64((const __m128i *)(diff + i * stride)));
+ const __m128i sq = _mm_mullo_epi32(d, d);
+ v_col = _mm_add_epi32(v_col, sq);
+ const __m128i s1 = _mm_hadd_epi32(sq, sq);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ } else {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m128i d0 =
+ _mm_loadl_epi64((const __m128i *)(diff + (2 * i) * stride));
+ const __m128i d1 =
+ _mm_loadl_epi64((const __m128i *)(diff + (2 * i + 1) * stride));
+ const __m128i d01 = _mm_unpacklo_epi16(d0, d1);
+ const __m128i sq = _mm_madd_epi16(d01, d01);
+ v_col = _mm_add_epi32(v_col, sq);
+ const __m128i s1 = _mm_hadd_epi32(sq, sq);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ }
+ _mm_storeu_si128((__m128i *)col_sum, v_col);
+ total = sum_u32x4_to_u64(v_col);
+ } else if (bw == 8) {
+ __m256i v_col = _mm256_setzero_si256();
+ if (h_shift == 0) {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m256i d = _mm256_cvtepi16_epi32(
+ _mm_loadu_si128((const __m128i *)(diff + i * stride)));
+ const __m256i sq = _mm256_mullo_epi32(d, d);
+ v_col = _mm256_add_epi32(v_col, sq);
+ const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+ _mm256_extracti128_si256(sq, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ } else {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m128i d0 =
+ _mm_loadu_si128((const __m128i *)(diff + (2 * i) * stride));
+ const __m128i d1 =
+ _mm_loadu_si128((const __m128i *)(diff + (2 * i + 1) * stride));
+ const __m256i d0_256 = _mm256_cvtepi16_epi32(d0);
+ const __m256i d1_256 = _mm256_cvtepi16_epi32(d1);
+ const __m256i sq = _mm256_add_epi32(_mm256_mullo_epi32(d0_256, d0_256),
+ _mm256_mullo_epi32(d1_256, d1_256));
+ v_col = _mm256_add_epi32(v_col, sq);
+ const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+ _mm256_extracti128_si256(sq, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ }
+ _mm256_storeu_si256((__m256i *)col_sum, v_col);
+ total = sum_u32x8_to_u64(v_col);
+ } else if (bw == 16) {
+ __m256i v_col = _mm256_setzero_si256();
+ if (h_shift == 0) {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m256i d =
+ _mm256_loadu_si256((const __m256i *)(diff + i * stride));
+ const __m256i sq = _mm256_madd_epi16(d, d);
+ v_col = _mm256_add_epi32(v_col, sq);
+ const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+ _mm256_extracti128_si256(sq, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ } else {
+ for (int i = 0; i < esq_h; ++i) {
+ const __m256i d0 =
+ _mm256_loadu_si256((const __m256i *)(diff + (2 * i) * stride));
+ const __m256i d1 =
+ _mm256_loadu_si256((const __m256i *)(diff + (2 * i + 1) * stride));
+ const __m256i sq = _mm256_add_epi32(_mm256_madd_epi16(d0, d0),
+ _mm256_madd_epi16(d1, d1));
+ v_col = _mm256_add_epi32(v_col, sq);
+ const __m128i sq128 = _mm_add_epi32(_mm256_castsi256_si128(sq),
+ _mm256_extracti128_si256(sq, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ }
+ _mm256_storeu_si256((__m256i *)col_sum, v_col);
+ total = sum_u32x8_to_u64(v_col);
+ } else {
+ assert(bw == 32);
+ __m256i v_col_lo = _mm256_setzero_si256();
+ __m256i v_col_hi = _mm256_setzero_si256();
+ if (h_shift == 0) {
+ for (int i = 0; i < esq_h; ++i) {
+ const int16_t *r0 = diff + i * stride;
+ const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
+ const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
+ const __m256i sq_lo = _mm256_madd_epi16(d0_lo, d0_lo);
+ const __m256i sq_hi = _mm256_madd_epi16(d0_hi, d0_hi);
+ v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
+ v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
+ const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
+ const __m128i sq128 =
+ _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
+ _mm256_extracti128_si256(sq_sum, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ } else {
+ for (int i = 0; i < esq_h; ++i) {
+ const int16_t *r0 = diff + (2 * i) * stride;
+ const int16_t *r1 = diff + (2 * i + 1) * stride;
+ const __m256i d0_lo = _mm256_loadu_si256((const __m256i *)r0);
+ const __m256i d0_hi = _mm256_loadu_si256((const __m256i *)(r0 + 16));
+ const __m256i d1_lo = _mm256_loadu_si256((const __m256i *)r1);
+ const __m256i d1_hi = _mm256_loadu_si256((const __m256i *)(r1 + 16));
+ const __m256i sq_lo = _mm256_add_epi32(_mm256_madd_epi16(d0_lo, d0_lo),
+ _mm256_madd_epi16(d1_lo, d1_lo));
+ const __m256i sq_hi = _mm256_add_epi32(_mm256_madd_epi16(d0_hi, d0_hi),
+ _mm256_madd_epi16(d1_hi, d1_hi));
+ v_col_lo = _mm256_add_epi32(v_col_lo, sq_lo);
+ v_col_hi = _mm256_add_epi32(v_col_hi, sq_hi);
+ const __m256i sq_sum = _mm256_add_epi32(sq_lo, sq_hi);
+ const __m128i sq128 =
+ _mm_add_epi32(_mm256_castsi256_si128(sq_sum),
+ _mm256_extracti128_si256(sq_sum, 1));
+ const __m128i s1 = _mm_hadd_epi32(sq128, sq128);
+ const __m128i s2 = _mm_hadd_epi32(s1, s1);
+ row_sum[i] = (uint32_t)_mm_cvtsi128_si32(s2);
+ }
+ }
+ _mm256_storeu_si256((__m256i *)col_sum, v_col_lo);
+ _mm256_storeu_si256((__m256i *)(col_sum + 8), v_col_hi);
+ total = sum_u32x8_to_u64(v_col_lo) + sum_u32x8_to_u64(v_col_hi);
+ }
+
+ // Output hordist and verdist arrays are normalized 1D projections of esq
+ if (total == 0) {
+ const float hor_val = 1.0f / esq_w;
+ for (int j = 0; j < esq_w - 1; ++j) hordist[j] = hor_val;
+ const float ver_val = 1.0f / esq_h;
+ for (int i = 0; i < esq_h - 1; ++i) verdist[i] = ver_val;
+ return;
+ }
+
+ const float e_recip = 1.0f / (float)total;
+ for (int j = 0; j < esq_w - 1; ++j) hordist[j] = (float)col_sum[j] * e_recip;
+ for (int i = 0; i < esq_h - 1; ++i) verdist[i] = (float)row_sum[i] * e_recip;
+}
diff --git a/test/horver_correlation_test.cc b/test/horver_correlation_test.cc
index c9212de185..476d2e00e4 100644
--- a/test/horver_correlation_test.cc
+++ b/test/horver_correlation_test.cc
@@ -151,4 +151,122 @@ INSTANTIATE_TEST_SUITE_P(
AVX2, HorverTest, ::testing::Values(av1_get_horver_correlation_full_avx2));
#endif // HAVE_AVX2
+using EnergyDistFunc = void (*)(const int16_t *diff, int stride, int bw, int bh,
+ float *hordist, float *verdist);
+
+class EnergyDistributionTest : public ::testing::TestWithParam<EnergyDistFunc> {
+ public:
+ void SetUp() override {
+ data_buf_ = (int16_t *)aom_malloc(MAX_SB_SQUARE * sizeof(int16_t));
+ ASSERT_NE(data_buf_, nullptr);
+ target_func_ = GetParam();
+ }
+ void TearDown() override { aom_free(data_buf_); }
+ void RunRandomTest();
+ void RunZeroTest();
+ void RunExtremeValuesTest();
+
+ private:
+ EnergyDistFunc target_func_;
+ ACMRandom rng_;
+ int16_t *data_buf_;
+};
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EnergyDistributionTest);
+
+void EnergyDistributionTest::RunRandomTest() {
+ for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+ const int bw = tx_size_wide[tx_size];
+ const int bh = tx_size_high[tx_size];
+ if (bw > 32 || bh > 32) continue;
+ const int esq_w = bw <= 8 ? bw : bw / 2;
+ const int esq_h = bh <= 8 ? bh : bh / 2;
+ for (int iter = 0; iter < 500 && !HasFatalFailure(); ++iter) {
+ float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+ float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+ for (int i = 0; i < MAX_SB_SQUARE; ++i) {
+ data_buf_[i] = (rng_.Rand16() % (1 << 13)) - (1 << 12);
+ }
+
+ av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+ hdist_ref, vdist_ref);
+ API_REGISTER_STATE_CHECK(
+ target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+ for (int j = 0; j < esq_w - 1; ++j) {
+ ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j])
+ << "hordist mismatch at " << j << " (" << bw << "x" << bh << ")";
+ }
+ for (int i = 0; i < esq_h - 1; ++i) {
+ ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i])
+ << "verdist mismatch at " << i << " (" << bw << "x" << bh << ")";
+ }
+ }
+ }
+}
+
+void EnergyDistributionTest::RunZeroTest() {
+ memset(data_buf_, 0, MAX_SB_SQUARE * sizeof(int16_t));
+ for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+ const int bw = tx_size_wide[tx_size];
+ const int bh = tx_size_high[tx_size];
+ if (bw > 32 || bh > 32) continue;
+ const int esq_w = bw <= 8 ? bw : bw / 2;
+ const int esq_h = bh <= 8 ? bh : bh / 2;
+ float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+ float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+ av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+ hdist_ref, vdist_ref);
+ API_REGISTER_STATE_CHECK(
+ target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+ for (int j = 0; j < esq_w - 1; ++j) {
+ ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j]);
+ }
+ for (int i = 0; i < esq_h - 1; ++i) {
+ ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i]);
+ }
+ }
+}
+
+void EnergyDistributionTest::RunExtremeValuesTest() {
+ for (int i = 0; i < MAX_SB_SQUARE; ++i) {
+ data_buf_[i] = (1 << 12) - 1;
+ }
+ for (int tx_size = 0; tx_size < TX_SIZES_ALL; ++tx_size) {
+ const int bw = tx_size_wide[tx_size];
+ const int bh = tx_size_high[tx_size];
+ if (bw > 32 || bh > 32) continue;
+ const int esq_w = bw <= 8 ? bw : bw / 2;
+ const int esq_h = bh <= 8 ? bh : bh / 2;
+ float hdist_ref[16] = { 0 }, vdist_ref[16] = { 0 };
+ float hdist_test[16] = { 0 }, vdist_test[16] = { 0 };
+
+ av1_get_energy_distribution_finer_c(data_buf_, MAX_SB_SIZE, bw, bh,
+ hdist_ref, vdist_ref);
+ API_REGISTER_STATE_CHECK(
+ target_func_(data_buf_, MAX_SB_SIZE, bw, bh, hdist_test, vdist_test));
+
+ for (int j = 0; j < esq_w - 1; ++j) {
+ ASSERT_FLOAT_EQ(hdist_ref[j], hdist_test[j]);
+ }
+ for (int i = 0; i < esq_h - 1; ++i) {
+ ASSERT_FLOAT_EQ(vdist_ref[i], vdist_test[i]);
+ }
+ }
+}
+
+TEST_P(EnergyDistributionTest, RandomValues) { RunRandomTest(); }
+
+TEST_P(EnergyDistributionTest, ZeroValues) { RunZeroTest(); }
+
+TEST_P(EnergyDistributionTest, ExtremeValues) { RunExtremeValuesTest(); }
+
+#if HAVE_AVX2
+INSTANTIATE_TEST_SUITE_P(
+ AVX2, EnergyDistributionTest,
+ ::testing::Values(av1_get_energy_distribution_finer_avx2));
+#endif // HAVE_AVX2
+
} // namespace