/* * 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 #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 (*intra_pred_y_fn_t)(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 { public: static void SetUpTestCase() { mb_ = reinterpret_cast( vpx_memalign(32, sizeof(MACROBLOCKD))); mi_ = reinterpret_cast( vpx_memalign(32, sizeof(MODE_INFO))); data_array_ = reinterpret_cast( 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; REGISTER_STATE_CHECK(pred_fn_(mbptr_, data_ptr_[0] - kStride, data_ptr_[0] - 1, kStride, data_ptr_[0], kStride)); } intra_pred_y_fn_t 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 typedef void (*intra_pred_uv_fn_t)(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 { public: static void SetUpTestCase() { mb_ = reinterpret_cast( vpx_memalign(32, sizeof(MACROBLOCKD))); mi_ = reinterpret_cast( vpx_memalign(32, sizeof(MODE_INFO))); data_array_ = reinterpret_cast( 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); } intra_pred_uv_fn_t 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 } // namespace