fdct8x8_test.cc 25.8 KB
Newer Older
Daniel Kang's avatar
Daniel Kang committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/*
 *  Copyright (c) 2012 The WebM project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "third_party/googletest/src/include/gtest/gtest.h"
16
#include "test/acm_random.h"
17 18
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
19
#include "test/util.h"
Daniel Kang's avatar
Daniel Kang committed
20

21
#include "./vp9_rtcd.h"
22
#include "vp9/common/vp9_entropy.h"
23
#include "vpx/vpx_codec.h"
24 25
#include "vpx/vpx_integer.h"

26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
const int kNumCoeffs = 64;
const double kPi = 3.141592653589793238462643383279502884;
void reference_8x8_dct_1d(const double in[8], double out[8], int stride) {
  const double kInvSqrt2 = 0.707106781186547524400844362104;
  for (int k = 0; k < 8; k++) {
    out[k] = 0.0;
    for (int n = 0; n < 8; n++)
      out[k] += in[n] * cos(kPi * (2 * n + 1) * k / 16.0);
    if (k == 0)
      out[k] = out[k] * kInvSqrt2;
  }
}

void reference_8x8_dct_2d(const int16_t input[kNumCoeffs],
                          double output[kNumCoeffs]) {
  // First transform columns
  for (int i = 0; i < 8; ++i) {
    double temp_in[8], temp_out[8];
    for (int j = 0; j < 8; ++j)
      temp_in[j] = input[j*8 + i];
    reference_8x8_dct_1d(temp_in, temp_out, 1);
    for (int j = 0; j < 8; ++j)
      output[j * 8 + i] = temp_out[j];
  }
  // Then transform rows
  for (int i = 0; i < 8; ++i) {
    double temp_in[8], temp_out[8];
    for (int j = 0; j < 8; ++j)
      temp_in[j] = output[j + i*8];
    reference_8x8_dct_1d(temp_in, temp_out, 1);
    // Scale by some magic number
    for (int j = 0; j < 8; ++j)
      output[j + i * 8] = temp_out[j] * 2;
  }
Daniel Kang's avatar
Daniel Kang committed
60 61 62 63 64
}

using libvpx_test::ACMRandom;

namespace {
65 66 67 68

const int kSignBiasMaxDiff255 = 1500;
const int kSignBiasMaxDiff15 = 10000;

69 70 71
typedef void (*FdctFunc)(const int16_t *in, tran_low_t *out, int stride);
typedef void (*IdctFunc)(const tran_low_t *in, uint8_t *out, int stride);
typedef void (*FhtFunc)(const int16_t *in, tran_low_t *out, int stride,
72
                        int tx_type);
73
typedef void (*IhtFunc)(const tran_low_t *in, uint8_t *out, int stride,
74
                        int tx_type);
75

76 77
typedef std::tr1::tuple<FdctFunc, IdctFunc, int, vpx_bit_depth_t> Dct8x8Param;
typedef std::tr1::tuple<FhtFunc, IhtFunc, int, vpx_bit_depth_t> Ht8x8Param;
78
typedef std::tr1::tuple<IdctFunc, IdctFunc, int, vpx_bit_depth_t> Idct8x8Param;
79

80
void fdct8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
81
  vp9_fdct8x8_c(in, out, stride);
82
}
83

84
void fht8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
85
  vp9_fht8x8_c(in, out, stride, tx_type);
86 87
}

88 89
#if CONFIG_VP9_HIGHBITDEPTH
void idct8x8_10(const tran_low_t *in, uint8_t *out, int stride) {
90
  vp9_highbd_idct8x8_64_add_c(in, out, stride, 10);
91 92 93
}

void idct8x8_12(const tran_low_t *in, uint8_t *out, int stride) {
94
  vp9_highbd_idct8x8_64_add_c(in, out, stride, 12);
95 96 97
}

void iht8x8_10(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
98
  vp9_highbd_iht8x8_64_add_c(in, out, stride, tx_type, 10);
99 100 101
}

void iht8x8_12(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
102
  vp9_highbd_iht8x8_64_add_c(in, out, stride, tx_type, 12);
103
}
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

void idct8x8_10_add_10_c(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_10_add_c(in, out, stride, 10);
}

void idct8x8_10_add_12_c(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_10_add_c(in, out, stride, 12);
}

#if HAVE_SSE2
void idct8x8_10_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_10_add_sse2(in, out, stride, 10);
}

void idct8x8_10_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_10_add_sse2(in, out, stride, 12);
}

void idct8x8_64_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_64_add_sse2(in, out, stride, 10);
}

void idct8x8_64_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
  vp9_highbd_idct8x8_64_add_sse2(in, out, stride, 12);
}
#endif  // HAVE_SSE2
#endif  // CONFIG_VP9_HIGHBITDEPTH
131

132
class FwdTrans8x8TestBase {
133
 public:
134
  virtual ~FwdTrans8x8TestBase() {}
135 136

 protected:
137 138
  virtual void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) = 0;
  virtual void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) = 0;
139

140 141 142
  void RunSignBiasCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
143
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_output_block, 64);
144 145
    int count_sign_block[64][2];
    const int count_test_block = 100000;
Daniel Kang's avatar
Daniel Kang committed
146

147
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
148

149 150 151
    for (int i = 0; i < count_test_block; ++i) {
      // Initialize a test block with input range [-255, 255].
      for (int j = 0; j < 64; ++j)
152 153
        test_input_block[j] = ((rnd.Rand16() >> (16 - bit_depth_)) & mask_) -
                              ((rnd.Rand16() >> (16 - bit_depth_)) & mask_);
154
      ASM_REGISTER_STATE_CHECK(
155
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
156

157 158 159 160 161 162 163
      for (int j = 0; j < 64; ++j) {
        if (test_output_block[j] < 0)
          ++count_sign_block[j][0];
        else if (test_output_block[j] > 0)
          ++count_sign_block[j][1];
      }
    }
Daniel Kang's avatar
Daniel Kang committed
164 165

    for (int j = 0; j < 64; ++j) {
166
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
167
      const int max_diff = kSignBiasMaxDiff255;
168
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
169 170 171 172 173 174
          << "Error: 8x8 FDCT/FHT has a sign bias > "
          << 1. * max_diff / count_test_block * 100 << "%"
          << " for input range [-255, 255] at index " << j
          << " count0: " << count_sign_block[j][0]
          << " count1: " << count_sign_block[j][1]
          << " diff: " << diff;
Daniel Kang's avatar
Daniel Kang committed
175 176
    }

177
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
178

179
    for (int i = 0; i < count_test_block; ++i) {
180
      // Initialize a test block with input range [-mask_ / 16, mask_ / 16].
181
      for (int j = 0; j < 64; ++j)
182 183
        test_input_block[j] = ((rnd.Rand16() & mask_) >> 4) -
                              ((rnd.Rand16() & mask_) >> 4);
184
      ASM_REGISTER_STATE_CHECK(
185
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
186

187 188 189 190 191 192 193
      for (int j = 0; j < 64; ++j) {
        if (test_output_block[j] < 0)
          ++count_sign_block[j][0];
        else if (test_output_block[j] > 0)
          ++count_sign_block[j][1];
      }
    }
Daniel Kang's avatar
Daniel Kang committed
194 195

    for (int j = 0; j < 64; ++j) {
196
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
197
      const int max_diff = kSignBiasMaxDiff15;
198
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
199
          << "Error: 8x8 FDCT/FHT has a sign bias > "
200 201 202 203 204
          << 1. * max_diff / count_test_block * 100 << "%"
          << " for input range [-15, 15] at index " << j
          << " count0: " << count_sign_block[j][0]
          << " count1: " << count_sign_block[j][1]
          << " diff: " << diff;
Daniel Kang's avatar
Daniel Kang committed
205 206 207
    }
  }

208 209 210 211 212
  void RunRoundTripErrorCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
213
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
214
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_temp_block, 64);
215 216
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
217 218 219 220
#if CONFIG_VP9_HIGHBITDEPTH
    DECLARE_ALIGNED_ARRAY(16, uint16_t, dst16, 64);
    DECLARE_ALIGNED_ARRAY(16, uint16_t, src16, 64);
#endif
Daniel Kang's avatar
Daniel Kang committed
221

222
    for (int i = 0; i < count_test_block; ++i) {
223
      // Initialize a test block with input range [-mask_, mask_].
224
      for (int j = 0; j < 64; ++j) {
225 226 227 228 229 230 231 232 233 234 235
        if (bit_depth_ == VPX_BITS_8) {
          src[j] = rnd.Rand8();
          dst[j] = rnd.Rand8();
          test_input_block[j] = src[j] - dst[j];
#if CONFIG_VP9_HIGHBITDEPTH
        } else {
          src16[j] = rnd.Rand16() & mask_;
          dst16[j] = rnd.Rand16() & mask_;
          test_input_block[j] = src16[j] - dst16[j];
#endif
        }
236
      }
Daniel Kang's avatar
Daniel Kang committed
237

238
      ASM_REGISTER_STATE_CHECK(
239 240 241 242 243 244 245 246 247 248 249 250
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
      for (int j = 0; j < 64; ++j) {
          if (test_temp_block[j] > 0) {
            test_temp_block[j] += 2;
            test_temp_block[j] /= 4;
            test_temp_block[j] *= 4;
          } else {
            test_temp_block[j] -= 2;
            test_temp_block[j] /= 4;
            test_temp_block[j] *= 4;
          }
      }
251 252 253 254 255 256 257 258 259
      if (bit_depth_ == VPX_BITS_8) {
        ASM_REGISTER_STATE_CHECK(
            RunInvTxfm(test_temp_block, dst, pitch_));
#if CONFIG_VP9_HIGHBITDEPTH
      } else {
        ASM_REGISTER_STATE_CHECK(
            RunInvTxfm(test_temp_block, CONVERT_TO_BYTEPTR(dst16), pitch_));
#endif
      }
260 261

      for (int j = 0; j < 64; ++j) {
262 263 264 265
#if CONFIG_VP9_HIGHBITDEPTH
        const int diff =
            bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
#else
266
        const int diff = dst[j] - src[j];
267
#endif
268 269 270 271 272
        const int error = diff * diff;
        if (max_error < error)
          max_error = error;
        total_error += error;
      }
Daniel Kang's avatar
Daniel Kang committed
273 274
    }

275
    EXPECT_GE(1 << 2 * (bit_depth_ - 8), max_error)
276 277
      << "Error: 8x8 FDCT/IDCT or FHT/IHT has an individual"
      << " roundtrip error > 1";
Daniel Kang's avatar
Daniel Kang committed
278

279
    EXPECT_GE((count_test_block << 2 * (bit_depth_ - 8))/5, total_error)
280 281 282
      << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip "
      << "error > 1/5 per block";
  }
Daniel Kang's avatar
Daniel Kang committed
283

284 285 286 287
  void RunExtremalCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
288
    int total_coeff_error = 0;
289
    const int count_test_block = 100000;
290
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
291 292
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_temp_block, 64);
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, ref_temp_block, 64);
293 294
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
295 296 297 298
#if CONFIG_VP9_HIGHBITDEPTH
    DECLARE_ALIGNED_ARRAY(16, uint16_t, dst16, 64);
    DECLARE_ALIGNED_ARRAY(16, uint16_t, src16, 64);
#endif
Daniel Kang's avatar
Daniel Kang committed
299

300
    for (int i = 0; i < count_test_block; ++i) {
301
      // Initialize a test block with input range [-mask_, mask_].
302
      for (int j = 0; j < 64; ++j) {
303 304 305 306 307 308 309 310 311 312 313 314 315
        if (bit_depth_ == VPX_BITS_8) {
          if (i == 0) {
            src[j] = 255;
            dst[j] = 0;
          } else if (i == 1) {
            src[j] = 0;
            dst[j] = 255;
          } else {
            src[j] = rnd.Rand8() % 2 ? 255 : 0;
            dst[j] = rnd.Rand8() % 2 ? 255 : 0;
          }
          test_input_block[j] = src[j] - dst[j];
#if CONFIG_VP9_HIGHBITDEPTH
316
        } else {
317 318 319 320 321 322 323 324 325 326 327 328
          if (i == 0) {
            src16[j] = mask_;
            dst16[j] = 0;
          } else if (i == 1) {
            src16[j] = 0;
            dst16[j] = mask_;
          } else {
            src16[j] = rnd.Rand8() % 2 ? mask_ : 0;
            dst16[j] = rnd.Rand8() % 2 ? mask_ : 0;
          }
          test_input_block[j] = src16[j] - dst16[j];
#endif
329
        }
330
      }
Daniel Kang's avatar
Daniel Kang committed
331

332
      ASM_REGISTER_STATE_CHECK(
333
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
334
      ASM_REGISTER_STATE_CHECK(
335
          fwd_txfm_ref(test_input_block, ref_temp_block, pitch_, tx_type_));
336 337 338 339 340 341 342 343 344
      if (bit_depth_ == VPX_BITS_8) {
        ASM_REGISTER_STATE_CHECK(
            RunInvTxfm(test_temp_block, dst, pitch_));
#if CONFIG_VP9_HIGHBITDEPTH
      } else {
        ASM_REGISTER_STATE_CHECK(
            RunInvTxfm(test_temp_block, CONVERT_TO_BYTEPTR(dst16), pitch_));
#endif
      }
345 346

      for (int j = 0; j < 64; ++j) {
347 348 349 350
#if CONFIG_VP9_HIGHBITDEPTH
        const int diff =
            bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
#else
351
        const int diff = dst[j] - src[j];
352
#endif
353 354 355 356
        const int error = diff * diff;
        if (max_error < error)
          max_error = error;
        total_error += error;
357 358 359

        const int coeff_diff = test_temp_block[j] - ref_temp_block[j];
        total_coeff_error += abs(coeff_diff);
360 361
      }

362
      EXPECT_GE(1 << 2 * (bit_depth_ - 8), max_error)
363 364 365
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
          << "an individual roundtrip error > 1";

366
      EXPECT_GE((count_test_block << 2 * (bit_depth_ - 8))/5, total_error)
367 368
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average"
          << " roundtrip error > 1/5 per block";
369 370 371 372

      EXPECT_EQ(0, total_coeff_error)
          << "Error: Extremal 8x8 FDCT/FHT has"
          << "overflow issues in the intermediate steps > 1";
Daniel Kang's avatar
Daniel Kang committed
373
    }
374
  }
Daniel Kang's avatar
Daniel Kang committed
375

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
  void RunInvAccuracyCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 1000;
    DECLARE_ALIGNED_ARRAY(16, int16_t, in, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, coeff, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, kNumCoeffs);
#if CONFIG_VP9_HIGHBITDEPTH
    DECLARE_ALIGNED_ARRAY(16, uint16_t, src16, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint16_t, dst16, kNumCoeffs);
#endif

    for (int i = 0; i < count_test_block; ++i) {
      double out_r[kNumCoeffs];

      // Initialize a test block with input range [-255, 255].
      for (int j = 0; j < kNumCoeffs; ++j) {
        if (bit_depth_ == VPX_BITS_8) {
          src[j] = rnd.Rand8() % 2 ? 255 : 0;
          dst[j] = src[j] > 0 ? 0 : 255;
          in[j] = src[j] - dst[j];
#if CONFIG_VP9_HIGHBITDEPTH
        } else {
          src16[j] = rnd.Rand8() % 2 ? mask_ : 0;
          dst16[j] = src16[j] > 0 ? 0 : mask_;
          in[j] = src16[j] - dst16[j];
#endif
        }
      }

      reference_8x8_dct_2d(in, out_r);
      for (int j = 0; j < kNumCoeffs; ++j)
408
        coeff[j] = static_cast<tran_low_t>(round(out_r[j]));
409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450

      if (bit_depth_ == VPX_BITS_8) {
        ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, pitch_));
#if CONFIG_VP9_HIGHBITDEPTH
      } else {
        ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, CONVERT_TO_BYTEPTR(dst16),
                                            pitch_));
#endif
      }

      for (int j = 0; j < kNumCoeffs; ++j) {
#if CONFIG_VP9_HIGHBITDEPTH
        const uint32_t diff =
            bit_depth_ == VPX_BITS_8 ? dst[j] - src[j] : dst16[j] - src16[j];
#else
        const uint32_t diff = dst[j] - src[j];
#endif
        const uint32_t error = diff * diff;
        EXPECT_GE(1u << 2 * (bit_depth_ - 8), error)
            << "Error: 8x8 IDCT has error " << error
            << " at index " << j;
      }
    }
  }

  void RunFwdAccuracyCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 1000;
    DECLARE_ALIGNED_ARRAY(16, int16_t, in, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, coeff_r, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, coeff, kNumCoeffs);

    for (int i = 0; i < count_test_block; ++i) {
      double out_r[kNumCoeffs];

      // Initialize a test block with input range [-mask_, mask_].
      for (int j = 0; j < kNumCoeffs; ++j)
        in[j] = rnd.Rand8() % 2 == 0 ? mask_ : -mask_;

      RunFwdTxfm(in, coeff, pitch_);
      reference_8x8_dct_2d(in, out_r);
      for (int j = 0; j < kNumCoeffs; ++j)
451
        coeff_r[j] = static_cast<tran_low_t>(round(out_r[j]));
452 453 454 455 456 457 458 459 460 461

      for (int j = 0; j < kNumCoeffs; ++j) {
        const uint32_t diff = coeff[j] - coeff_r[j];
        const uint32_t error = diff * diff;
        EXPECT_GE(9u << 2 * (bit_depth_ - 8), error)
            << "Error: 8x8 DCT has error " << error
            << " at index " << j;
      }
    }
  }
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518

void CompareInvReference(IdctFunc ref_txfm, int thresh) {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 10000;
    const int eob = 12;
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, coeff, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, ref, kNumCoeffs);
#if CONFIG_VP9_HIGHBITDEPTH
    DECLARE_ALIGNED_ARRAY(16, uint16_t, dst16, kNumCoeffs);
    DECLARE_ALIGNED_ARRAY(16, uint16_t, ref16, kNumCoeffs);
#endif
    const int16_t *scan = vp9_default_scan_orders[TX_8X8].scan;

    for (int i = 0; i < count_test_block; ++i) {
      for (int j = 0; j < kNumCoeffs; ++j) {
        if (j < eob) {
          // Random values less than the threshold, either positive or negative
          coeff[scan[j]] = rnd(thresh) * (1-2*(i%2));
        } else {
          coeff[scan[j]] = 0;
        }
        if (bit_depth_ == VPX_BITS_8) {
          dst[j] = 0;
          ref[j] = 0;
#if CONFIG_VP9_HIGHBITDEPTH
        } else {
          dst16[j] = 0;
          ref16[j] = 0;
#endif
        }
      }
      if (bit_depth_ == VPX_BITS_8) {
        ref_txfm(coeff, ref, pitch_);
        ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, dst, pitch_));
#if CONFIG_VP9_HIGHBITDEPTH
      } else {
        ref_txfm(coeff, CONVERT_TO_BYTEPTR(ref16), pitch_);
        ASM_REGISTER_STATE_CHECK(RunInvTxfm(coeff, CONVERT_TO_BYTEPTR(dst16),
                                            pitch_));
#endif
      }

      for (int j = 0; j < kNumCoeffs; ++j) {
#if CONFIG_VP9_HIGHBITDEPTH
        const uint32_t diff =
            bit_depth_ == VPX_BITS_8 ? dst[j] - ref[j] : dst16[j] - ref16[j];
#else
        const uint32_t diff = dst[j] - ref[j];
#endif
        const uint32_t error = diff * diff;
        EXPECT_EQ(0u, error)
            << "Error: 8x8 IDCT has error " << error
            << " at index " << j;
      }
    }
  }
519 520
  int pitch_;
  int tx_type_;
521
  FhtFunc fwd_txfm_ref;
522 523
  vpx_bit_depth_t bit_depth_;
  int mask_;
524
};
Daniel Kang's avatar
Daniel Kang committed
525

526 527
class FwdTrans8x8DCT
    : public FwdTrans8x8TestBase,
528
      public ::testing::TestWithParam<Dct8x8Param> {
529 530 531 532 533 534 535
 public:
  virtual ~FwdTrans8x8DCT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
536
    pitch_    = 8;
537
    fwd_txfm_ref = fdct8x8_ref;
538 539
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
540 541 542 543 544
  }

  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
545
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
546 547
    fwd_txfm_(in, out, stride);
  }
548
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
549
    inv_txfm_(out, dst, stride);
Daniel Kang's avatar
Daniel Kang committed
550
  }
551

552 553
  FdctFunc fwd_txfm_;
  IdctFunc inv_txfm_;
554 555 556 557
};

TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
  RunSignBiasCheck();
558
}
Daniel Kang's avatar
Daniel Kang committed
559

560 561 562 563 564 565 566 567
TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

TEST_P(FwdTrans8x8DCT, ExtremalCheck) {
  RunExtremalCheck();
}

568 569 570 571 572 573 574 575
TEST_P(FwdTrans8x8DCT, FwdAccuracyCheck) {
  RunFwdAccuracyCheck();
}

TEST_P(FwdTrans8x8DCT, InvAccuracyCheck) {
  RunInvAccuracyCheck();
}

576 577
class FwdTrans8x8HT
    : public FwdTrans8x8TestBase,
578
      public ::testing::TestWithParam<Ht8x8Param> {
579 580 581 582 583 584 585 586 587
 public:
  virtual ~FwdTrans8x8HT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
    pitch_    = 8;
    fwd_txfm_ref = fht8x8_ref;
588 589
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
590 591 592 593 594
  }

  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
595
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
596 597
    fwd_txfm_(in, out, stride, tx_type_);
  }
598
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
599 600 601
    inv_txfm_(out, dst, stride, tx_type_);
  }

602 603
  FhtFunc fwd_txfm_;
  IhtFunc inv_txfm_;
604 605 606 607 608 609 610 611 612 613 614 615 616 617
};

TEST_P(FwdTrans8x8HT, SignBiasCheck) {
  RunSignBiasCheck();
}

TEST_P(FwdTrans8x8HT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

TEST_P(FwdTrans8x8HT, ExtremalCheck) {
  RunExtremalCheck();
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
class InvTrans8x8DCT
    : public FwdTrans8x8TestBase,
      public ::testing::TestWithParam<Idct8x8Param> {
 public:
  virtual ~InvTrans8x8DCT() {}

  virtual void SetUp() {
    ref_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    thresh_ = GET_PARAM(2);
    pitch_ = 8;
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
  }

  virtual void TearDown() { libvpx_test::ClearSystemState(); }

 protected:
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
    inv_txfm_(out, dst, stride);
  }
  void RunFwdTxfm(int16_t *out, tran_low_t *dst, int stride) {}

  IdctFunc ref_txfm_;
  IdctFunc inv_txfm_;
  int thresh_;
};

TEST_P(InvTrans8x8DCT, CompareReference) {
  CompareInvReference(ref_txfm_, thresh_);
}

650 651
using std::tr1::make_tuple;

652
#if CONFIG_VP9_HIGHBITDEPTH
653 654 655
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8DCT,
    ::testing::Values(
656
        make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0, VPX_BITS_8),
657
        make_tuple(&vp9_highbd_fdct8x8_c, &idct8x8_10, 0, VPX_BITS_10),
658
        make_tuple(&vp9_highbd_fdct8x8_c, &idct8x8_12, 0, VPX_BITS_12)));
659 660
#else
INSTANTIATE_TEST_CASE_P(
661
    C, FwdTrans8x8DCT,
662 663
    ::testing::Values(
        make_tuple(&vp9_fdct8x8_c, &vp9_idct8x8_64_add_c, 0, VPX_BITS_8)));
664
#endif  // CONFIG_VP9_HIGHBITDEPTH
665 666

#if CONFIG_VP9_HIGHBITDEPTH
667 668 669
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
670
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
671 672 673 674 675 676 677 678
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_10, 0, VPX_BITS_10),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_10, 1, VPX_BITS_10),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_10, 2, VPX_BITS_10),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_10, 3, VPX_BITS_10),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_12, 0, VPX_BITS_12),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_12, 1, VPX_BITS_12),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_12, 2, VPX_BITS_12),
        make_tuple(&vp9_highbd_fht8x8_c, &iht8x8_12, 3, VPX_BITS_12),
679 680 681 682
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 3, VPX_BITS_8)));
#else
683 684
// TODO(jingning): re-enable after this handles the expanded range [0, 65535]
// returned from Rand16().
685 686 687
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
688
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
689 690 691
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 3, VPX_BITS_8)));
692
#endif  // CONFIG_VP9_HIGHBITDEPTH
693

694
#if HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
695 696
// TODO(jingning): re-enable after this handles the expanded range [0, 65535]
// returned from Rand16().
697
INSTANTIATE_TEST_CASE_P(
698
    NEON, FwdTrans8x8DCT,
699
    ::testing::Values(
700 701
        make_tuple(&vp9_fdct8x8_neon, &vp9_idct8x8_64_add_neon, 0,
                   VPX_BITS_8)));
702 703 704
#endif  // HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE

#if HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
705
INSTANTIATE_TEST_CASE_P(
706
    NEON, FwdTrans8x8HT,
707
    ::testing::Values(
708 709 710 711
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 0, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_neon, 3, VPX_BITS_8)));
712
#endif  // HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
713

714
#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
715 716
// TODO(jingning): re-enable after these handle the expanded range [0, 65535]
// returned from Rand16().
717
INSTANTIATE_TEST_CASE_P(
718
    SSE2, FwdTrans8x8DCT,
719
    ::testing::Values(
720 721
        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0,
                   VPX_BITS_8)));
722 723 724
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
725
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0, VPX_BITS_8),
726 727 728
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 3, VPX_BITS_8)));
729 730 731 732 733 734
#endif  // HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE

#if HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8DCT,
    ::testing::Values(
735
        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_c, 0, VPX_BITS_8),
736 737 738 739 740 741 742
        make_tuple(&vp9_highbd_fdct8x8_c,
                   &idct8x8_64_add_10_sse2, 12, VPX_BITS_10),
        make_tuple(&vp9_highbd_fdct8x8_sse2,
                   &idct8x8_64_add_10_sse2, 12, VPX_BITS_10),
        make_tuple(&vp9_highbd_fdct8x8_c,
                   &idct8x8_64_add_12_sse2, 12, VPX_BITS_12),
        make_tuple(&vp9_highbd_fdct8x8_sse2,
743
                   &idct8x8_64_add_12_sse2, 12, VPX_BITS_12)));
744

745 746
// TODO(jingning): re-enable after these handle the expanded range [0, 65535]
// returned from Rand16().
747 748 749
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
750
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 3, VPX_BITS_8)));

// Optimizations take effect at a threshold of 6201, so we use a value close to
// that to test both branches.
INSTANTIATE_TEST_CASE_P(
    SSE2, InvTrans8x8DCT,
    ::testing::Values(
        make_tuple(&idct8x8_10_add_10_c,
                   &idct8x8_10_add_10_sse2, 6225, VPX_BITS_10),
        make_tuple(&idct8x8_10,
                   &idct8x8_64_add_10_sse2, 6225, VPX_BITS_10),
        make_tuple(&idct8x8_10_add_12_c,
                   &idct8x8_10_add_12_sse2, 6225, VPX_BITS_12),
        make_tuple(&idct8x8_12,
                   &idct8x8_64_add_12_sse2, 6225, VPX_BITS_12)));
#endif  // HAVE_SSE2 && CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
769

770 771
#if HAVE_SSSE3 && ARCH_X86_64 && !CONFIG_VP9_HIGHBITDEPTH && \
    !CONFIG_EMULATE_HARDWARE
772 773
// TODO(jingning): re-enable after this handles the expanded range [0, 65535]
// returned from Rand16().
774
INSTANTIATE_TEST_CASE_P(
775
    SSSE3, FwdTrans8x8DCT,
776
    ::testing::Values(
777 778
        make_tuple(&vp9_fdct8x8_ssse3, &vp9_idct8x8_64_add_ssse3, 0,
                   VPX_BITS_8)));
779
#endif
Daniel Kang's avatar
Daniel Kang committed
780
}  // namespace