1/*
2 *  Copyright (c) 2017 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 "third_party/googletest/src/include/gtest/gtest.h"
12
13#include "./vpx_dsp_rtcd.h"
14
15#include "test/acm_random.h"
16#include "test/buffer.h"
17#include "test/register_state_check.h"
18#include "vpx_ports/mem.h"
19#include "vpx_ports/vpx_timer.h"
20
21namespace {
22
23using ::libvpx_test::ACMRandom;
24using ::libvpx_test::Buffer;
25
26typedef void (*AvgPredFunc)(uint8_t *a, const uint8_t *b, int w, int h,
27                            const uint8_t *c, int c_stride);
28
29uint8_t avg_with_rounding(uint8_t a, uint8_t b) { return (a + b + 1) >> 1; }
30
31void reference_pred(const uint8_t *pred, const Buffer<uint8_t> &ref, int width,
32                    int height, uint8_t *avg) {
33  for (int y = 0; y < height; ++y) {
34    for (int x = 0; x < width; ++x) {
35      avg[y * width + x] = avg_with_rounding(
36          pred[y * width + x], ref.TopLeftPixel()[y * ref.stride() + x]);
37    }
38  }
39}
40
41class AvgPredTest : public ::testing::TestWithParam<AvgPredFunc> {
42 public:
43  virtual void SetUp() {
44    avg_pred_func_ = GetParam();
45    rnd_.Reset(ACMRandom::DeterministicSeed());
46  }
47
48 protected:
49  AvgPredFunc avg_pred_func_;
50  ACMRandom rnd_;
51};
52
53void fill(ACMRandom *r, uint8_t *a, const int width, const int height) {
54  for (int y = 0; y < height; ++y) {
55    for (int x = 0; x < width; ++x) {
56      a[x + width * y] = r->Rand8();
57    }
58  }
59}
60
61TEST_P(AvgPredTest, SizeCombinations) {
62  // This is called as part of the sub pixel variance. As such it must be one of
63  // the variance block sizes.
64
65  DECLARE_ALIGNED(16, uint8_t, pred[64 * 64]);
66  DECLARE_ALIGNED(16, uint8_t, avg_ref[64 * 64]);
67  DECLARE_ALIGNED(16, uint8_t, avg_chk[64 * 64]);
68
69  for (int width_pow = 2; width_pow <= 6; ++width_pow) {
70    for (int height_pow = width_pow - 1; height_pow <= width_pow + 1;
71         ++height_pow) {
72      // Don't test 4x2 or 64x128
73      if (height_pow == 1 || height_pow == 7) continue;
74
75      // The sse2 special-cases when ref width == stride, so make sure to test
76      // it.
77      for (int ref_padding = 0; ref_padding < 2; ref_padding++) {
78        const int width = 1 << width_pow;
79        const int height = 1 << height_pow;
80        // Only the reference buffer may have a stride not equal to width.
81        Buffer<uint8_t> ref =
82            Buffer<uint8_t>(width, height, ref_padding ? 8 : 0);
83
84        fill(&rnd_, pred, width, height);
85        ref.Set(&rnd_, &ACMRandom::Rand8);
86
87        reference_pred(pred, ref, width, height, avg_ref);
88        ASM_REGISTER_STATE_CHECK(avg_pred_func_(
89            avg_chk, pred, width, height, ref.TopLeftPixel(), ref.stride()));
90        ASSERT_EQ(memcmp(avg_ref, avg_chk, sizeof(*avg_ref) * width * height),
91                  0);
92      }
93    }
94  }
95}
96
97TEST_P(AvgPredTest, CompareReferenceRandom) {
98  const int width = 64;
99  const int height = 32;
100  Buffer<uint8_t> ref = Buffer<uint8_t>(width, height, 8);
101  DECLARE_ALIGNED(16, uint8_t, pred[width * height]);
102  DECLARE_ALIGNED(16, uint8_t, avg_ref[width * height]);
103  DECLARE_ALIGNED(16, uint8_t, avg_chk[width * height]);
104
105  for (int i = 0; i < 500; ++i) {
106    fill(&rnd_, pred, width, height);
107    ref.Set(&rnd_, &ACMRandom::Rand8);
108
109    reference_pred(pred, ref, width, height, avg_ref);
110    ASM_REGISTER_STATE_CHECK(avg_pred_func_(avg_chk, pred, width, height,
111                                            ref.TopLeftPixel(), ref.stride()));
112    ASSERT_EQ(memcmp(avg_ref, avg_chk, sizeof(*avg_ref) * width * height), 0);
113  }
114}
115
116TEST_P(AvgPredTest, DISABLED_Speed) {
117  DECLARE_ALIGNED(16, uint8_t, pred[64 * 64]);
118  DECLARE_ALIGNED(16, uint8_t, avg[64 * 64]);
119
120  for (int width_pow = 2; width_pow <= 6; ++width_pow) {
121    for (int height_pow = width_pow - 1; height_pow <= width_pow + 1;
122         ++height_pow) {
123      // Don't test 4x2 or 64x128
124      if (height_pow == 1 || height_pow == 7) continue;
125
126      for (int ref_padding = 0; ref_padding < 2; ref_padding++) {
127        const int width = 1 << width_pow;
128        const int height = 1 << height_pow;
129        Buffer<uint8_t> ref =
130            Buffer<uint8_t>(width, height, ref_padding ? 8 : 0);
131
132        fill(&rnd_, pred, width, height);
133        ref.Set(&rnd_, &ACMRandom::Rand8);
134
135        vpx_usec_timer timer;
136        vpx_usec_timer_start(&timer);
137        for (int i = 0; i < 10000000 / (width * height); ++i) {
138          avg_pred_func_(avg, pred, width, height, ref.TopLeftPixel(),
139                         ref.stride());
140        }
141        vpx_usec_timer_mark(&timer);
142
143        const int elapsed_time =
144            static_cast<int>(vpx_usec_timer_elapsed(&timer));
145        printf("Average Test (ref_padding: %d) %dx%d time: %5d us\n",
146               ref_padding, width, height, elapsed_time);
147      }
148    }
149  }
150}
151
152INSTANTIATE_TEST_CASE_P(C, AvgPredTest,
153                        ::testing::Values(&vpx_comp_avg_pred_c));
154
155#if HAVE_SSE2
156INSTANTIATE_TEST_CASE_P(SSE2, AvgPredTest,
157                        ::testing::Values(&vpx_comp_avg_pred_sse2));
158#endif  // HAVE_SSE2
159#if HAVE_VSX
160INSTANTIATE_TEST_CASE_P(VSX, AvgPredTest,
161                        ::testing::Values(&vpx_comp_avg_pred_vsx));
162#endif  // HAVE_VSX
163}  // namespace
164