fdct8x8_test.cc 25 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
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,
68
                        int tx_type);
69
typedef void (*IhtFunc)(const tran_low_t *in, uint8_t *out, int stride,
70
                        int tx_type);
71

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

76
void fdct8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
77
  vp9_fdct8x8_c(in, out, stride);
Jingning Han's avatar
Jingning Han committed
78
}
79

80
void fht8x8_ref(const int16_t *in, tran_low_t *out, int stride, int tx_type) {
81
  vp9_fht8x8_c(in, out, stride, tx_type);
Jingning Han's avatar
Jingning Han committed
82
83
}

84
85
#if CONFIG_VP9_HIGHBITDEPTH
void idct8x8_10(const tran_low_t *in, uint8_t *out, int stride) {
86
  vp9_highbd_idct8x8_64_add_c(in, out, stride, 10);
87
88
89
}

void idct8x8_12(const tran_low_t *in, uint8_t *out, int stride) {
90
  vp9_highbd_idct8x8_64_add_c(in, out, stride, 12);
91
92
93
}

void iht8x8_10(const tran_low_t *in, uint8_t *out, int stride, int tx_type) {
94
  vp9_highbd_iht8x8_64_add_c(in, out, stride, tx_type, 10);
95
96
97
}

void iht8x8_12(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, 12);
99
}
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

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
127

128
class FwdTrans8x8TestBase {
Jingning Han's avatar
Jingning Han committed
129
 public:
130
  virtual ~FwdTrans8x8TestBase() {}
Jingning Han's avatar
Jingning Han committed
131
132

 protected:
133
134
  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;
Jingning Han's avatar
Jingning Han committed
135

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

143
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
144

145
146
147
    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)
148
149
        test_input_block[j] = ((rnd.Rand16() >> (16 - bit_depth_)) & mask_) -
                              ((rnd.Rand16() >> (16 - bit_depth_)) & mask_);
150
      ASM_REGISTER_STATE_CHECK(
151
          RunFwdTxfm(test_input_block, test_output_block, pitch_));
Daniel Kang's avatar
Daniel Kang committed
152

153
154
155
156
157
158
159
      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
160
161

    for (int j = 0; j < 64; ++j) {
162
163
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
      const int max_diff = 1125;
164
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
165
166
167
168
169
170
          << "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
171
172
    }

173
    memset(count_sign_block, 0, sizeof(count_sign_block));
Daniel Kang's avatar
Daniel Kang committed
174

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

183
184
185
186
187
188
189
      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
190
191

    for (int j = 0; j < 64; ++j) {
192
193
      const int diff = abs(count_sign_block[j][0] - count_sign_block[j][1]);
      const int max_diff = 10000;
194
      EXPECT_LT(diff, max_diff << (bit_depth_ - 8))
195
196
197
198
199
200
          << "Error: 4x4 FDCT/FHT has a sign bias > "
          << 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
201
202
203
    }
  }

204
205
206
207
208
  void RunRoundTripErrorCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
    const int count_test_block = 100000;
209
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
210
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_temp_block, 64);
211
212
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
213
214
215
216
#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
217

218
    for (int i = 0; i < count_test_block; ++i) {
219
      // Initialize a test block with input range [-mask_, mask_].
220
      for (int j = 0; j < 64; ++j) {
221
222
223
224
225
226
227
228
229
230
231
        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
        }
232
      }
Daniel Kang's avatar
Daniel Kang committed
233

234
      ASM_REGISTER_STATE_CHECK(
235
236
237
238
239
240
241
242
243
244
245
246
          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;
          }
      }
247
248
249
250
251
252
253
254
255
      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
      }
256
257

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

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

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

280
281
282
283
  void RunExtremalCheck() {
    ACMRandom rnd(ACMRandom::DeterministicSeed());
    int max_error = 0;
    int total_error = 0;
284
    int total_coeff_error = 0;
285
    const int count_test_block = 100000;
286
    DECLARE_ALIGNED_ARRAY(16, int16_t, test_input_block, 64);
287
288
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, test_temp_block, 64);
    DECLARE_ALIGNED_ARRAY(16, tran_low_t, ref_temp_block, 64);
289
290
    DECLARE_ALIGNED_ARRAY(16, uint8_t, dst, 64);
    DECLARE_ALIGNED_ARRAY(16, uint8_t, src, 64);
291
292
293
294
#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
295

296
    for (int i = 0; i < count_test_block; ++i) {
297
      // Initialize a test block with input range [-mask_, mask_].
298
      for (int j = 0; j < 64; ++j) {
299
300
301
302
303
304
305
306
307
308
309
310
311
        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
312
        } else {
313
314
315
316
317
318
319
320
321
322
323
324
          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
325
        }
326
      }
Daniel Kang's avatar
Daniel Kang committed
327

328
      ASM_REGISTER_STATE_CHECK(
329
          RunFwdTxfm(test_input_block, test_temp_block, pitch_));
330
      ASM_REGISTER_STATE_CHECK(
331
          fwd_txfm_ref(test_input_block, ref_temp_block, pitch_, tx_type_));
332
333
334
335
336
337
338
339
340
      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
      }
341
342

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

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

358
      EXPECT_GE(1 << 2 * (bit_depth_ - 8), max_error)
359
360
361
          << "Error: Extremal 8x8 FDCT/IDCT or FHT/IHT has"
          << "an individual roundtrip error > 1";

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

      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
369
    }
370
  }
Daniel Kang's avatar
Daniel Kang committed
371

372
373
374
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
  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)
404
        coeff[j] = static_cast<tran_low_t>(round(out_r[j]));
