1/* 2 * Copyright (c) 2013 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 17#include "./vp9_rtcd.h" 18#include "./vpx_dsp_rtcd.h" 19#include "test/acm_random.h" 20#include "test/clear_system_state.h" 21#include "test/register_state_check.h" 22#include "test/util.h" 23#include "vp9/common/vp9_blockd.h" 24#include "vp9/common/vp9_scan.h" 25#include "vpx/vpx_integer.h" 26 27using libvpx_test::ACMRandom; 28 29namespace { 30typedef void (*FwdTxfmFunc)(const int16_t *in, tran_low_t *out, int stride); 31typedef void (*InvTxfmFunc)(const tran_low_t *in, uint8_t *out, int stride); 32typedef std::tr1::tuple<FwdTxfmFunc, 33 InvTxfmFunc, 34 InvTxfmFunc, 35 TX_SIZE, int> PartialInvTxfmParam; 36const int kMaxNumCoeffs = 1024; 37class PartialIDctTest : public ::testing::TestWithParam<PartialInvTxfmParam> { 38 public: 39 virtual ~PartialIDctTest() {} 40 virtual void SetUp() { 41 ftxfm_ = GET_PARAM(0); 42 full_itxfm_ = GET_PARAM(1); 43 partial_itxfm_ = GET_PARAM(2); 44 tx_size_ = GET_PARAM(3); 45 last_nonzero_ = GET_PARAM(4); 46 } 47 48 virtual void TearDown() { libvpx_test::ClearSystemState(); } 49 50 protected: 51 int last_nonzero_; 52 TX_SIZE tx_size_; 53 FwdTxfmFunc ftxfm_; 54 InvTxfmFunc full_itxfm_; 55 InvTxfmFunc partial_itxfm_; 56}; 57 58TEST_P(PartialIDctTest, RunQuantCheck) { 59 ACMRandom rnd(ACMRandom::DeterministicSeed()); 60 int size; 61 switch (tx_size_) { 62 case TX_4X4: 63 size = 4; 64 break; 65 case TX_8X8: 66 size = 8; 67 break; 68 case TX_16X16: 69 size = 16; 70 break; 71 case TX_32X32: 72 size = 32; 73 break; 74 default: 75 FAIL() << "Wrong Size!"; 76 break; 77 } 78 DECLARE_ALIGNED(16, tran_low_t, test_coef_block1[kMaxNumCoeffs]); 79 DECLARE_ALIGNED(16, tran_low_t, test_coef_block2[kMaxNumCoeffs]); 80 DECLARE_ALIGNED(16, uint8_t, dst1[kMaxNumCoeffs]); 81 DECLARE_ALIGNED(16, uint8_t, dst2[kMaxNumCoeffs]); 82 83 const int count_test_block = 1000; 84 const int block_size = size * size; 85 86 DECLARE_ALIGNED(16, int16_t, input_extreme_block[kMaxNumCoeffs]); 87 DECLARE_ALIGNED(16, tran_low_t, output_ref_block[kMaxNumCoeffs]); 88 89 int max_error = 0; 90 for (int i = 0; i < count_test_block; ++i) { 91 // clear out destination buffer 92 memset(dst1, 0, sizeof(*dst1) * block_size); 93 memset(dst2, 0, sizeof(*dst2) * block_size); 94 memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size); 95 memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size); 96 97 ACMRandom rnd(ACMRandom::DeterministicSeed()); 98 99 for (int i = 0; i < count_test_block; ++i) { 100 // Initialize a test block with input range [-255, 255]. 101 if (i == 0) { 102 for (int j = 0; j < block_size; ++j) 103 input_extreme_block[j] = 255; 104 } else if (i == 1) { 105 for (int j = 0; j < block_size; ++j) 106 input_extreme_block[j] = -255; 107 } else { 108 for (int j = 0; j < block_size; ++j) { 109 input_extreme_block[j] = rnd.Rand8() % 2 ? 255 : -255; 110 } 111 } 112 113 ftxfm_(input_extreme_block, output_ref_block, size); 114 115 // quantization with maximum allowed step sizes 116 test_coef_block1[0] = (output_ref_block[0] / 1336) * 1336; 117 for (int j = 1; j < last_nonzero_; ++j) 118 test_coef_block1[vp9_default_scan_orders[tx_size_].scan[j]] 119 = (output_ref_block[j] / 1828) * 1828; 120 } 121 122 ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size)); 123 ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block1, dst2, size)); 124 125 for (int j = 0; j < block_size; ++j) { 126 const int diff = dst1[j] - dst2[j]; 127 const int error = diff * diff; 128 if (max_error < error) 129 max_error = error; 130 } 131 } 132 133 EXPECT_EQ(0, max_error) 134 << "Error: partial inverse transform produces different results"; 135} 136 137TEST_P(PartialIDctTest, ResultsMatch) { 138 ACMRandom rnd(ACMRandom::DeterministicSeed()); 139 int size; 140 switch (tx_size_) { 141 case TX_4X4: 142 size = 4; 143 break; 144 case TX_8X8: 145 size = 8; 146 break; 147 case TX_16X16: 148 size = 16; 149 break; 150 case TX_32X32: 151 size = 32; 152 break; 153 default: 154 FAIL() << "Wrong Size!"; 155 break; 156 } 157 DECLARE_ALIGNED(16, tran_low_t, test_coef_block1[kMaxNumCoeffs]); 158 DECLARE_ALIGNED(16, tran_low_t, test_coef_block2[kMaxNumCoeffs]); 159 DECLARE_ALIGNED(16, uint8_t, dst1[kMaxNumCoeffs]); 160 DECLARE_ALIGNED(16, uint8_t, dst2[kMaxNumCoeffs]); 161 const int count_test_block = 1000; 162 const int max_coeff = 32766 / 4; 163 const int block_size = size * size; 164 int max_error = 0; 165 for (int i = 0; i < count_test_block; ++i) { 166 // clear out destination buffer 167 memset(dst1, 0, sizeof(*dst1) * block_size); 168 memset(dst2, 0, sizeof(*dst2) * block_size); 169 memset(test_coef_block1, 0, sizeof(*test_coef_block1) * block_size); 170 memset(test_coef_block2, 0, sizeof(*test_coef_block2) * block_size); 171 int max_energy_leftover = max_coeff * max_coeff; 172 for (int j = 0; j < last_nonzero_; ++j) { 173 int16_t coef = static_cast<int16_t>(sqrt(1.0 * max_energy_leftover) * 174 (rnd.Rand16() - 32768) / 65536); 175 max_energy_leftover -= coef * coef; 176 if (max_energy_leftover < 0) { 177 max_energy_leftover = 0; 178 coef = 0; 179 } 180 test_coef_block1[vp9_default_scan_orders[tx_size_].scan[j]] = coef; 181 } 182 183 memcpy(test_coef_block2, test_coef_block1, 184 sizeof(*test_coef_block2) * block_size); 185 186 ASM_REGISTER_STATE_CHECK(full_itxfm_(test_coef_block1, dst1, size)); 187 ASM_REGISTER_STATE_CHECK(partial_itxfm_(test_coef_block2, dst2, size)); 188 189 for (int j = 0; j < block_size; ++j) { 190 const int diff = dst1[j] - dst2[j]; 191 const int error = diff * diff; 192 if (max_error < error) 193 max_error = error; 194 } 195 } 196 197 EXPECT_EQ(0, max_error) 198 << "Error: partial inverse transform produces different results"; 199} 200using std::tr1::make_tuple; 201 202INSTANTIATE_TEST_CASE_P( 203 C, PartialIDctTest, 204 ::testing::Values( 205 make_tuple(&vpx_fdct32x32_c, 206 &vpx_idct32x32_1024_add_c, 207 &vpx_idct32x32_34_add_c, 208 TX_32X32, 34), 209 make_tuple(&vpx_fdct32x32_c, 210 &vpx_idct32x32_1024_add_c, 211 &vpx_idct32x32_1_add_c, 212 TX_32X32, 1), 213 make_tuple(&vpx_fdct16x16_c, 214 &vpx_idct16x16_256_add_c, 215 &vpx_idct16x16_10_add_c, 216 TX_16X16, 10), 217 make_tuple(&vpx_fdct16x16_c, 218 &vpx_idct16x16_256_add_c, 219 &vpx_idct16x16_1_add_c, 220 TX_16X16, 1), 221 make_tuple(&vpx_fdct8x8_c, 222 &vpx_idct8x8_64_add_c, 223 &vpx_idct8x8_12_add_c, 224 TX_8X8, 12), 225 make_tuple(&vpx_fdct8x8_c, 226 &vpx_idct8x8_64_add_c, 227 &vpx_idct8x8_1_add_c, 228 TX_8X8, 1), 229 make_tuple(&vpx_fdct4x4_c, 230 &vpx_idct4x4_16_add_c, 231 &vpx_idct4x4_1_add_c, 232 TX_4X4, 1))); 233 234#if HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 235INSTANTIATE_TEST_CASE_P( 236 NEON, PartialIDctTest, 237 ::testing::Values( 238 make_tuple(&vpx_fdct32x32_c, 239 &vpx_idct32x32_1024_add_c, 240 &vpx_idct32x32_1_add_neon, 241 TX_32X32, 1), 242 make_tuple(&vpx_fdct16x16_c, 243 &vpx_idct16x16_256_add_c, 244 &vpx_idct16x16_10_add_neon, 245 TX_16X16, 10), 246 make_tuple(&vpx_fdct16x16_c, 247 &vpx_idct16x16_256_add_c, 248 &vpx_idct16x16_1_add_neon, 249 TX_16X16, 1), 250 make_tuple(&vpx_fdct8x8_c, 251 &vpx_idct8x8_64_add_c, 252 &vpx_idct8x8_12_add_neon, 253 TX_8X8, 12), 254 make_tuple(&vpx_fdct8x8_c, 255 &vpx_idct8x8_64_add_c, 256 &vpx_idct8x8_1_add_neon, 257 TX_8X8, 1), 258 make_tuple(&vpx_fdct4x4_c, 259 &vpx_idct4x4_16_add_c, 260 &vpx_idct4x4_1_add_neon, 261 TX_4X4, 1))); 262#endif // HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 263 264#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 265INSTANTIATE_TEST_CASE_P( 266 SSE2, PartialIDctTest, 267 ::testing::Values( 268 make_tuple(&vpx_fdct32x32_c, 269 &vpx_idct32x32_1024_add_c, 270 &vpx_idct32x32_34_add_sse2, 271 TX_32X32, 34), 272 make_tuple(&vpx_fdct32x32_c, 273 &vpx_idct32x32_1024_add_c, 274 &vpx_idct32x32_1_add_sse2, 275 TX_32X32, 1), 276 make_tuple(&vpx_fdct16x16_c, 277 &vpx_idct16x16_256_add_c, 278 &vpx_idct16x16_10_add_sse2, 279 TX_16X16, 10), 280 make_tuple(&vpx_fdct16x16_c, 281 &vpx_idct16x16_256_add_c, 282 &vpx_idct16x16_1_add_sse2, 283 TX_16X16, 1), 284 make_tuple(&vpx_fdct8x8_c, 285 &vpx_idct8x8_64_add_c, 286 &vpx_idct8x8_12_add_sse2, 287 TX_8X8, 12), 288 make_tuple(&vpx_fdct8x8_c, 289 &vpx_idct8x8_64_add_c, 290 &vpx_idct8x8_1_add_sse2, 291 TX_8X8, 1), 292 make_tuple(&vpx_fdct4x4_c, 293 &vpx_idct4x4_16_add_c, 294 &vpx_idct4x4_1_add_sse2, 295 TX_4X4, 1))); 296#endif 297 298#if HAVE_SSSE3 && CONFIG_USE_X86INC && ARCH_X86_64 && \ 299 !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 300INSTANTIATE_TEST_CASE_P( 301 SSSE3_64, PartialIDctTest, 302 ::testing::Values( 303 make_tuple(&vpx_fdct8x8_c, 304 &vpx_idct8x8_64_add_c, 305 &vpx_idct8x8_12_add_ssse3, 306 TX_8X8, 12))); 307#endif 308 309#if HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 310INSTANTIATE_TEST_CASE_P( 311 MSA, PartialIDctTest, 312 ::testing::Values( 313 make_tuple(&vpx_fdct32x32_c, 314 &vpx_idct32x32_1024_add_c, 315 &vpx_idct32x32_34_add_msa, 316 TX_32X32, 34), 317 make_tuple(&vpx_fdct32x32_c, 318 &vpx_idct32x32_1024_add_c, 319 &vpx_idct32x32_1_add_msa, 320 TX_32X32, 1), 321 make_tuple(&vpx_fdct16x16_c, 322 &vpx_idct16x16_256_add_c, 323 &vpx_idct16x16_10_add_msa, 324 TX_16X16, 10), 325 make_tuple(&vpx_fdct16x16_c, 326 &vpx_idct16x16_256_add_c, 327 &vpx_idct16x16_1_add_msa, 328 TX_16X16, 1), 329 make_tuple(&vpx_fdct8x8_c, 330 &vpx_idct8x8_64_add_c, 331 &vpx_idct8x8_12_add_msa, 332 TX_8X8, 10), 333 make_tuple(&vpx_fdct8x8_c, 334 &vpx_idct8x8_64_add_c, 335 &vpx_idct8x8_1_add_msa, 336 TX_8X8, 1), 337 make_tuple(&vpx_fdct4x4_c, 338 &vpx_idct4x4_16_add_c, 339 &vpx_idct4x4_1_add_msa, 340 TX_4X4, 1))); 341#endif // HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE 342 343} // namespace 344