Add HBD data path for av1_block_error_avx2

- Add unit test for av1_block_error.
- Fix av1_dist_block logic for calling av1_block_error.

Change-Id: Id8a47ee113417360a29fc2334d9ca72b5793e2d7
diff --git a/av1/common/av1_rtcd_defs.pl b/av1/common/av1_rtcd_defs.pl
index 19f4204..6be2be0 100755
--- a/av1/common/av1_rtcd_defs.pl
+++ b/av1/common/av1_rtcd_defs.pl
@@ -278,6 +278,7 @@
     # the transform coefficients are held in 32-bit
     # values, so the assembler code for  av1_block_error can no longer be used.
     add_proto qw/int64_t av1_block_error/, "const tran_low_t *coeff, const tran_low_t *dqcoeff, intptr_t block_size, int64_t *ssz";
+    specialize qw/av1_block_error avx2/;
 
     add_proto qw/void av1_quantize_fp/, "const tran_low_t *coeff_ptr, intptr_t n_coeffs, int skip_block, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, const int16_t *quant_shift_ptr, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, const int16_t *iscan, const qm_val_t * qm_ptr, const qm_val_t *iqm_ptr";
 
@@ -310,6 +311,7 @@
     # the transform coefficients are held in 32-bit
     # values, so the assembler code for  av1_block_error can no longer be used.
     add_proto qw/int64_t av1_block_error/, "const tran_low_t *coeff, const tran_low_t *dqcoeff, intptr_t block_size, int64_t *ssz";
+    specialize qw/av1_block_error avx2/;
 
     add_proto qw/void av1_quantize_fp/, "const tran_low_t *coeff_ptr, intptr_t n_coeffs, int skip_block, const int16_t *zbin_ptr, const int16_t *round_ptr, const int16_t *quant_ptr, const int16_t *quant_shift_ptr, tran_low_t *qcoeff_ptr, tran_low_t *dqcoeff_ptr, const int16_t *dequant_ptr, uint16_t *eob_ptr, const int16_t *scan, const int16_t *iscan";
     specialize qw/av1_quantize_fp sse2/;
diff --git a/av1/encoder/rdopt.c b/av1/encoder/rdopt.c
index 396c0a0..8ca465c 100644
--- a/av1/encoder/rdopt.c
+++ b/av1/encoder/rdopt.c
@@ -1292,14 +1292,16 @@
                                    &this_sse) >>
                 shift;
 #endif  // CONFIG_HIGHBITDEPTH
