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