fdct8x8_test.cc 25.9 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 17 18

#include "./vp9_rtcd.h"
#include "./vpx_dsp_rtcd.h"
19
#include "test/acm_random.h"
20 21
#include "test/clear_system_state.h"
#include "test/register_state_check.h"
22
#include "test/util.h"
23
#include "vp9/common/vp9_entropy.h"
Scott LaVarnway's avatar
Scott LaVarnway committed
24
#include "vp9/common/vp9_scan.h"
25
#include "vpx/vpx_codec.h"
26
#include "vpx/vpx_integer.h"
27
#include "vpx_ports/mem.h"
28

29 30 31 32
using libvpx_test::ACMRandom;

namespace {

33 34
const int kNumCoeffs = 64;
const double kPi = 3.141592653589793238462643383279502884;
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49

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

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,
                        int tx_type);
typedef void (*IhtFunc)(const tran_low_t *in, uint8_t *out, int stride,
                        int tx_type);

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

50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
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
82 83
}

84

85
void fdct8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
86
  vpx_fdct8x8_c(in, out, stride);
87
}
88

89
void fht8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
90
  vp9_fht8x8_c(in, out, stride, tx_type);
91 92
}

93 94
#if CONFIG_VP9_HIGHBITDEPTH
void idct8x8_10(const tran_low_t *in, uint8_t *out, int stride) {
95
  vpx_highbd_idct8x8_64_add_c(in, out, stride, 10);
96 97 98
}

void idct8x8_12(const tran_low_t *in, uint8_t *out, int stride) {
99
  vpx_highbd_idct8x8_64_add_c(in, out, stride, 12);
100 101 102
}

void iht8x8_10(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
103
  vp9_highbd_iht8x8_64_add_c(in, out, stride, tx_type, 10);
104 105 106
}

void iht8x8_12(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
107
  vp9_highbd_iht8x8_64_add_c(in, out, stride, tx_type, 12);
108
}
109 110

void idct8x8_10_add_10_c(const tran_low_t *in, uint8_t *out, int stride) {
111
  vpx_highbd_idct8x8_10_add_c(in, out, stride, 10);
112 113 114
}

void idct8x8_10_add_12_c(const tran_low_t *in, uint8_t *out, int stride) {
115
  vpx_highbd_idct8x8_10_add_c(in, out, stride, 12);
116 117 118 119
}

#if HAVE_SSE2
void idct8x8_10_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
120
  vpx_highbd_idct8x8_10_add_sse2(in, out, stride, 10);
121 122 123
}

void idct8x8_10_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
124
  vpx_highbd_idct8x8_10_add_sse2(in, out, stride, 12);
125 126 127
}

void idct8x8_64_add_10_sse2(const tran_low_t *in, uint8_t *out, int stride) {
128
  vpx_highbd_idct8x8_64_add_sse2(in, out, stride, 10);
129 130 131
}

void idct8x8_64_add_12_sse2(const tran_low_t *in, uint8_t *out, int stride) {
132
  vpx_highbd_idct8x8_64_add_sse2(in, out, stride, 12);
133 134 135
}
#endif  // HAVE_SSE2
#endif  // CONFIG_VP9_HIGHBITDEPTH
136

137
class FwdTrans8x8TestBase {
138
 public:
139
  virtual ~FwdTrans8x8TestBase() {}
140 141

 protected:
142 143
  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;
144

145 146
  void RunSignBiasCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
147 148
    DECLARE_ALIGNED(16, int16_t, test_input_block[64]);
    DECLARE_ALIGNED(16, tran_low_t, test_output_block[64]);
149 150
    int count_sign_block[64][2];
    const int count_test_block = 100000;
Daniel Kang's avatar
Daniel Kang committed
151

152
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
153

154 155 156
    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)
157 158
        test_input_block[j] = ((rnd.Rand16() >> (16 - bit_depth_)) & mask_) -
                              ((rnd.Rand16() >> (16 - bit_depth_)) & mask_);
