1/*
2 *  Copyright (c) 2012 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
12#include <string.h>
13#include "test/acm_random.h"
14#include "test/clear_system_state.h"
15#include "test/register_state_check.h"
16#include "third_party/googletest/src/include/gtest/gtest.h"
17
18#include "./vpx_config.h"
19#include "./vp8_rtcd.h"
20#include "vp8/common/blockd.h"
21#include "vpx_mem/vpx_mem.h"
22
23namespace {
24
25using libvpx_test::ACMRandom;
26
27class IntraPredBase {
28 public:
29  virtual ~IntraPredBase() { libvpx_test::ClearSystemState(); }
30
31 protected:
32  void SetupMacroblock(MACROBLOCKD *mbptr,
33                       MODE_INFO *miptr,
34                       uint8_t *data,
35                       int block_size,
36                       int stride,
37                       int num_planes) {
38    mbptr_ = mbptr;
39    miptr_ = miptr;
40    mbptr_->up_available = 1;
41    mbptr_->left_available = 1;
42    mbptr_->mode_info_context = miptr_;
43    stride_ = stride;
44    block_size_ = block_size;
45    num_planes_ = num_planes;
46    for (int p = 0; p < num_planes; p++)
47      data_ptr_[p] = data + stride * (block_size + 1) * p +
48                     stride + block_size;
49  }
50
51  void FillRandom() {
52    // Fill edges with random data
53    ACMRandom rnd(ACMRandom::DeterministicSeed());
54    for (int p = 0; p < num_planes_; p++) {
55      for (int x = -1 ; x <= block_size_; x++)
56        data_ptr_[p][x - stride_] = rnd.Rand8();
57      for (int y = 0; y < block_size_; y++)
58        data_ptr_[p][y * stride_ - 1] = rnd.Rand8();
59    }
60  }
61
62  virtual void Predict(MB_PREDICTION_MODE mode) = 0;
63
64  void SetLeftUnavailable() {
65    mbptr_->left_available = 0;
66    for (int p = 0; p < num_planes_; p++)
67      for (int i = -1; i < block_size_; ++i)
68        data_ptr_[p][stride_ * i - 1] = 129;
69  }
70
71  void SetTopUnavailable() {
72    mbptr_->up_available = 0;
73    for (int p = 0; p < num_planes_; p++)
74      memset(&data_ptr_[p][-1 - stride_], 127, block_size_ + 2);
75  }
76
77  void SetTopLeftUnavailable() {
78    SetLeftUnavailable();
79    SetTopUnavailable();
80  }
81
82  int BlockSizeLog2Min1() const {
83    switch (block_size_) {
84      case 16:
85        return 3;
86      case 8:
87        return 2;
88      default:
89        return 0;
90    }
91  }
92
93  // check DC prediction output against a reference
94  void CheckDCPrediction() const {
95    for (int p = 0; p < num_planes_; p++) {
96      // calculate expected DC
97      int expected;
98      if (mbptr_->up_available || mbptr_->left_available) {
99        int sum = 0, shift = BlockSizeLog2Min1() + mbptr_->up_available +
100                             mbptr_->left_available;
101        if (mbptr_->up_available)
102          for (int x = 0; x < block_size_; x++)
103            sum += data_ptr_[p][x - stride_];
104        if (mbptr_->left_available)
105          for (int y = 0; y < block_size_; y++)
106            sum += data_ptr_[p][y * stride_ - 1];
107        expected = (sum + (1 << (shift - 1))) >> shift;
108      } else {
109        expected = 0x80;
110      }
111      // check that all subsequent lines are equal to the first
112      for (int y = 1; y < block_size_; ++y)
113        ASSERT_EQ(0, memcmp(data_ptr_[p], &data_ptr_[p][y * stride_],
114                            block_size_));
115      // within the first line, ensure that each pixel has the same value
116      for (int x = 1; x < block_size_; ++x)
117        ASSERT_EQ(data_ptr_[p][0], data_ptr_[p][x]);
118      // now ensure that that pixel has the expected (DC) value
119      ASSERT_EQ(expected, data_ptr_[p][0]);
120    }
121  }
122
123  // check V prediction output against a reference
124  void CheckVPrediction() const {
125    // check that all lines equal the top border
126    for (int p = 0; p < num_planes_; p++)
127      for (int y = 0; y < block_size_; y++)
128        ASSERT_EQ(0, memcmp(&data_ptr_[p][-stride_],
129                            &data_ptr_[p][y * stride_], block_size_));
130  }
131
132  // check H prediction output against a reference
133  void CheckHPrediction() const {
134    // for each line, ensure that each pixel is equal to the left border
135    for (int p = 0; p < num_planes_; p++)
136      for (int y = 0; y < block_size_; y++)
137        for (int x = 0; x < block_size_; x++)
138          ASSERT_EQ(data_ptr_[p][-1 + y * stride_],
139                    data_ptr_[p][x + y * stride_]);
140  }
141
142  static int ClipByte(int value) {
143    if (value > 255)
144      return 255;
145    else if (value < 0)
146      return 0;
147    return value;
148  }
149
150  // check TM prediction output against a reference
151  void CheckTMPrediction() const {
152    for (int p = 0; p < num_planes_; p++)
153      for (int y = 0; y < block_size_; y++)
154        for (int x = 0; x < block_size_; x++) {
155          const int expected = ClipByte(data_ptr_[p][x - stride_]
156                                      + data_ptr_[p][stride_ * y - 1]
157                                      - data_ptr_[p][-1 - stride_]);
158          ASSERT_EQ(expected, data_ptr_[p][y * stride_ + x]);
159       }
160  }
161
162  // Actual test
163  void RunTest() {
164    {
165      SCOPED_TRACE("DC_PRED");
166      FillRandom();
167      Predict(DC_PRED);
168      CheckDCPrediction();
169    }
170    {
171      SCOPED_TRACE("DC_PRED LEFT");
172      FillRandom();
173      SetLeftUnavailable();
174      Predict(DC_PRED);
175      CheckDCPrediction();
176    }
177    {
178      SCOPED_TRACE("DC_PRED TOP");
179      FillRandom();
180      SetTopUnavailable();
181      Predict(DC_PRED);
182      CheckDCPrediction();
183    }
184    {
185      SCOPED_TRACE("DC_PRED TOP_LEFT");
186      FillRandom();
187      SetTopLeftUnavailable();
188      Predict(DC_PRED);
189      CheckDCPrediction();
190    }
191    {
192      SCOPED_TRACE("H_PRED");
193      FillRandom();
194      Predict(H_PRED);
195      CheckHPrediction();
196    }
197    {
198      SCOPED_TRACE("V_PRED");
199      FillRandom();
200      Predict(V_PRED);
201      CheckVPrediction();
202    }
203    {
204      SCOPED_TRACE("TM_PRED");
205      FillRandom();
206      Predict(TM_PRED);
207      CheckTMPrediction();
208    }
209  }
210
211  MACROBLOCKD *mbptr_;
212  MODE_INFO *miptr_;
213  uint8_t *data_ptr_[2];  // in the case of Y, only [0] is used
214  int stride_;
215  int block_size_;
216  int num_planes_;
217};
218
219typedef void (*IntraPredYFunc)(MACROBLOCKD *x,
220                               uint8_t *yabove_row,
221                               uint8_t *yleft,
222                               int left_stride,
223                               uint8_t *ypred_ptr,
224                               int y_stride);
225
226class IntraPredYTest
227    : public IntraPredBase,
228      public ::testing::TestWithParam<IntraPredYFunc> {
229 public:
230  static void SetUpTestCase() {
231    mb_ = reinterpret_cast<MACROBLOCKD*>(
232        vpx_memalign(32, sizeof(MACROBLOCKD)));
233    mi_ = reinterpret_cast<MODE_INFO*>(
234        vpx_memalign(32, sizeof(MODE_INFO)));
235    data_array_ = reinterpret_cast<uint8_t*>(
236        vpx_memalign(kDataAlignment, kDataBufferSize));
237  }
238
239  static void TearDownTestCase() {
240    vpx_free(data_array_);
241    vpx_free(mi_);
242    vpx_free(mb_);
243    data_array_ = NULL;
244  }
245
246 protected:
247  static const int kBlockSize = 16;
248  static const int kDataAlignment = 16;
249  static const int kStride = kBlockSize * 3;
250  // We use 48 so that the data pointer of the first pixel in each row of
251  // each macroblock is 16-byte aligned, and this gives us access to the
252  // top-left and top-right corner pixels belonging to the top-left/right
253  // macroblocks.
254  // We use 17 lines so we have one line above us for top-prediction.
255  static const int kDataBufferSize = kStride * (kBlockSize + 1);
256
257  virtual void SetUp() {
258    pred_fn_ = GetParam();
259    SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 1);
260  }
261
262  virtual void Predict(MB_PREDICTION_MODE mode) {
263    mbptr_->mode_info_context->mbmi.mode = mode;
264    ASM_REGISTER_STATE_CHECK(pred_fn_(mbptr_,
265                                      data_ptr_[0] - kStride,
266                                      data_ptr_[0] - 1, kStride,
267                                      data_ptr_[0], kStride));
268  }
269
270  IntraPredYFunc pred_fn_;
271  static uint8_t* data_array_;
272  static MACROBLOCKD * mb_;
273  static MODE_INFO *mi_;
274};
275
276MACROBLOCKD* IntraPredYTest::mb_ = NULL;
277MODE_INFO* IntraPredYTest::mi_ = NULL;
278uint8_t* IntraPredYTest::data_array_ = NULL;
279
280TEST_P(IntraPredYTest, IntraPredTests) {
281  RunTest();
282}
283
284INSTANTIATE_TEST_CASE_P(C, IntraPredYTest,
285                        ::testing::Values(
286                            vp8_build_intra_predictors_mby_s_c));
287#if HAVE_SSE2
288INSTANTIATE_TEST_CASE_P(SSE2, IntraPredYTest,
289                        ::testing::Values(
290                            vp8_build_intra_predictors_mby_s_sse2));
291#endif
292#if HAVE_SSSE3
293INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredYTest,
294                        ::testing::Values(
295                            vp8_build_intra_predictors_mby_s_ssse3));
296#endif
297#if HAVE_NEON
298INSTANTIATE_TEST_CASE_P(NEON, IntraPredYTest,
299                        ::testing::Values(
300                            vp8_build_intra_predictors_mby_s_neon));
301#endif
302
303typedef void (*IntraPredUvFunc)(MACROBLOCKD *x,
304                                uint8_t *uabove_row,
305                                uint8_t *vabove_row,
306                                uint8_t *uleft,
307                                uint8_t *vleft,
308                                int left_stride,
309                                uint8_t *upred_ptr,
310                                uint8_t *vpred_ptr,
311                                int pred_stride);
312
313class IntraPredUVTest
314    : public IntraPredBase,
315      public ::testing::TestWithParam<IntraPredUvFunc> {
316 public:
317  static void SetUpTestCase() {
318    mb_ = reinterpret_cast<MACROBLOCKD*>(
319        vpx_memalign(32, sizeof(MACROBLOCKD)));
320    mi_ = reinterpret_cast<MODE_INFO*>(
321        vpx_memalign(32, sizeof(MODE_INFO)));
322    data_array_ = reinterpret_cast<uint8_t*>(
323        vpx_memalign(kDataAlignment, kDataBufferSize));
324  }
325
326  static void TearDownTestCase() {
327    vpx_free(data_array_);
328    vpx_free(mi_);
329    vpx_free(mb_);
330    data_array_ = NULL;
331  }
332
333 protected:
334  static const int kBlockSize = 8;
335  static const int kDataAlignment = 8;
336  static const int kStride = kBlockSize * 3;
337  // We use 24 so that the data pointer of the first pixel in each row of
338  // each macroblock is 8-byte aligned, and this gives us access to the
339  // top-left and top-right corner pixels belonging to the top-left/right
340  // macroblocks.
341  // We use 9 lines so we have one line above us for top-prediction.
342  // [0] = U, [1] = V
343  static const int kDataBufferSize = 2 * kStride * (kBlockSize + 1);
344
345  virtual void SetUp() {
346    pred_fn_ = GetParam();
347    SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 2);
348  }
349
350  virtual void Predict(MB_PREDICTION_MODE mode) {
351    mbptr_->mode_info_context->mbmi.uv_mode = mode;
352    pred_fn_(mbptr_, data_ptr_[0] - kStride, data_ptr_[1] - kStride,
353             data_ptr_[0] - 1, data_ptr_[1] - 1, kStride,
354             data_ptr_[0], data_ptr_[1], kStride);
355  }
356
357  IntraPredUvFunc pred_fn_;
358  // We use 24 so that the data pointer of the first pixel in each row of
359  // each macroblock is 8-byte aligned, and this gives us access to the
360  // top-left and top-right corner pixels belonging to the top-left/right
361  // macroblocks.
362  // We use 9 lines so we have one line above us for top-prediction.
363  // [0] = U, [1] = V
364  static uint8_t* data_array_;
365  static MACROBLOCKD* mb_;
366  static MODE_INFO* mi_;
367};
368
369MACROBLOCKD* IntraPredUVTest::mb_ = NULL;
370MODE_INFO* IntraPredUVTest::mi_ = NULL;
371uint8_t* IntraPredUVTest::data_array_ = NULL;
372
373TEST_P(IntraPredUVTest, IntraPredTests) {
374  RunTest();
375}
376
377INSTANTIATE_TEST_CASE_P(C, IntraPredUVTest,
378                        ::testing::Values(
379                            vp8_build_intra_predictors_mbuv_s_c));
380#if HAVE_SSE2
381INSTANTIATE_TEST_CASE_P(SSE2, IntraPredUVTest,
382                        ::testing::Values(
383                            vp8_build_intra_predictors_mbuv_s_sse2));
384#endif
385#if HAVE_SSSE3
386INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredUVTest,
387                        ::testing::Values(
388                            vp8_build_intra_predictors_mbuv_s_ssse3));
389#endif
390#if HAVE_NEON
391INSTANTIATE_TEST_CASE_P(NEON, IntraPredUVTest,
392                        ::testing::Values(
393                            vp8_build_intra_predictors_mbuv_s_neon));
394#endif
395
396}  // namespace
397