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