159
      ASM_REGISTER_STATE_CHECK(
160
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
161

162 163 164 165 166 167 168
      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
169 170

    for (int j = 0; j < 64; ++j) {
171
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
172
      const int max_diff = kSignBiasMaxDiff255;
173
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
174 175 176 177 178 179
          << "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
180 181
    }

182
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
183

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

192 193 194 195 196 197 198
      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
199 200

    for (int j = 0; j < 64; ++j) {
201
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
202
      const int max_diff = kSignBiasMaxDiff15;
203
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
204
          << "Error: 8x8 FDCT/FHT has a sign bias > "
205 206 207 208 209
          << 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
210 211 212
    }
  }

213 214 215 216 217
  void RunRoundTripErrorCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
218 219 220 221
    DECLARE_ALIGNED(16, int16_t, test_input_block[64]);
    DECLARE_ALIGNED(16, tran_low_t, test_temp_block[64]);
    DECLARE_ALIGNED(16, uint8_t, dst[64]);
    DECLARE_ALIGNED(16, uint8_t, src[64]);
222
#if CONFIG_VP9_HIGHBITDEPTH
223 224
    DECLARE_ALIGNED(16, uint16_t, dst16[64]);
    DECLARE_ALIGNED(16, uint16_t, src16[64]);
225
#endif
Daniel Kang's avatar
Daniel Kang committed
226

227
    for (int i = 0; i < count_test_block; ++i) {
228
      // Initialize a test block with input range [-mask_, mask_].
229
      for (int j = 0; j < 64; ++j) {
230 231 232 233 234 235 236 237 238 239 240
        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
        }
241
      }
Daniel Kang's avatar
Daniel Kang committed
242

243
      ASM_REGISTER_STATE_CHECK(
244 245 246 247 248 249 250 251 252 253 254 255
          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;
          }
      }
256 257 258 259 260 261 262 263 264
      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
      }
265 266

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

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

284
    EXPECT_GE((count_test_block << 2 * (bit_depth_ - 8))/5, total_error)
285 286 287
      << "Error: 8x8 FDCT/IDCT or FHT/IHT has average roundtrip "
      << "error > 1/5 per block";
  }
Daniel Kang's avatar
Daniel Kang committed
288

289 290 291 292
  void RunExtremalCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
293
    int total_coeff_error = 0;
294
    const int count_test_block = 100000;
295 296 297 298 299
    DECLARE_ALIGNED(16, int16_t, test_input_block[64]);
    DECLARE_ALIGNED(16, tran_low_t, test_temp_block[64]);
    DECLARE_ALIGNED(16, tran_low_t, ref_temp_block[64]);
    DECLARE_ALIGNED(16, uint8_t, dst[64]);
    DECLARE_ALIGNED(16, uint8_t, src[64]);
300
#if CONFIG_VP9_HIGHBITDEPTH
301 302
    DECLARE_ALIGNED(16, uint16_t, dst16[64]);
    DECLARE_ALIGNED(16, uint16_t, src16[64]);
303
#endif
Daniel Kang's avatar
Daniel Kang committed
304

305
    for (int i = 0; i < count_test_block; ++i) {
306
      // Initialize a test block with input range [-mask_, mask_].
307
      for (int j = 0; j < 64; ++j) {
308 309 310 311 312 313 314 315 316 317 318 319 320
        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
321
        } else {
322 323 324 325 326 327 328 329 330 331 332 333
          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
334
        }
335
      }
Daniel Kang's avatar
Daniel Kang committed
336

337
      ASM_REGISTER_STATE_CHECK(
338
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
339
      ASM_REGISTER_STATE_CHECK(
340
          fwd_txfm_ref(test_input_block, ref_temp_block, pitch_, tx_type_));
341 342 343 344 345 346 347 348 349
      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
      }
350 351

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

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

367
      EXPECT_GE(1 << 2 * (bit_depth_ - 8), max_error)
368 369 370
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
          << "an individual roundtrip error > 1";

371
      EXPECT_GE((count_test_block << 2 * (bit_depth_ - 8))/5, total_error)
372 373
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has average"
          << " roundtrip error > 1/5 per block";
374 375 376 377

      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
