| /* |
| * 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 <string.h> |
| #include "test/acm_random.h" |
| #include "test/clear_system_state.h" |
| #include "test/register_state_check.h" |
| #include "third_party/googletest/src/include/gtest/gtest.h" |
| |
| #include "./vpx_config.h" |
| #include "./vp8_rtcd.h" |
| #include "vp8/common/blockd.h" |
| #include "vpx_mem/vpx_mem.h" |
| |
| namespace { |
| |
| using libvpx_test::ACMRandom; |
| |
| class IntraPredBase { |
| public: |
| virtual ~IntraPredBase() { libvpx_test::ClearSystemState(); } |
| |
| protected: |
| void SetupMacroblock(MACROBLOCKD *mbptr, |
| MODE_INFO *miptr, |
| uint8_t *data, |
| int block_size, |
| int stride, |
| int num_planes) { |
| mbptr_ = mbptr; |
| miptr_ = miptr; |
| mbptr_->up_available = 1; |
| mbptr_->left_available = 1; |
| mbptr_->mode_info_context = miptr_; |
| stride_ = stride; |
| block_size_ = block_size; |
| num_planes_ = num_planes; |
| for (int p = 0; p < num_planes; p++) |
| data_ptr_[p] = data + stride * (block_size + 1) * p + |
| stride + block_size; |
| } |
| |
| void FillRandom() { |
| // Fill edges with random data |
| ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| for (int p = 0; p < num_planes_; p++) { |
| for (int x = -1 ; x <= block_size_; x++) |
| data_ptr_[p][x - stride_] = rnd.Rand8(); |
| for (int y = 0; y < block_size_; y++) |
| data_ptr_[p][y * stride_ - 1] = rnd.Rand8(); |
| } |
| } |
| |
| virtual void Predict(MB_PREDICTION_MODE mode) = 0; |
| |
| void SetLeftUnavailable() { |
| mbptr_->left_available = 0; |
| for (int p = 0; p < num_planes_; p++) |
| for (int i = -1; i < block_size_; ++i) |
| data_ptr_[p][stride_ * i - 1] = 129; |
| } |
| |
| void SetTopUnavailable() { |
| mbptr_->up_available = 0; |
| for (int p = 0; p < num_planes_; p++) |
| memset(&data_ptr_[p][-1 - stride_], 127, block_size_ + 2); |
| } |
| |
| void SetTopLeftUnavailable() { |
| SetLeftUnavailable(); |
| SetTopUnavailable(); |
| } |
| |
| int BlockSizeLog2Min1() const { |
| switch (block_size_) { |
| case 16: |
| return 3; |
| case 8: |
| return 2; |
| default: |
| return 0; |
| } |
| } |
| |
| // check DC prediction output against a reference |
| void CheckDCPrediction() const { |
| for (int p = 0; p < num_planes_; p++) { |
| // calculate expected DC |
| int expected; |
| if (mbptr_->up_available || mbptr_->left_available) { |
| int sum = 0, shift = BlockSizeLog2Min1() + mbptr_->up_available + |
| mbptr_->left_available; |
| if (mbptr_->up_available) |
| for (int x = 0; x < block_size_; x++) |
| sum += data_ptr_[p][x - stride_]; |
| if (mbptr_->left_available) |
| for (int y = 0; y < block_size_; y++) |
| sum += data_ptr_[p][y * stride_ - 1]; |
| expected = (sum + (1 << (shift - 1))) >> shift; |
| } else { |
| expected = 0x80; |
| } |
| // check that all subsequent lines are equal to the first |
| for (int y = 1; y < block_size_; ++y) |
| ASSERT_EQ(0, memcmp(data_ptr_[p], &data_ptr_[p][y * stride_], |
| block_size_)); |
| // within the first line, ensure that each pixel has the same value |
| for (int x = 1; x < block_size_; ++x) |
| ASSERT_EQ(data_ptr_[p][0], data_ptr_[p][x]); |
| // now ensure that that pixel has the expected (DC) value |
| ASSERT_EQ(expected, data_ptr_[p][0]); |
| } |
| } |
| |
| // check V prediction output against a reference |
| void CheckVPrediction() const { |
| // check that all lines equal the top border |
| for (int p = 0; p < num_planes_; p++) |
| for (int y = 0; y < block_size_; y++) |
| ASSERT_EQ(0, memcmp(&data_ptr_[p][-stride_], |
| &data_ptr_[p][y * stride_], block_size_)); |
| } |
| |
| // check H prediction output against a reference |
| void CheckHPrediction() const { |
| // for each line, ensure that each pixel is equal to the left border |
| for (int p = 0; p < num_planes_; p++) |
| for (int y = 0; y < block_size_; y++) |
| for (int x = 0; x < block_size_; x++) |
| ASSERT_EQ(data_ptr_[p][-1 + y * stride_], |
| data_ptr_[p][x + y * stride_]); |
| } |
| |
| static int ClipByte(int value) { |
| if (value > 255) |
| return 255; |
| else if (value < 0) |
| return 0; |
| return value; |
| } |
| |
| // check TM prediction output against a reference |
| void CheckTMPrediction() const { |
| for (int p = 0; p < num_planes_; p++) |
| for (int y = 0; y < block_size_; y++) |
| for (int x = 0; x < block_size_; x++) { |
| const int expected = ClipByte(data_ptr_[p][x - stride_] |
| + data_ptr_[p][stride_ * y - 1] |
| - data_ptr_[p][-1 - stride_]); |
| ASSERT_EQ(expected, data_ptr_[p][y * stride_ + x]); |
| } |
| } |
| |
| // Actual test |
| void RunTest() { |
| { |
| SCOPED_TRACE("DC_PRED"); |
| FillRandom(); |
| Predict(DC_PRED); |
| CheckDCPrediction(); |
| } |
| { |
| SCOPED_TRACE("DC_PRED LEFT"); |
| FillRandom(); |
| SetLeftUnavailable(); |
| Predict(DC_PRED); |
| CheckDCPrediction(); |
| } |
| { |
| SCOPED_TRACE("DC_PRED TOP"); |
| FillRandom(); |
| SetTopUnavailable(); |
| Predict(DC_PRED); |
| CheckDCPrediction(); |
| } |
| { |
| SCOPED_TRACE("DC_PRED TOP_LEFT"); |
| FillRandom(); |
| SetTopLeftUnavailable(); |
| Predict(DC_PRED); |
| CheckDCPrediction(); |
| } |
| { |
| SCOPED_TRACE("H_PRED"); |
| FillRandom(); |
| Predict(H_PRED); |
| CheckHPrediction(); |
| } |
| { |
| SCOPED_TRACE("V_PRED"); |
| FillRandom(); |
| Predict(V_PRED); |
| CheckVPrediction(); |
| } |
| { |
| SCOPED_TRACE("TM_PRED"); |
| FillRandom(); |
| Predict(TM_PRED); |
| CheckTMPrediction(); |
| } |
| } |
| |
| MACROBLOCKD *mbptr_; |
| MODE_INFO *miptr_; |
| uint8_t *data_ptr_[2]; // in the case of Y, only [0] is used |
| int stride_; |
| int block_size_; |
| int num_planes_; |
| }; |
| |
| typedef void (*IntraPredYFunc)(MACROBLOCKD *x, |
| uint8_t *yabove_row, |
| uint8_t *yleft, |
| int left_stride, |
| uint8_t *ypred_ptr, |
| int y_stride); |
| |
| class IntraPredYTest |
| : public IntraPredBase, |
| public ::testing::TestWithParam<IntraPredYFunc> { |
| public: |
| static void SetUpTestCase() { |
| mb_ = reinterpret_cast<MACROBLOCKD*>( |
| vpx_memalign(32, sizeof(MACROBLOCKD))); |
| mi_ = reinterpret_cast<MODE_INFO*>( |
| vpx_memalign(32, sizeof(MODE_INFO))); |
| data_array_ = reinterpret_cast<uint8_t*>( |
| vpx_memalign(kDataAlignment, kDataBufferSize)); |
| } |
| |
| static void TearDownTestCase() { |
| vpx_free(data_array_); |
| vpx_free(mi_); |
| vpx_free(mb_); |
| data_array_ = NULL; |
| } |
| |
| protected: |
| static const int kBlockSize = 16; |
| static const int kDataAlignment = 16; |
| static const int kStride = kBlockSize * 3; |
| // We use 48 so that the data pointer of the first pixel in each row of |
| // each macroblock is 16-byte aligned, and this gives us access to the |
| // top-left and top-right corner pixels belonging to the top-left/right |
| // macroblocks. |
| // We use 17 lines so we have one line above us for top-prediction. |
| static const int kDataBufferSize = kStride * (kBlockSize + 1); |
| |
| virtual void SetUp() { |
| pred_fn_ = GetParam(); |
| SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 1); |
| } |
| |
| virtual void Predict(MB_PREDICTION_MODE mode) { |
| mbptr_->mode_info_context->mbmi.mode = mode; |
| ASM_REGISTER_STATE_CHECK(pred_fn_(mbptr_, |
| data_ptr_[0] - kStride, |
| data_ptr_[0] - 1, kStride, |
| data_ptr_[0], kStride)); |
| } |
| |
| IntraPredYFunc pred_fn_; |
| static uint8_t* data_array_; |
| static MACROBLOCKD * mb_; |
| static MODE_INFO *mi_; |
| }; |
| |
| MACROBLOCKD* IntraPredYTest::mb_ = NULL; |
| MODE_INFO* IntraPredYTest::mi_ = NULL; |
| uint8_t* IntraPredYTest::data_array_ = NULL; |
| |
| TEST_P(IntraPredYTest, IntraPredTests) { |
| RunTest(); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(C, IntraPredYTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mby_s_c)); |
| #if HAVE_SSE2 |
| INSTANTIATE_TEST_CASE_P(SSE2, IntraPredYTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mby_s_sse2)); |
| #endif |
| #if HAVE_SSSE3 |
| INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredYTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mby_s_ssse3)); |
| #endif |
| #if HAVE_NEON |
| INSTANTIATE_TEST_CASE_P(NEON, IntraPredYTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mby_s_neon)); |
| #endif |
| |
| typedef void (*IntraPredUvFunc)(MACROBLOCKD *x, |
| uint8_t *uabove_row, |
| uint8_t *vabove_row, |
| uint8_t *uleft, |
| uint8_t *vleft, |
| int left_stride, |
| uint8_t *upred_ptr, |
| uint8_t *vpred_ptr, |
| int pred_stride); |
| |
| class IntraPredUVTest |
| : public IntraPredBase, |
| public ::testing::TestWithParam<IntraPredUvFunc> { |
| public: |
| static void SetUpTestCase() { |
| mb_ = reinterpret_cast<MACROBLOCKD*>( |
| vpx_memalign(32, sizeof(MACROBLOCKD))); |
| mi_ = reinterpret_cast<MODE_INFO*>( |
| vpx_memalign(32, sizeof(MODE_INFO))); |
| data_array_ = reinterpret_cast<uint8_t*>( |
| vpx_memalign(kDataAlignment, kDataBufferSize)); |
| } |
| |
| static void TearDownTestCase() { |
| vpx_free(data_array_); |
| vpx_free(mi_); |
| vpx_free(mb_); |
| data_array_ = NULL; |
| } |
| |
| protected: |
| static const int kBlockSize = 8; |
| static const int kDataAlignment = 8; |
| static const int kStride = kBlockSize * 3; |
| // We use 24 so that the data pointer of the first pixel in each row of |
| // each macroblock is 8-byte aligned, and this gives us access to the |
| // top-left and top-right corner pixels belonging to the top-left/right |
| // macroblocks. |
| // We use 9 lines so we have one line above us for top-prediction. |
| // [0] = U, [1] = V |
| static const int kDataBufferSize = 2 * kStride * (kBlockSize + 1); |
| |
| virtual void SetUp() { |
| pred_fn_ = GetParam(); |
| SetupMacroblock(mb_, mi_, data_array_, kBlockSize, kStride, 2); |
| } |
| |
| virtual void Predict(MB_PREDICTION_MODE mode) { |
| mbptr_->mode_info_context->mbmi.uv_mode = mode; |
| pred_fn_(mbptr_, data_ptr_[0] - kStride, data_ptr_[1] - kStride, |
| data_ptr_[0] - 1, data_ptr_[1] - 1, kStride, |
| data_ptr_[0], data_ptr_[1], kStride); |
| } |
| |
| IntraPredUvFunc pred_fn_; |
| // We use 24 so that the data pointer of the first pixel in each row of |
| // each macroblock is 8-byte aligned, and this gives us access to the |
| // top-left and top-right corner pixels belonging to the top-left/right |
| // macroblocks. |
| // We use 9 lines so we have one line above us for top-prediction. |
| // [0] = U, [1] = V |
| static uint8_t* data_array_; |
| static MACROBLOCKD* mb_; |
| static MODE_INFO* mi_; |
| }; |
| |
| MACROBLOCKD* IntraPredUVTest::mb_ = NULL; |
| MODE_INFO* IntraPredUVTest::mi_ = NULL; |
| uint8_t* IntraPredUVTest::data_array_ = NULL; |
| |
| TEST_P(IntraPredUVTest, IntraPredTests) { |
| RunTest(); |
| } |
| |
| INSTANTIATE_TEST_CASE_P(C, IntraPredUVTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mbuv_s_c)); |
| #if HAVE_SSE2 |
| INSTANTIATE_TEST_CASE_P(SSE2, IntraPredUVTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mbuv_s_sse2)); |
| #endif |
| #if HAVE_SSSE3 |
| INSTANTIATE_TEST_CASE_P(SSSE3, IntraPredUVTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mbuv_s_ssse3)); |
| #endif |
| #if HAVE_NEON |
| INSTANTIATE_TEST_CASE_P(NEON, IntraPredUVTest, |
| ::testing::Values( |
| vp8_build_intra_predictors_mbuv_s_neon)); |
| #endif |
| |
| } // namespace |