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_idct4x4_16_add_c(const int16_t *input, uint8_t *output, int pitch); 27 } 28 29 using libvpx_test::ACMRandom; 30 31 namespace { 32 const int kNumCoeffs = 16; 33 typedef void (*fdct_t)(const int16_t *in, int16_t *out, int stride); 34 typedef void (*idct_t)(const int16_t *in, uint8_t *out, int stride); 35 typedef void (*fht_t) (const int16_t *in, int16_t *out, int stride, 36 int tx_type); 37 typedef void (*iht_t) (const int16_t *in, uint8_t *out, int stride, 38 int tx_type); 39 40 typedef std::tr1::tuple<fdct_t, idct_t, int> dct_4x4_param_t; 41 typedef std::tr1::tuple<fht_t, iht_t, int> ht_4x4_param_t; 42 43 void fdct4x4_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { 44 vp9_fdct4x4_c(in, out, stride); 45 } 46 47 void fht4x4_ref(const int16_t *in, int16_t *out, int stride, int tx_type) { 48 vp9_fht4x4_c(in, out, stride, tx_type); 49 } 50 51 class Trans4x4TestBase { 52 public: 53 virtual ~Trans4x4TestBase() {} 54 55 protected: 56 virtual void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) = 0; 57 58 virtual void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) = 0; 59 60 void RunAccuracyCheck() { 61 ACMRandom rnd(ACMRandom::DeterministicSeed()); 62 uint32_t max_error = 0; 63 int64_t total_error = 0; 64 const int count_test_block = 10000; 65 for (int i = 0; i < count_test_block; ++i) { 66 DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, kNumCoeffs); 67 DECLARE_ALIGNED_ARRAY(16, int16_t, test_temp_block, kNumCoeffs); 68 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs); 69 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs); 70 71 // Initialize a test block with input range [-255, 255]. 72 for (int j = 0; j < kNumCoeffs; ++j) { 73 src[j] = rnd.Rand8(); 74 dst[j] = rnd.Rand8(); 75 test_input_block[j] = src[j] - dst[j]; 76 } 77 78 REGISTER_STATE_CHECK(RunFwdTxfm(test_input_block, 79 test_temp_block, pitch_)); 80 REGISTER_STATE_CHECK(RunInvTxfm(test_temp_block, dst, pitch_)); 81 82 for (int j = 0; j < kNumCoeffs; ++j) { 83 const uint32_t diff = dst[j] - src[j]; 84 const uint32_t error = diff * diff; 85 if (max_error < error) 86 max_error = error; 87 total_error += error; 88 } 89 } 90 91 EXPECT_GE(1u, max_error) 92 << "Error: 4x4 FHT/IHT has an individual round trip error > 1"; 93 94 EXPECT_GE(count_test_block , total_error) 95 << "Error: 4x4 FHT/IHT has average round trip error > 1 per block"; 96 } 97 98 void RunCoeffCheck() { 99 ACMRandom rnd(ACMRandom::DeterministicSeed()); 100 const int count_test_block = 5000; 101 DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs); 102 DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs); 103 DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs); 104 105 for (int i = 0; i < count_test_block; ++i) { 106 // Initialize a test block with input range [-255, 255]. 107 for (int j = 0; j < kNumCoeffs; ++j) 108 input_block[j] = rnd.Rand8() - rnd.Rand8(); 109 110 fwd_txfm_ref(input_block, output_ref_block, pitch_, tx_type_); 111 REGISTER_STATE_CHECK(RunFwdTxfm(input_block, output_block, pitch_)); 112 113 // The minimum quant value is 4. 114 for (int j = 0; j < kNumCoeffs; ++j) 115 EXPECT_EQ(output_block[j], output_ref_block[j]); 116 } 117 } 118 119 void RunMemCheck() { 120 ACMRandom rnd(ACMRandom::DeterministicSeed()); 121 const int count_test_block = 5000; 122 DECLARE_ALIGNED_ARRAY(16, int16_t, input_block, kNumCoeffs); 123 DECLARE_ALIGNED_ARRAY(16, int16_t, input_extreme_block, kNumCoeffs); 124 DECLARE_ALIGNED_ARRAY(16, int16_t, output_ref_block, kNumCoeffs); 125 DECLARE_ALIGNED_ARRAY(16, int16_t, output_block, kNumCoeffs); 126 127 for (int i = 0; i < count_test_block; ++i) { 128 // Initialize a test block with input range [-255, 255]. 129 for (int j = 0; j < kNumCoeffs; ++j) { 130 input_block[j] = rnd.Rand8() - rnd.Rand8(); 131 input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255; 132 } 133 if (i == 0) 134 for (int j = 0; j < kNumCoeffs; ++j) 135 input_extreme_block[j] = 255; 136 if (i == 1) 137 for (int j = 0; j < kNumCoeffs; ++j) 138 input_extreme_block[j] = -255; 139 140 fwd_txfm_ref(input_extreme_block, output_ref_block, pitch_, tx_type_); 141 REGISTER_STATE_CHECK(RunFwdTxfm(input_extreme_block, 142 output_block, pitch_)); 143 144 // The minimum quant value is 4. 145 for (int j = 0; j < kNumCoeffs; ++j) { 146 EXPECT_EQ(output_block[j], output_ref_block[j]); 147 EXPECT_GE(4 * DCT_MAX_VALUE, abs(output_block[j])) 148 << "Error: 16x16 FDCT has coefficient larger than 4*DCT_MAX_VALUE"; 149 } 150 } 151 } 152 153 void RunInvAccuracyCheck() { 154 ACMRandom rnd(ACMRandom::DeterministicSeed()); 155 const int count_test_block = 1000; 156 DECLARE_ALIGNED_ARRAY(16, int16_t, in, kNumCoeffs); 157 DECLARE_ALIGNED_ARRAY(16, int16_t, coeff, kNumCoeffs); 158 DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs); 159 DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs); 160 161 for (int i = 0; i < count_test_block; ++i) { 162 // Initialize a test block with input range [-255, 255]. 163 for (int j = 0; j < kNumCoeffs; ++j) { 164 src[j] = rnd.Rand8(); 165 dst[j] = rnd.Rand8(); 166 in[j] = src[j] - dst[j]; 167 } 168 169 fwd_txfm_ref(in, coeff, pitch_, tx_type_); 170 171 REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, pitch_)); 172 173 for (int j = 0; j < kNumCoeffs; ++j) { 174 const uint32_t diff = dst[j] - src[j]; 175 const uint32_t error = diff * diff; 176 EXPECT_GE(1u, error) 177 << "Error: 16x16 IDCT has error " << error 178 << " at index " << j; 179 } 180 } 181 } 182 183 int pitch_; 184 int tx_type_; 185 fht_t fwd_txfm_ref; 186 }; 187 188 class Trans4x4DCT 189 : public Trans4x4TestBase, 190 public ::testing::TestWithParam<dct_4x4_param_t> { 191 public: 192 virtual ~Trans4x4DCT() {} 193 194 virtual void SetUp() { 195 fwd_txfm_ = GET_PARAM(0); 196 inv_txfm_ = GET_PARAM(1); 197 tx_type_ = GET_PARAM(2); 198 pitch_ = 4; 199 fwd_txfm_ref = fdct4x4_ref; 200 } 201 virtual void TearDown() { libvpx_test::ClearSystemState(); } 202 203 protected: 204 void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) { 205 fwd_txfm_(in, out, stride); 206 } 207 void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) { 208 inv_txfm_(out, dst, stride); 209 } 210 211 fdct_t fwd_txfm_; 212 idct_t inv_txfm_; 213 }; 214 215 TEST_P(Trans4x4DCT, AccuracyCheck) { 216 RunAccuracyCheck(); 217 } 218 219 TEST_P(Trans4x4DCT, CoeffCheck) { 220 RunCoeffCheck(); 221 } 222 223 TEST_P(Trans4x4DCT, MemCheck) { 224 RunMemCheck(); 225 } 226 227 TEST_P(Trans4x4DCT, InvAccuracyCheck) { 228 RunInvAccuracyCheck(); 229 } 230 231 class Trans4x4HT 232 : public Trans4x4TestBase, 233 public ::testing::TestWithParam<ht_4x4_param_t> { 234 public: 235 virtual ~Trans4x4HT() {} 236 237 virtual void SetUp() { 238 fwd_txfm_ = GET_PARAM(0); 239 inv_txfm_ = GET_PARAM(1); 240 tx_type_ = GET_PARAM(2); 241 pitch_ = 4; 242 fwd_txfm_ref = fht4x4_ref; 243 } 244 virtual void TearDown() { libvpx_test::ClearSystemState(); } 245 246 protected: 247 void RunFwdTxfm(const int16_t *in, int16_t *out, int stride) { 248 fwd_txfm_(in, out, stride, tx_type_); 249 } 250 251 void RunInvTxfm(const int16_t *out, uint8_t *dst, int stride) { 252 inv_txfm_(out, dst, stride, tx_type_); 253 } 254 255 fht_t fwd_txfm_; 256 iht_t inv_txfm_; 257 }; 258 259 TEST_P(Trans4x4HT, AccuracyCheck) { 260 RunAccuracyCheck(); 261 } 262 263 TEST_P(Trans4x4HT, CoeffCheck) { 264 RunCoeffCheck(); 265 } 266 267 TEST_P(Trans4x4HT, MemCheck) { 268 RunMemCheck(); 269 } 270 271 TEST_P(Trans4x4HT, InvAccuracyCheck) { 272 RunInvAccuracyCheck(); 273 } 274 275 using std::tr1::make_tuple; 276 277 INSTANTIATE_TEST_CASE_P( 278 C, Trans4x4DCT, 279 ::testing::Values( 280 make_tuple(&vp9_fdct4x4_c, &vp9_idct4x4_16_add_c, 0))); 281 INSTANTIATE_TEST_CASE_P( 282 C, Trans4x4HT, 283 ::testing::Values( 284 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_c, 0), 285 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_c, 1), 286 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_c, 2), 287 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_c, 3))); 288 289 #if HAVE_NEON 290 INSTANTIATE_TEST_CASE_P( 291 NEON, Trans4x4DCT, 292 ::testing::Values( 293 make_tuple(&vp9_fdct4x4_c, 294 &vp9_idct4x4_16_add_neon, 0))); 295 INSTANTIATE_TEST_CASE_P( 296 DISABLED_NEON, Trans4x4HT, 297 ::testing::Values( 298 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_neon, 0), 299 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_neon, 1), 300 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_neon, 2), 301 make_tuple(&vp9_fht4x4_c, &vp9_iht4x4_16_add_neon, 3))); 302 #endif 303 304 #if HAVE_SSE2 305 INSTANTIATE_TEST_CASE_P( 306 SSE2, Trans4x4DCT, 307 ::testing::Values( 308 make_tuple(&vp9_fdct4x4_sse2, 309 &vp9_idct4x4_16_add_sse2, 0))); 310 INSTANTIATE_TEST_CASE_P( 311 SSE2, Trans4x4HT, 312 ::testing::Values( 313 make_tuple(&vp9_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 0), 314 make_tuple(&vp9_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 1), 315 make_tuple(&vp9_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 2), 316 make_tuple(&vp9_fht4x4_sse2, &vp9_iht4x4_16_add_sse2, 3))); 317 #endif 318 319 } // namespace 320