378
    }
379
  }
Daniel Kang's avatar
Daniel Kang committed
380

381 382 383
  void RunInvAccuracyCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 1000;
384 385 386 387
    DECLARE_ALIGNED(16, int16_t, in[kNumCoeffs]);
    DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint8_t, src[kNumCoeffs]);
388
#if CONFIG_VP9_HIGHBITDEPTH
389 390
    DECLARE_ALIGNED(16, uint16_t, src16[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
#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)
413
        coeff[j] = static_cast<tran_low_t>(round(out_r[j]));
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

      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;
442 443 444
    DECLARE_ALIGNED(16, int16_t, in[kNumCoeffs]);
    DECLARE_ALIGNED(16, tran_low_t, coeff_r[kNumCoeffs]);
    DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
445 446 447 448 449 450 451 452 453 454 455

    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)
456
        coeff_r[j] = static_cast<tran_low_t>(round(out_r[j]));
457 458 459 460 461 462 463 464 465 466

      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;
      }
    }
  }
467 468 469 470 471

void CompareInvReference(IdctFunc ref_txfm, int thresh) {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    const int count_test_block = 10000;
    const int eob = 12;
472 473 474
    DECLARE_ALIGNED(16, tran_low_t, coeff[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint8_t, dst[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint8_t, ref[kNumCoeffs]);
475
#if CONFIG_VP9_HIGHBITDEPTH
476 477
    DECLARE_ALIGNED(16, uint16_t, dst16[kNumCoeffs]);
    DECLARE_ALIGNED(16, uint16_t, ref16[kNumCoeffs]);
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 519 520 521 522 523
#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;
      }
    }
  }
524 525
  int pitch_;
  int tx_type_;
526
  FhtFunc fwd_txfm_ref;
527 528
  vpx_bit_depth_t bit_depth_;
  int mask_;
529
};
Daniel Kang's avatar
Daniel Kang committed
530

531 532
class FwdTrans8x8DCT
    : public FwdTrans8x8TestBase,
533
      public ::testing::TestWithParam<Dct8x8Param> {
534 535 536 537 538 539 540
 public:
  virtual ~FwdTrans8x8DCT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
541
    pitch_    = 8;
542
    fwd_txfm_ref = fdct8x8_ref;
543 544
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
545 546 547 548 549
  }

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

 protected:
550
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
551 552
    fwd_txfm_(in, out, stride);
  }
553
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
554
    inv_txfm_(out, dst, stride);
Daniel Kang's avatar
Daniel Kang committed
555
  }
556

557 558
  FdctFunc fwd_txfm_;
  IdctFunc inv_txfm_;
559 560 561 562
};

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

565 566 567 568 569 570 571 572
TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

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

573 574 575 576 577 578 579 580
TEST_P(FwdTrans8x8DCT, FwdAccuracyCheck) {
  RunFwdAccuracyCheck();
}

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

581 582
class FwdTrans8x8HT
    : public FwdTrans8x8TestBase,
583
      public ::testing::TestWithParam<Ht8x8Param> {
584 585 586 587 588 589 590 591 592
 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;
593 594
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
595 596 597 598 599
  }

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

 protected:
600
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
601 602
    fwd_txfm_(in, out, stride, tx_type_);
  }
603
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
604 605 606
    inv_txfm_(out, dst, stride, tx_type_);
  }

607 608
  FhtFunc fwd_txfm_;
  IhtFunc inv_txfm_;
609 610 611 612 613 614 615 616 617 618 619 620 621 622
};

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

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

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

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 650 651 652 653 654
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_);
}

655 656
using std::tr1::make_tuple;

657
#if CONFIG_VP9_HIGHBITDEPTH
658 659 660
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8DCT,
    ::testing::Values(
661
        make_tuple(&vpx_fdct8x8_c, &vpx_idct8x8_64_add_c, 0, VPX_BITS_8),
662 663
        make_tuple(&vpx_highbd_fdct8x8_c, &idct8x8_10, 0, VPX_BITS_10),
        make_tuple(&vpx_highbd_fdct8x8_c, &idct8x8_12, 0, VPX_BITS_12)));
