1 /* 2 * Copyright (c) 2012 The WebM project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <math.h> 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include "third_party/googletest/src/include/gtest/gtest.h" 16 #include "test/acm_random.h" 17 #include "test/clear_system_state.h" 18 #include "test/register_state_check.h" 19 #include "test/util.h" 20 21 #include "./vp9_rtcd.h" 22 #include "vp9/common/vp9_entropy.h" 23 #include "vpx/vpx_integer.h" 24 25 extern "C" { 26 void vp9_idct8x8_64_add_c(const int16_t *input, uint8_t *output, int pitch); 27 } 28 29 using libvpx_test::ACMRandom; 30 31 namespace { 32 typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride); 33 typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride); 34 typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride, 35 int tx_type); 36 typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride, 37 int tx_type); 38 39 typedef std::tr1::tuple<fdct_t, idct_t, int> dct_8x8_param_t; 40 typedef std::tr1::tuple<fht_t, iht_t, int> ht_8x8_param_t; 41 42 void fdct8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { 43 vp9_fdct8x8_c(in, out, stride); 44 } 45 46 void fht8x8_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { 47 vp9_fht8x8_c(in, out, stride, tx_type); 48 } 49 50 class FwdTrans8x8TestBase { 51 public: 52 virtual ~FwdTrans8x8TestBase() {} 53 54 protected: 55 virtual void RunFwdTxfm(int16_t *in, int16_t *out, int stride) = 0; 56 virtual void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) = 0; 57 58 void RunSignBiasCheck() { 59 ACMRandom rnd(ACMRandom::DeterministicSeed()); 60 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64); 61 DECLARE_ALIGNED_ARRAY(16, int16_t, test_output_block, 64); 62 int count_sign_block[64][2]; 63 const int count_test_block = 100000; 64 65 memset(count_sign_block, 0, sizeof(count_sign_block)); 66 67 for (int i = 0; i < count_test_block; ++i) { 68 // Initialize a test block with input range [-255, 255]. 69 for (int j = 0; j < 64; ++j) 70 test_input_block[j] = rnd.Rand8() - rnd.Rand8(); 71 REGISTER_STATE_CHECK( 72 RunFwdTxfm(test_input_block, test_output_block, pitch_)); 73 74 for (int j = 0; j < 64; ++j) { 75 if (test_output_block[j] < 0) 76 ++count_sign_block[j][0]; 77 else if (test_output_block[j] > 0) 78 ++count_sign_block[j][1]; 79 } 80 } 81 82 for (int j = 0; j < 64; ++j) { 83 const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]); 84 const int max_diff = 1125; 85 EXPECT_LT(diff, max_diff) 86 << "Error: 8x8 FDCT/FHT has a sign bias > " 87 << 1. * max_diff / count_test_block * 100 << "%" 88 << " for input range [-255, 255] at index " << j 89 << " count0: " << count_sign_block[j][0] 90 << " count1: " << count_sign_block[j][1] 91 << " diff: " << diff; 92 } 93 94 memset(count_sign_block, 0, sizeof(count_sign_block)); 95 96 for (int i = 0; i < count_test_block; ++i) { 97 // Initialize a test block with input range [-15, 15]. 98 for (int j = 0; j < 64; ++j) 99 test_input_block[j] = (rnd.Rand8() >> 4) - (rnd.Rand8() >> 4); 100 REGISTER_STATE_CHECK( 101 RunFwdTxfm(test_input_block, test_output_block, pitch_)); 102 103 for (int j = 0; j < 64; ++j) { 104 if (test_output_block[j] < 0) 105 ++count_sign_block[j][0]; 106 else if (test_output_block[j] > 0) 107 ++count_sign_block[j][1]; 108 } 109 } 110 111 for (int j = 0; j < 64; ++j) { 112 const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]); 113 const int max_diff = 10000; 114 EXPECT_LT(diff, max_diff) 115 << "Error: 4x4 FDCT/FHT has a sign bias > " 116 << 1. * max_diff / count_test_block * 100 << "%" 117 << " for input range [-15, 15] at index " << j 118 << " count0: " << count_sign_block[j][0] 119 << " count1: " << count_sign_block[j][1] 120 << " diff: " << diff; 121 } 122 } 123 124 void RunRoundTripErrorCheck() { 125 ACMRandom rnd(ACMRandom::DeterministicSeed()); 126 int max_error = 0; 127 int total_error = 0; 128 const int count_test_block = 100000; 129 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64); 130 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64); 131 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64); 132 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64); 133 134 for (int i = 0; i < count_test_block; ++i) { 135 // Initialize a test block with input range [-255, 255]. 136 for (int j = 0; j < 64; ++j) { 137 src[j] = rnd.Rand8(); 138 dst[j] = rnd.Rand8(); 139 test_input_block[j] = src[j] - dst[j]; 140 } 141 142 REGISTER_STATE_CHECK( 143 RunFwdTxfm(test_input_block, test_temp_block, pitch_)); 144 for (int j = 0; j < 64; ++j) { 145 if (test_temp_block[j] > 0) { 146 test_temp_block[j] += 2; 147 test_temp_block[j] /= 4; 148 test_temp_block[j] *= 4; 149 } else { 150 test_temp_block[j] -= 2; 151 test_temp_block[j] /= 4; 152 test_temp_block[j] *= 4; 153 } 154 } 155 REGISTER_STATE_CHECK( 156 RunInvTxfm(test_temp_block, dst, pitch_)); 157 158 for (int j = 0; j < 64; ++j) { 159 const int diff = dst[j] - src[j]; 160 const int error = diff * diff; 161 if (max_error < error) 162 max_error = error; 163 total_error += error; 164 } 165 } 166 167 EXPECT_GE(1, max_error) 168 << "Error: 8x8 FDCT/IDCT or FHT/IHT has an individual" 169 << " roundtrip error > 1"; 170 171 EXPECT_GE(count_test_block/5, total_error) 172 << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip " 173 << "error > 1/5 per block"; 174 } 175 176 void RunExtremalCheck() { 177 ACMRandom rnd(ACMRandom::DeterministicSeed()); 178 int max_error = 0; 179 int total_error = 0; 180 const int count_test_block = 100000; 181 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64); 182 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, 64); 183 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64); 184 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64); 185 186 for (int i = 0; i < count_test_block; ++i) { 187 // Initialize a test block with input range [-255, 255]. 188 for (int j = 0; j < 64; ++j) { 189 src[j] = rnd.Rand8() % 2 ? 255 : 0; 190 dst[j] = src[j] > 0 ? 0 : 255; 191 test_input_block[j] = src[j] - dst[j]; 192 } 193 194 REGISTER_STATE_CHECK( 195 RunFwdTxfm(test_input_block, test_temp_block, pitch_)); 196 REGISTER_STATE_CHECK( 197 RunInvTxfm(test_temp_block, dst, pitch_)); 198 199 for (int j = 0; j < 64; ++j) { 200 const int diff = dst[j] - src[j]; 201 const int error = diff * diff; 202 if (max_error < error) 203 max_error = error; 204 total_error += error; 205 } 206 207 EXPECT_GE(1, max_error) 208 << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has" 209 << "an individual roundtrip error > 1"; 210 211 EXPECT_GE(count_test_block/5, total_error) 212 << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average" 213 << " roundtrip error > 1/5 per block"; 214 } 215 } 216 217 int pitch_; 218 int tx_type_; 219 fht_t fwd_txfm_ref; 220 }; 221 222 class FwdTrans8x8DCT 223 : public FwdTrans8x8TestBase, 224 public ::testing::TestWithParam<dct_8x8_param_t> { 225 public: 226 virtual ~FwdTrans8x8DCT() {} 227 228 virtual void SetUp() { 229 fwd_txfm_ = GET_PARAM(0); 230 inv_txfm_ = GET_PARAM(1); 231 tx_type_ = GET_PARAM(2); 232 pitch_ = 8; 233 fwd_txfm_ref = fdct8x8_ref; 234 } 235 236 virtual void TearDown() { libvpx_test::ClearSystemState(); } 237 238 protected: 239 void RunFwdTxfm(int16_t *in, int16_t *out, int stride) { 240 fwd_txfm_(in, out, stride); 241 } 242 void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) { 243 inv_txfm_(out, dst, stride); 244 } 245 246 fdct_t fwd_txfm_; 247 idct_t inv_txfm_; 248 }; 249 250 TEST_P(FwdTrans8x8DCT, SignBiasCheck) { 251 RunSignBiasCheck(); 252 } 253 254 TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) { 255 RunRoundTripErrorCheck(); 256 } 257 258 TEST_P(FwdTrans8x8DCT, ExtremalCheck) { 259 RunExtremalCheck(); 260 } 261 262 class FwdTrans8x8HT 263 : public FwdTrans8x8TestBase, 264 public ::testing::TestWithParam<ht_8x8_param_t> { 265 public: 266 virtual ~FwdTrans8x8HT() {} 267 268 virtual void SetUp() { 269 fwd_txfm_ = GET_PARAM(0); 270 inv_txfm_ = GET_PARAM(1); 271 tx_type_ = GET_PARAM(2); 272 pitch_ = 8; 273 fwd_txfm_ref = fht8x8_ref; 274 } 275 276 virtual void TearDown() { libvpx_test::ClearSystemState(); } 277 278 protected: 279 void RunFwdTxfm(int16_t *in, int16_t *out, int stride) { 280 fwd_txfm_(in, out, stride, tx_type_); 281 } 282 void RunInvTxfm(int16_t *out, uint8_t *dst, int stride) { 283 inv_txfm_(out, dst, stride, tx_type_); 284 } 285 286 fht_t fwd_txfm_; 287 iht_t inv_txfm_; 288 }; 289 290 TEST_P(FwdTrans8x8HT, SignBiasCheck) { 291 RunSignBiasCheck(); 292 } 293 294 TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) { 295 RunRoundTripErrorCheck(); 296 } 297 298 TEST_P(FwdTrans8x8HT, ExtremalCheck) { 299 RunExtremalCheck(); 300 } 301 302 using std::tr1::make_tuple; 303 304 INSTANTIATE_TEST_CASE_P( 305 C, FwdTrans8x8DCT, 306 ::testing::Values( 307 make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0))); 308 INSTANTIATE_TEST_CASE_P( 309 C, FwdTrans8x8HT, 310 ::testing::Values( 311 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0), 312 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 1), 313 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 2), 314 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 3))); 315 316 #if HAVE_NEON 317 INSTANTIATE_TEST_CASE_P( 318 NEON, FwdTrans8x8DCT, 319 ::testing::Values( 320 make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_neon, 0))); 321 INSTANTIATE_TEST_CASE_P( 322 DISABLED_NEON, FwdTrans8x8HT, 323 ::testing::Values( 324 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 0), 325 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 1), 326 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 2), 327 make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 3))); 328 #endif 329 330 #if HAVE_SSE2 331 INSTANTIATE_TEST_CASE_P( 332 SSE2, FwdTrans8x8DCT, 333 ::testing::Values( 334 make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0))); 335 INSTANTIATE_TEST_CASE_P( 336 SSE2, FwdTrans8x8HT, 337 ::testing::Values( 338 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0), 339 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 1), 340 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 2), 341 make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 3))); 342 #endif 343 } // namespace 344