error_block_test.cc (10923B)
1 /* 2 * Copyright (c) 2016, Alliance for Open Media. All rights reserved. 3 * 4 * This source code is subject to the terms of the BSD 2 Clause License and 5 * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License 6 * was not distributed with this source code in the LICENSE file, you can 7 * obtain it at www.aomedia.org/license/software. If the Alliance for Open 8 * Media Patent License 1.0 was not distributed with this source code in the 9 * PATENTS file, you can obtain it at www.aomedia.org/license/patent. 10 */ 11 12 #include <cmath> 13 #include <cstdlib> 14 #include <string> 15 #include <tuple> 16 17 #include "gtest/gtest.h" 18 19 #include "config/aom_config.h" 20 #include "config/av1_rtcd.h" 21 22 #include "test/acm_random.h" 23 #include "test/register_state_check.h" 24 #include "test/util.h" 25 #include "av1/common/entropy.h" 26 #include "aom/aom_codec.h" 27 #include "aom/aom_integer.h" 28 29 using libaom_test::ACMRandom; 30 31 namespace { 32 const int kNumIterations = 1000; 33 34 using ErrorBlockFunc = int64_t (*)(const tran_low_t *coeff, 35 const tran_low_t *dqcoeff, 36 intptr_t block_size, int64_t *ssz, int bps); 37 38 using ErrorBlockFunc8Bits = int64_t (*)(const tran_low_t *coeff, 39 const tran_low_t *dqcoeff, 40 intptr_t block_size, int64_t *ssz); 41 42 using ErrorBlockLpFunc = int64_t (*)(const int16_t *coeff, 43 const int16_t *dqcoeff, 44 intptr_t block_size); 45 46 using ErrorBlockParam = 47 std::tuple<ErrorBlockFunc, ErrorBlockFunc, aom_bit_depth_t>; 48 49 template <ErrorBlockFunc8Bits fn> 50 int64_t BlockError8BitWrapper(const tran_low_t *coeff, 51 const tran_low_t *dqcoeff, intptr_t block_size, 52 int64_t *ssz, int bps) { 53 EXPECT_EQ(bps, 8); 54 return fn(coeff, dqcoeff, block_size, ssz); 55 } 56 57 template <ErrorBlockLpFunc fn> 58 int64_t BlockErrorLpWrapper(const tran_low_t *coeff, const tran_low_t *dqcoeff, 59 intptr_t block_size, int64_t *ssz, int bps) { 60 EXPECT_EQ(bps, 8); 61 *ssz = -1; 62 return fn(reinterpret_cast<const int16_t *>(coeff), 63 reinterpret_cast<const int16_t *>(dqcoeff), block_size); 64 } 65 66 class ErrorBlockTest : public ::testing::TestWithParam<ErrorBlockParam> { 67 public: 68 ~ErrorBlockTest() override = default; 69 void SetUp() override { 70 error_block_op_ = GET_PARAM(0); 71 ref_error_block_op_ = GET_PARAM(1); 72 bit_depth_ = GET_PARAM(2); 73 } 74 75 protected: 76 aom_bit_depth_t bit_depth_; 77 ErrorBlockFunc error_block_op_; 78 ErrorBlockFunc ref_error_block_op_; 79 }; 80 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ErrorBlockTest); 81 82 TEST_P(ErrorBlockTest, OperationCheck) { 83 ACMRandom rnd(ACMRandom::DeterministicSeed()); 84 DECLARE_ALIGNED(16, tran_low_t, coeff[4096]); 85 DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]); 86 int err_count_total = 0; 87 int first_failure = -1; 88 intptr_t block_size; 89 int64_t ssz; 90 int64_t ret; 91 int64_t ref_ssz; 92 int64_t ref_ret; 93 const int msb = bit_depth_ + 8 - 1; 94 for (int i = 0; i < kNumIterations; ++i) { 95 int err_count = 0; 96 block_size = 16 << (i % 9); // All block sizes from 4x4, 8x4 ..64x64 97 for (int j = 0; j < block_size; j++) { 98 // coeff and dqcoeff will always have at least the same sign, and this 99 // can be used for optimization, so generate test input precisely. 100 if (rnd(2)) { 101 // Positive number 102 coeff[j] = rnd(1 << msb); 103 dqcoeff[j] = rnd(1 << msb); 104 } else { 105 // Negative number 106 coeff[j] = -rnd(1 << msb); 107 dqcoeff[j] = -rnd(1 << msb); 108 } 109 } 110 ref_ret = 111 ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_); 112 API_REGISTER_STATE_CHECK( 113 ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_)); 114 err_count += (ref_ret != ret) | (ref_ssz != ssz); 115 if (err_count && !err_count_total) { 116 first_failure = i; 117 } 118 err_count_total += err_count; 119 } 120 EXPECT_EQ(0, err_count_total) 121 << "Error: Error Block Test, C output doesn't match optimized output. " 122 << "First failed at test case " << first_failure; 123 } 124 125 TEST_P(ErrorBlockTest, ExtremeValues) { 126 ACMRandom rnd(ACMRandom::DeterministicSeed()); 127 DECLARE_ALIGNED(16, tran_low_t, coeff[4096]); 128 DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]); 129 int err_count_total = 0; 130 int first_failure = -1; 131 intptr_t block_size; 132 int64_t ssz; 133 int64_t ret; 134 int64_t ref_ssz; 135 int64_t ref_ret; 136 const int msb = bit_depth_ + 8 - 1; 137 int max_val = ((1 << msb) - 1); 138 for (int i = 0; i < kNumIterations; ++i) { 139 int err_count = 0; 140 int k = (i / 9) % 9; 141 142 // Change the maximum coeff value, to test different bit boundaries 143 if (k == 8 && (i % 9) == 0) { 144 max_val >>= 1; 145 } 146 block_size = 16 << (i % 9); // All block sizes from 4x4, 8x4 ..64x64 147 for (int j = 0; j < block_size; j++) { 148 if (k < 4) { 149 // Test at positive maximum values 150 coeff[j] = k % 2 ? max_val : 0; 151 dqcoeff[j] = (k >> 1) % 2 ? max_val : 0; 152 } else if (k < 8) { 153 // Test at negative maximum values 154 coeff[j] = k % 2 ? -max_val : 0; 155 dqcoeff[j] = (k >> 1) % 2 ? -max_val : 0; 156 } else { 157 if (rnd(2)) { 158 // Positive number 159 coeff[j] = rnd(1 << 14); 160 dqcoeff[j] = rnd(1 << 14); 161 } else { 162 // Negative number 163 coeff[j] = -rnd(1 << 14); 164 dqcoeff[j] = -rnd(1 << 14); 165 } 166 } 167 } 168 ref_ret = 169 ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_); 170 API_REGISTER_STATE_CHECK( 171 ret = error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_)); 172 err_count += (ref_ret != ret) | (ref_ssz != ssz); 173 if (err_count && !err_count_total) { 174 first_failure = i; 175 } 176 err_count_total += err_count; 177 } 178 EXPECT_EQ(0, err_count_total) 179 << "Error: Error Block Test, C output doesn't match optimized output. " 180 << "First failed at test case " << first_failure; 181 } 182 183 TEST_P(ErrorBlockTest, DISABLED_Speed) { 184 ACMRandom rnd(ACMRandom::DeterministicSeed()); 185 DECLARE_ALIGNED(16, tran_low_t, coeff[4096]); 186 DECLARE_ALIGNED(16, tran_low_t, dqcoeff[4096]); 187 intptr_t block_size; 188 int64_t ssz; 189 int num_iters = 100000; 190 int64_t ref_ssz; 191 const int msb = bit_depth_ + 8 - 1; 192 for (int i = 0; i < 9; ++i) { 193 block_size = 16 << (i % 9); // All block sizes from 4x4, 8x4 ..64x64 194 for (int k = 0; k < 9; k++) { 195 for (int j = 0; j < block_size; j++) { 196 if (k < 5) { 197 if (rnd(2)) { 198 // Positive number 199 coeff[j] = rnd(1 << msb); 200 dqcoeff[j] = rnd(1 << msb); 201 } else { 202 // Negative number 203 coeff[j] = -rnd(1 << msb); 204 dqcoeff[j] = -rnd(1 << msb); 205 } 206 } else { 207 if (rnd(2)) { 208 // Positive number 209 coeff[j] = rnd(1 << 14); 210 dqcoeff[j] = rnd(1 << 14); 211 } else { 212 // Negative number 213 coeff[j] = -rnd(1 << 14); 214 dqcoeff[j] = -rnd(1 << 14); 215 } 216 } 217 } 218 aom_usec_timer ref_timer, test_timer; 219 220 aom_usec_timer_start(&ref_timer); 221 for (int iter = 0; iter < num_iters; ++iter) { 222 ref_error_block_op_(coeff, dqcoeff, block_size, &ref_ssz, bit_depth_); 223 } 224 aom_usec_timer_mark(&ref_timer); 225 const int elapsed_time_c = 226 static_cast<int>(aom_usec_timer_elapsed(&ref_timer)); 227 228 aom_usec_timer_start(&test_timer); 229 for (int iter = 0; iter < num_iters; ++iter) { 230 error_block_op_(coeff, dqcoeff, block_size, &ssz, bit_depth_); 231 } 232 aom_usec_timer_mark(&test_timer); 233 234 const int elapsed_time_simd = 235 static_cast<int>(aom_usec_timer_elapsed(&test_timer)); 236 237 printf( 238 " c_time=%d \t simd_time=%d \t " 239 "gain=%d \n", 240 elapsed_time_c, elapsed_time_simd, 241 (elapsed_time_c / elapsed_time_simd)); 242 } 243 } 244 } 245 246 using std::make_tuple; 247 248 #if HAVE_SSE2 249 const ErrorBlockParam kErrorBlockTestParamsSse2[] = { 250 #if CONFIG_AV1_HIGHBITDEPTH 251 make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c, 252 AOM_BITS_10), 253 make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c, 254 AOM_BITS_12), 255 make_tuple(&av1_highbd_block_error_sse2, &av1_highbd_block_error_c, 256 AOM_BITS_8), 257 #endif 258 make_tuple(&BlockError8BitWrapper<av1_block_error_sse2>, 259 &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8), 260 make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_sse2>, 261 &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8) 262 }; 263 264 INSTANTIATE_TEST_SUITE_P(SSE2, ErrorBlockTest, 265 ::testing::ValuesIn(kErrorBlockTestParamsSse2)); 266 #endif // HAVE_SSE2 267 268 #if HAVE_AVX2 269 const ErrorBlockParam kErrorBlockTestParamsAvx2[] = { 270 #if CONFIG_AV1_HIGHBITDEPTH 271 make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c, 272 AOM_BITS_10), 273 make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c, 274 AOM_BITS_12), 275 make_tuple(&av1_highbd_block_error_avx2, &av1_highbd_block_error_c, 276 AOM_BITS_8), 277 #endif 278 make_tuple(&BlockError8BitWrapper<av1_block_error_avx2>, 279 &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8), 280 make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_avx2>, 281 &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8) 282 }; 283 284 INSTANTIATE_TEST_SUITE_P(AVX2, ErrorBlockTest, 285 ::testing::ValuesIn(kErrorBlockTestParamsAvx2)); 286 #endif // HAVE_AVX2 287 288 #if HAVE_NEON 289 const ErrorBlockParam kErrorBlockTestParamsNeon[] = { 290 #if CONFIG_AV1_HIGHBITDEPTH 291 make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c, 292 AOM_BITS_10), 293 make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c, 294 AOM_BITS_12), 295 make_tuple(&av1_highbd_block_error_neon, &av1_highbd_block_error_c, 296 AOM_BITS_8), 297 #endif 298 make_tuple(&BlockError8BitWrapper<av1_block_error_neon>, 299 &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8), 300 make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_neon>, 301 &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8) 302 }; 303 304 INSTANTIATE_TEST_SUITE_P(NEON, ErrorBlockTest, 305 ::testing::ValuesIn(kErrorBlockTestParamsNeon)); 306 #endif // HAVE_NEON 307 308 #if HAVE_SVE 309 const ErrorBlockParam kErrorBlockTestParamsSVE[] = { 310 make_tuple(&BlockError8BitWrapper<av1_block_error_sve>, 311 &BlockError8BitWrapper<av1_block_error_c>, AOM_BITS_8), 312 make_tuple(&BlockErrorLpWrapper<av1_block_error_lp_sve>, 313 &BlockErrorLpWrapper<av1_block_error_lp_c>, AOM_BITS_8) 314 }; 315 316 INSTANTIATE_TEST_SUITE_P(SVE, ErrorBlockTest, 317 ::testing::ValuesIn(kErrorBlockTestParamsSVE)); 318 #endif // HAVE_SVE 319 } // namespace