405
406
407
408
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

      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)
447
        coeff_r[j] = static_cast<tran_low_t>(round(out_r[j]));
448
449
450
451
452
453
454
455
456
457

      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;
      }
    }
  }
458
459
460
461
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

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;
      }
    }
  }
515
516
  int pitch_;
  int tx_type_;
517
  FhtFunc fwd_txfm_ref;
518
519
  vpx_bit_depth_t bit_depth_;
  int mask_;
520
};
Daniel Kang's avatar
Daniel Kang committed
521

522
523
class FwdTrans8x8DCT
    : public FwdTrans8x8TestBase,
524
      public ::testing::TestWithParam<Dct8x8Param> {
525
526
527
528
529
530
531
 public:
  virtual ~FwdTrans8x8DCT() {}

  virtual void SetUp() {
    fwd_txfm_ = GET_PARAM(0);
    inv_txfm_ = GET_PARAM(1);
    tx_type_  = GET_PARAM(2);
532
    pitch_    = 8;
533
    fwd_txfm_ref = fdct8x8_ref;
534
535
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
536
537
538
539
540
  }

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

 protected:
541
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
542
543
    fwd_txfm_(in, out, stride);
  }
544
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
545
    inv_txfm_(out, dst, stride);
Daniel Kang's avatar
Daniel Kang committed
546
  }
547

548
549
  FdctFunc fwd_txfm_;
  IdctFunc inv_txfm_;
550
551
552
553
};

TEST_P(FwdTrans8x8DCT, SignBiasCheck) {
  RunSignBiasCheck();
Jingning Han's avatar
Jingning Han committed
554
}
Daniel Kang's avatar
Daniel Kang committed
555

556
557
558
559
560
561
562
563
TEST_P(FwdTrans8x8DCT, RoundTripErrorCheck) {
  RunRoundTripErrorCheck();
}

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

564
565
566
567
568
569
570
571
TEST_P(FwdTrans8x8DCT, FwdAccuracyCheck) {
  RunFwdAccuracyCheck();
}

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

572
573
class FwdTrans8x8HT
    : public FwdTrans8x8TestBase,
574
      public ::testing::TestWithParam<Ht8x8Param> {
575
576
577
578
579
580
581
582
583
 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;
584
585
    bit_depth_ = GET_PARAM(3);
    mask_ = (1 << bit_depth_) - 1;
586
587
588
589
590
  }

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

 protected:
591
  void RunFwdTxfm(int16_t *in, tran_low_t *out, int stride) {
592
593
    fwd_txfm_(in, out, stride, tx_type_);
  }
594
  void RunInvTxfm(tran_low_t *out, uint8_t *dst, int stride) {
595
596
597
    inv_txfm_(out, dst, stride, tx_type_);
  }

598
599
  FhtFunc fwd_txfm_;
  IhtFunc inv_txfm_;
600
601
602
603
604
605
606
607
608
609
610
611
612
613
};

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

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

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

614
615
616
617
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
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_);
}

646
647
using std::tr1::make_tuple;

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

#if CONFIG_VP9_HIGHBITDEPTH
663
664
665
INSTANTIATE_TEST_CASE_P(
    C, FwdTrans8x8HT,
    ::testing::Values(
666
667
668
669
670
671
672
673
        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),
674
675
676
677
678
679
680
681
682
683
684
685
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
        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(
        make_tuple(&vp9_fht8x8_c, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
        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)));
686
#endif  // CONFIG_VP9_HIGHBITDEPTH
687

688
#if HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
James Zern's avatar
James Zern committed
689
690
691
INSTANTIATE_TEST_CASE_P(
    NEON, FwdTrans8x8DCT,
    ::testing::Values(
692
693
        make_tuple(&vp9_fdct8x8_neon, &vp9_idct8x8_64_add_neon, 0,
                   VPX_BITS_8)));
James Zern's avatar
James Zern committed
694
695
696
INSTANTIATE_TEST_CASE_P(
    DISABLED_NEON, FwdTrans8x8HT,
    ::testing::Values(
697
698
699
700
        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)));
701
#endif  // HAVE_NEON_ASM && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
James Zern's avatar
James Zern committed
702

703
#if HAVE_SSE2 && !CONFIG_VP9_HIGHBITDEPTH && !CONFIG_EMULATE_HARDWARE
704
705
706
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8DCT,
    ::testing::Values(
707
708
        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_sse2, 0,
                   VPX_BITS_8)));
709
710
711
INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
712
713
714
715
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_sse2, 0, VPX_BITS_8),
        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)));
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
#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(
        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,
                   &idct8x8_64_add_12_sse2, 12, VPX_BITS_12),
        make_tuple(&vp9_fdct8x8_sse2, &vp9_idct8x8_64_add_c, 0, VPX_BITS_8)));


INSTANTIATE_TEST_CASE_P(
    SSE2, FwdTrans8x8HT,
    ::testing::Values(
        make_tuple(&vp9_fht8x8_sse2, &vp9_iht8x8_64_add_c, 0, VPX_BITS_8),
        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
755

756
757
#if HAVE_SSSE3 && ARCH_X86_64 && !CONFIG_VP9_HIGHBITDEPTH && \
    !CONFIG_EMULATE_HARDWARE
758
INSTANTIATE_TEST_CASE_P(
759
    SSSE3, FwdTrans8x8DCT,
760
    ::testing::Values(
761
762
        make_tuple(&vp9_fdct8x8_ssse3, &vp9_idct8x8_64_add_ssse3, 0,
                   VPX_BITS_8)));
763
#endif
Daniel Kang's avatar
Daniel Kang committed
764
}  // namespace