-#elif CONFIG_HIGHBITDEPTH
-    const int bd = (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH) ? xd->bd : 8;
-    *out_dist =
-        av1_highbd_block_error(coeff, dqcoeff, buffer_length, &this_sse, bd) >>
-        shift;
-#else
-    *out_dist =
-        av1_block_error(coeff, dqcoeff, buffer_length, &this_sse) >> shift;
+#else   // !CONFIG_PVQ
+#if CONFIG_HIGHBITDEPTH
+    if (xd->cur_buf->flags & YV12_FLAG_HIGHBITDEPTH)
+      *out_dist = av1_highbd_block_error(coeff, dqcoeff, buffer_length,
+                                         &this_sse, xd->bd) >>
+                  shift;
+    else
+#endif
+      *out_dist =
+          av1_block_error(coeff, dqcoeff, buffer_length, &this_sse) >> shift;
 #endif  // CONFIG_PVQ
     *out_sse = this_sse >> shift;
   } else {
diff --git a/av1/encoder/x86/error_intrin_avx2.c b/av1/encoder/x86/error_intrin_avx2.c
index ae733a1..20ba414 100644
--- a/av1/encoder/x86/error_intrin_avx2.c
+++ b/av1/encoder/x86/error_intrin_avx2.c
@@ -14,7 +14,20 @@
 #include "./av1_rtcd.h"
 #include "aom/aom_integer.h"
 
-int64_t av1_block_error_avx2(const int16_t *coeff, const int16_t *dqcoeff,
+static INLINE void read_coeff(const tran_low_t *coeff, intptr_t offset,
+                              __m256i *c) {
+  const tran_low_t *addr = coeff + offset;
+#if CONFIG_HIGHBITDEPTH
+  const __m256i x0 = _mm256_loadu_si256((const __m256i *)addr);
+  const __m256i x1 = _mm256_loadu_si256((const __m256i *)addr + 1);
+  const __m256i y = _mm256_packs_epi32(x0, x1);
+  *c = _mm256_permute4x64_epi64(y, 0xD8);
+#else
+  *c = _mm256_loadu_si256((const __m256i *)addr);
+#endif
+}
+
+int64_t av1_block_error_avx2(const tran_low_t *coeff, const tran_low_t *dqcoeff,
                              intptr_t block_size, int64_t *ssz) {
   __m256i sse_reg, ssz_reg, coeff_reg, dqcoeff_reg;
   __m256i exp_dqcoeff_lo, exp_dqcoeff_hi, exp_coeff_lo, exp_coeff_hi;
@@ -22,16 +35,16 @@
   __m128i sse_reg128, ssz_reg128;
   int64_t sse;
   int i;
-  const __m256i zero_reg = _mm256_set1_epi16(0);
+  const __m256i zero_reg = _mm256_setzero_si256();
 
   // init sse and ssz registerd to zero
-  sse_reg = _mm256_set1_epi16(0);
-  ssz_reg = _mm256_set1_epi16(0);
+  sse_reg = _mm256_setzero_si256();
+  ssz_reg = _mm256_setzero_si256();
 
   for (i = 0; i < block_size; i += 16) {
     // load 32 bytes from coeff and dqcoeff
-    coeff_reg = _mm256_loadu_si256((const __m256i *)(coeff + i));
-    dqcoeff_reg = _mm256_loadu_si256((const __m256i *)(dqcoeff + i));
+    read_coeff(coeff, i, &coeff_reg);
+    read_coeff(dqcoeff, i, &dqcoeff_reg);
     // dqcoeff - coeff
     dqcoeff_reg = _mm256_sub_epi16(dqcoeff_reg, coeff_reg);
     // madd (dqcoeff - coeff)
diff --git a/test/block_error_test.cc b/test/block_error_test.cc
new file mode 100644
index 0000000..4364af4
--- /dev/null
+++ b/test/block_error_test.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2017, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "third_party/googletest/src/googletest/include/gtest/gtest.h"
+
+#include "./aom_config.h"
+#include "./av1_rtcd.h"
+
+#include "test/acm_random.h"
+#include "test/clear_system_state.h"
+#include "test/register_state_check.h"
+#include "test/util.h"
+
+namespace {
+using libaom_test::ACMRandom;
+
+typedef int64_t (*BlockErrorFunc)(const tran_low_t *coeff,
+                                  const tran_low_t *dqcoeff, intptr_t size,
+                                  int64_t *ssz);
+#if CONFIG_HIGHBITDEPTH
+typedef int64_t (*HbdBlockErrorFunc)(const tran_low_t *coeff,
+                                     const tran_low_t *dqcoeff, intptr_t size,
+                                     int64_t *ssz, int bd);
+#endif
+
+typedef std::tr1::tuple<BlockErrorFunc, BlockErrorFunc, TX_SIZE,
+                        aom_bit_depth_t>
+    BlockErrorParam;
+
+const int kTestNum = 10000;
+
+class BlockErrorTest : public ::testing::TestWithParam<BlockErrorParam> {
+ public:
+  BlockErrorTest()
+      : blk_err_ref_(GET_PARAM(0)), blk_err_(GET_PARAM(1)),
+        tx_size_(GET_PARAM(2)), bd_(GET_PARAM(3)) {}
+
+  virtual ~BlockErrorTest() {}
+
+  virtual void SetUp() {
+    const intptr_t block_size = getCoeffNum();
+    coeff_ = reinterpret_cast<tran_low_t *>(
+        aom_memalign(16, 2 * block_size * sizeof(tran_low_t)));
+  }
+
+  virtual void TearDown() {
+    aom_free(coeff_);
+    coeff_ = NULL;
+    libaom_test::ClearSystemState();
+  }
+
+  void BlockErrorRun(int testNum) {
+    int i;
+    int64_t error_ref, error;
+    int64_t sse_ref, sse;
+    const intptr_t block_size = getCoeffNum();
+    tran_low_t *dqcoeff = coeff_ + block_size;
+    for (i = 0; i < testNum; ++i) {
+      FillRandomData();
+
+      error_ref = blk_err_ref_(coeff_, dqcoeff, block_size, &sse_ref);
+      ASM_REGISTER_STATE_CHECK(error =
+                                   blk_err_(coeff_, dqcoeff, block_size, &sse));
+
+      EXPECT_EQ(error_ref, error) << "Error doesn't match on test: " << i;
+      EXPECT_EQ(sse_ref, sse) << "SSE doesn't match on test: " << i;
+    }
+  }
+
+  intptr_t getCoeffNum() { return tx_size_2d[tx_size_]; }
+
+  void FillRandomData() {
+    const intptr_t block_size = getCoeffNum();
+    tran_low_t *dqcoeff = coeff_ + block_size;
+    intptr_t i;
+    int16_t margin = 512;
+    for (i = 0; i < block_size; ++i) {
+      coeff_[i] = GetRandomNumWithRange(INT16_MIN + margin, INT16_MAX - margin);
+      dqcoeff[i] = coeff_[i] + GetRandomDeltaWithRange(margin);
+    }
+  }
+
+  void FillConstantData() {
+    const intptr_t block_size = getCoeffNum();
+    tran_low_t *dqcoeff = coeff_ + block_size;
+    intptr_t i;
+    for (i = 0; i < block_size; ++i) {
+      coeff_[i] = 5;
+      dqcoeff[i] = 7;
+    }
+  }
+
+  tran_low_t GetRandomNumWithRange(int16_t min, int16_t max) {
+    return clamp((int16_t)rnd_.Rand16(), min, max);
+  }
+
+  tran_low_t GetRandomDeltaWithRange(int16_t delta) {
+    tran_low_t value = (int16_t)rnd_.Rand16();
+    value %= delta;
+    return value;
+  }
+
+  BlockErrorFunc blk_err_ref_;
+  BlockErrorFunc blk_err_;
+  TX_SIZE tx_size_;
+  aom_bit_depth_t bd_;
+  ACMRandom rnd_;
+  tran_low_t *coeff_;
+};
+
+TEST_P(BlockErrorTest, BitExact) { BlockErrorRun(kTestNum); }
+
+using std::tr1::make_tuple;
+
+#if !CONFIG_HIGHBITDEPTH && HAVE_SSE2
+const BlockErrorParam kBlkErrParamArraySse2[] = { make_tuple(
+    &av1_block_error_c, &av1_block_error_sse2, TX_32X32, AOM_BITS_8) };
+INSTANTIATE_TEST_CASE_P(SSE2, BlockErrorTest,
+                        ::testing::ValuesIn(kBlkErrParamArraySse2));
+#endif
+
+#if HAVE_AVX2
+const BlockErrorParam kBlkErrParamArrayAvx2[] = { make_tuple(
+    &av1_block_error_c, &av1_block_error_avx2, TX_32X32, AOM_BITS_8) };
+INSTANTIATE_TEST_CASE_P(AVX2, BlockErrorTest,
+                        ::testing::ValuesIn(kBlkErrParamArrayAvx2));
+#endif
+}  // namespace
diff --git a/test/test.mk b/test/test.mk
index 45bb212..26d65c8 100644
--- a/test/test.mk
+++ b/test/test.mk
@@ -167,6 +167,7 @@
 LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += quantize_func_test.cc
 endif
 endif
+LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += block_error_test.cc
 
 LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += av1_inv_txfm_test.cc
 LIBAOM_TEST_SRCS-$(CONFIG_AV1_ENCODER) += av1_dct_test.cc