664 665
#else
INSTANTIATE_TEST_CASE_P(
666
    C, FwdTrans8x8DCT,
667
    ::testing::Values(
668
        make_tuple(&vpx_fdct8x8_c, &vpx_idct8x8_64_add_c, 0, VPX_BITS_8)));
669
#endif  // CONFIG_VP9_HIGHBITDEPTH
670 671

#if CONFIG_VP9_HIGHBITDEPTH
672 673 674
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
675
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
676 677 678 679 680 681 682 683
        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),
684 685 686 687 688 689 690
        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
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
691
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
692 693 694
        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)));
695
#endif  // CONFIG_VP9_HIGHBITDEPTH
696

697
#if HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
698
INSTANTIATE_TEST_CASE_P(
699
    NEON, FwdTrans8x8DCT,
700
    ::testing::Values(
701
        make_tuple(&vpx_fdct8x8_neon, &vpx_idct8x8_64_add_neon, 0,
702
                   VPX_BITS_8)));
703 704 705
#endif  // HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE

#if HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
706
INSTANTIATE_TEST_CASE_P(
707
    NEON, FwdTrans8x8HT,
708
    ::testing::Values(
709 710 711 712
        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)));
713
#endif  // HAVE_NEON && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
714

715
#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
716
INSTANTIATE_TEST_CASE_P(
717
    SSE2, FwdTrans8x8DCT,
718
    ::testing::Values(
719
        make_tuple(&vpx_fdct8x8_sse2, &vpx_idct8x8_64_add_sse2, 0,
720
                   VPX_BITS_8)));
721 722 723
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
724
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0, VPX_BITS_8),
725 726 727
        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)));
728 729 730 731 732 733
#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(
734
        make_tuple(&vpx_fdct8x8_sse2, &vpx_idct8x8_64_add_c, 0, VPX_BITS_8),
735
        make_tuple(&vpx_highbd_fdct8x8_c,
736
                   &idct8x8_64_add_10_sse2, 12, VPX_BITS_10),
737
        make_tuple(&vpx_highbd_fdct8x8_sse2,
738
                   &idct8x8_64_add_10_sse2, 12, VPX_BITS_10),
739
        make_tuple(&vpx_highbd_fdct8x8_c,
740
                   &idct8x8_64_add_12_sse2, 12, VPX_BITS_12),
741
        make_tuple(&vpx_highbd_fdct8x8_sse2,
742
                   &idct8x8_64_add_12_sse2, 12, VPX_BITS_12)));
743 744 745 746

INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
747
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
        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
766

Johann's avatar
Johann committed
767 768
#if HAVE_SSSE3 && CONFIG_USE_X86INC && ARCH_X86_64 && \
    !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
769
INSTANTIATE_TEST_CASE_P(
770
    SSSE3, FwdTrans8x8DCT,
771
    ::testing::Values(
772
        make_tuple(&vpx_fdct8x8_ssse3, &vpx_idct8x8_64_add_ssse3, 0,
773
                   VPX_BITS_8)));
774
#endif
775

776
#if HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
777 778 779
INSTANTIATE_TEST_CASE_P(
    MSA, FwdTrans8x8DCT,
    ::testing::Values(
780
        make_tuple(&vpx_fdct8x8_msa, &vpx_idct8x8_64_add_msa, 0, VPX_BITS_8)));
781 782 783
INSTANTIATE_TEST_CASE_P(
    MSA, FwdTrans8x8HT,
    ::testing::Values(
784 785 786 787
        make_tuple(&vp9_fht8x8_msa, &vp9_iht8x8_64_add_msa, 0, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_msa, &vp9_iht8x8_64_add_msa, 1, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_msa, &vp9_iht8x8_64_add_msa, 2, VPX_BITS_8),
        make_tuple(&vp9_fht8x8_msa, &vp9_iht8x8_64_add_msa, 3, VPX_BITS_8)));
788
#endif  // HAVE_MSA && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
Daniel Kang's avatar
Daniel Kang committed
789
}  // namespace