1 /* 2 * Copyright (c) 2013 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 <stdlib.h> 12 #include <string.h> 13 #include <tuple> 14 15 #include "third_party/googletest/src/include/gtest/gtest.h" 16 17 #include "./vp8_rtcd.h" 18 #include "./vpx_config.h" 19 #include "test/acm_random.h" 20 #include "test/bench.h" 21 #include "test/clear_system_state.h" 22 #include "test/register_state_check.h" 23 #include "test/util.h" 24 #include "vpx/vpx_integer.h" 25 #include "vpx_mem/vpx_mem.h" 26 #include "vpx_ports/msvc.h" 27 28 namespace { 29 30 using libvpx_test::ACMRandom; 31 using std::make_tuple; 32 33 typedef void (*PredictFunc)(uint8_t *src_ptr, int src_pixels_per_line, 34 int xoffset, int yoffset, uint8_t *dst_ptr, 35 int dst_pitch); 36 37 typedef std::tuple<int, int, PredictFunc> PredictParam; 38 39 class PredictTestBase : public AbstractBench, 40 public ::testing::TestWithParam<PredictParam> { 41 public: 42 PredictTestBase() 43 : width_(GET_PARAM(0)), height_(GET_PARAM(1)), predict_(GET_PARAM(2)), 44 src_(NULL), padded_dst_(NULL), dst_(NULL), dst_c_(NULL) {} 45 46 virtual void SetUp() { 47 src_ = new uint8_t[kSrcSize]; 48 ASSERT_TRUE(src_ != NULL); 49 50 // padded_dst_ provides a buffer of kBorderSize around the destination 51 // memory to facilitate detecting out of bounds writes. 52 dst_stride_ = kBorderSize + width_ + kBorderSize; 53 padded_dst_size_ = dst_stride_ * (kBorderSize + height_ + kBorderSize); 54 padded_dst_ = 55 reinterpret_cast<uint8_t *>(vpx_memalign(16, padded_dst_size_)); 56 ASSERT_TRUE(padded_dst_ != NULL); 57 dst_ = padded_dst_ + (kBorderSize * dst_stride_) + kBorderSize; 58 59 dst_c_ = new uint8_t[16 * 16]; 60 ASSERT_TRUE(dst_c_ != NULL); 61 62 memset(src_, 0, kSrcSize); 63 memset(padded_dst_, 128, padded_dst_size_); 64 memset(dst_c_, 0, 16 * 16); 65 } 66 67 virtual void TearDown() { 68 delete[] src_; 69 src_ = NULL; 70 vpx_free(padded_dst_); 71 padded_dst_ = NULL; 72 dst_ = NULL; 73 delete[] dst_c_; 74 dst_c_ = NULL; 75 libvpx_test::ClearSystemState(); 76 } 77 78 protected: 79 // Make reference arrays big enough for 16x16 functions. Six-tap filters need 80 // 5 extra pixels outside of the macroblock. 81 static const int kSrcStride = 21; 82 static const int kSrcSize = kSrcStride * kSrcStride; 83 static const int kBorderSize = 16; 84 85 int width_; 86 int height_; 87 PredictFunc predict_; 88 uint8_t *src_; 89 uint8_t *padded_dst_; 90 uint8_t *dst_; 91 int padded_dst_size_; 92 uint8_t *dst_c_; 93 int dst_stride_; 94 95 bool CompareBuffers(const uint8_t *a, int a_stride, const uint8_t *b, 96 int b_stride) const { 97 for (int height = 0; height < height_; ++height) { 98 EXPECT_EQ(0, memcmp(a + height * a_stride, b + height * b_stride, 99 sizeof(*a) * width_)) 100 << "Row " << height << " does not match."; 101 } 102 103 return !HasFailure(); 104 } 105 106 // Given a block of memory 'a' with size 'a_size', determine if all regions 107 // excepting block 'b' described by 'b_stride', 'b_height', and 'b_width' 108 // match pixel value 'c'. 109 bool CheckBorder(const uint8_t *a, int a_size, const uint8_t *b, int b_width, 110 int b_height, int b_stride, uint8_t c) const { 111 const uint8_t *a_end = a + a_size; 112 const int b_size = (b_stride * b_height) + b_width; 113 const uint8_t *b_end = b + b_size; 114 const int left_border = (b_stride - b_width) / 2; 115 const int right_border = left_border + ((b_stride - b_width) % 2); 116 117 EXPECT_GE(b - left_border, a) << "'b' does not start within 'a'"; 118 EXPECT_LE(b_end + right_border, a_end) << "'b' does not end within 'a'"; 119 120 // Top border. 121 for (int pixel = 0; pixel < b - a - left_border; ++pixel) { 122 EXPECT_EQ(c, a[pixel]) << "Mismatch at " << pixel << " in top border."; 123 } 124 125 // Left border. 126 for (int height = 0; height < b_height; ++height) { 127 for (int width = left_border; width > 0; --width) { 128 EXPECT_EQ(c, b[height * b_stride - width]) 129 << "Mismatch at row " << height << " column " << left_border - width 130 << " in left border."; 131 } 132 } 133 134 // Right border. 135 for (int height = 0; height < b_height; ++height) { 136 for (int width = b_width; width < b_width + right_border; ++width) { 137 EXPECT_EQ(c, b[height * b_stride + width]) 138 << "Mismatch at row " << height << " column " << width - b_width 139 << " in right border."; 140 } 141 } 142 143 // Bottom border. 144 for (int pixel = static_cast<int>(b - a + b_size); pixel < a_size; 145 ++pixel) { 146 EXPECT_EQ(c, a[pixel]) << "Mismatch at " << pixel << " in bottom border."; 147 } 148 149 return !HasFailure(); 150 } 151 152 void TestWithRandomData(PredictFunc reference) { 153 ACMRandom rnd(ACMRandom::DeterministicSeed()); 154 155 // Run tests for almost all possible offsets. 156 for (int xoffset = 0; xoffset < 8; ++xoffset) { 157 for (int yoffset = 0; yoffset < 8; ++yoffset) { 158 if (xoffset == 0 && yoffset == 0) { 159 // This represents a copy which is not required to be handled by this 160 // module. 161 continue; 162 } 163 164 for (int i = 0; i < kSrcSize; ++i) { 165 src_[i] = rnd.Rand8(); 166 } 167 reference(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, 168 dst_c_, 16); 169 170 ASM_REGISTER_STATE_CHECK(predict_(&src_[kSrcStride * 2 + 2], kSrcStride, 171 xoffset, yoffset, dst_, dst_stride_)); 172 173 ASSERT_TRUE(CompareBuffers(dst_c_, 16, dst_, dst_stride_)); 174 ASSERT_TRUE(CheckBorder(padded_dst_, padded_dst_size_, dst_, width_, 175 height_, dst_stride_, 128)); 176 } 177 } 178 } 179 180 void TestWithUnalignedDst(PredictFunc reference) { 181 ACMRandom rnd(ACMRandom::DeterministicSeed()); 182 183 // Only the 4x4 need to be able to handle unaligned writes. 184 if (width_ == 4 && height_ == 4) { 185 for (int xoffset = 0; xoffset < 8; ++xoffset) { 186 for (int yoffset = 0; yoffset < 8; ++yoffset) { 187 if (xoffset == 0 && yoffset == 0) { 188 continue; 189 } 190 for (int i = 0; i < kSrcSize; ++i) { 191 src_[i] = rnd.Rand8(); 192 } 193 reference(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, 194 dst_c_, 16); 195 196 for (int i = 1; i < 4; ++i) { 197 memset(padded_dst_, 128, padded_dst_size_); 198 199 ASM_REGISTER_STATE_CHECK(predict_(&src_[kSrcStride * 2 + 2], 200 kSrcStride, xoffset, yoffset, 201 dst_ + i, dst_stride_ + i)); 202 203 ASSERT_TRUE(CompareBuffers(dst_c_, 16, dst_ + i, dst_stride_ + i)); 204 ASSERT_TRUE(CheckBorder(padded_dst_, padded_dst_size_, dst_ + i, 205 width_, height_, dst_stride_ + i, 128)); 206 } 207 } 208 } 209 } 210 } 211 212 void Run() { 213 for (int xoffset = 0; xoffset < 8; ++xoffset) { 214 for (int yoffset = 0; yoffset < 8; ++yoffset) { 215 if (xoffset == 0 && yoffset == 0) { 216 continue; 217 } 218 219 predict_(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, dst_, 220 dst_stride_); 221 } 222 } 223 } 224 }; // namespace 225 226 class SixtapPredictTest : public PredictTestBase {}; 227 228 TEST_P(SixtapPredictTest, TestWithRandomData) { 229 TestWithRandomData(vp8_sixtap_predict16x16_c); 230 } 231 TEST_P(SixtapPredictTest, TestWithUnalignedDst) { 232 TestWithUnalignedDst(vp8_sixtap_predict16x16_c); 233 } 234 235 TEST_P(SixtapPredictTest, TestWithPresetData) { 236 // Test input 237 static const uint8_t kTestData[kSrcSize] = { 238 184, 4, 191, 82, 92, 41, 0, 1, 226, 236, 172, 20, 182, 42, 226, 239 177, 79, 94, 77, 179, 203, 206, 198, 22, 192, 19, 75, 17, 192, 44, 240 233, 120, 48, 168, 203, 141, 210, 203, 143, 180, 184, 59, 201, 110, 102, 241 171, 32, 182, 10, 109, 105, 213, 60, 47, 236, 253, 67, 55, 14, 3, 242 99, 247, 124, 148, 159, 71, 34, 114, 19, 177, 38, 203, 237, 239, 58, 243 83, 155, 91, 10, 166, 201, 115, 124, 5, 163, 104, 2, 231, 160, 16, 244 234, 4, 8, 103, 153, 167, 174, 187, 26, 193, 109, 64, 141, 90, 48, 245 200, 174, 204, 36, 184, 114, 237, 43, 238, 242, 207, 86, 245, 182, 247, 246 6, 161, 251, 14, 8, 148, 182, 182, 79, 208, 120, 188, 17, 6, 23, 247 65, 206, 197, 13, 242, 126, 128, 224, 170, 110, 211, 121, 197, 200, 47, 248 188, 207, 208, 184, 221, 216, 76, 148, 143, 156, 100, 8, 89, 117, 14, 249 112, 183, 221, 54, 197, 208, 180, 69, 176, 94, 180, 131, 215, 121, 76, 250 7, 54, 28, 216, 238, 249, 176, 58, 142, 64, 215, 242, 72, 49, 104, 251 87, 161, 32, 52, 216, 230, 4, 141, 44, 181, 235, 224, 57, 195, 89, 252 134, 203, 144, 162, 163, 126, 156, 84, 185, 42, 148, 145, 29, 221, 194, 253 134, 52, 100, 166, 105, 60, 140, 110, 201, 184, 35, 181, 153, 93, 121, 254 243, 227, 68, 131, 134, 232, 2, 35, 60, 187, 77, 209, 76, 106, 174, 255 15, 241, 227, 115, 151, 77, 175, 36, 187, 121, 221, 223, 47, 118, 61, 256 168, 105, 32, 237, 236, 167, 213, 238, 202, 17, 170, 24, 226, 247, 131, 257 145, 6, 116, 117, 121, 11, 194, 41, 48, 126, 162, 13, 93, 209, 131, 258 154, 122, 237, 187, 103, 217, 99, 60, 200, 45, 78, 115, 69, 49, 106, 259 200, 194, 112, 60, 56, 234, 72, 251, 19, 120, 121, 182, 134, 215, 135, 260 10, 114, 2, 247, 46, 105, 209, 145, 165, 153, 191, 243, 12, 5, 36, 261 119, 206, 231, 231, 11, 32, 209, 83, 27, 229, 204, 149, 155, 83, 109, 262 35, 93, 223, 37, 84, 14, 142, 37, 160, 52, 191, 96, 40, 204, 101, 263 77, 67, 52, 53, 43, 63, 85, 253, 147, 113, 226, 96, 6, 125, 179, 264 115, 161, 17, 83, 198, 101, 98, 85, 139, 3, 137, 75, 99, 178, 23, 265 201, 255, 91, 253, 52, 134, 60, 138, 131, 208, 251, 101, 48, 2, 227, 266 228, 118, 132, 245, 202, 75, 91, 44, 160, 231, 47, 41, 50, 147, 220, 267 74, 92, 219, 165, 89, 16 268 }; 269 270 // Expected results for xoffset = 2 and yoffset = 2. 271 static const int kExpectedDstStride = 16; 272 static const uint8_t kExpectedDst[256] = { 273 117, 102, 74, 135, 42, 98, 175, 206, 70, 73, 222, 197, 50, 24, 39, 274 49, 38, 105, 90, 47, 169, 40, 171, 215, 200, 73, 109, 141, 53, 85, 275 177, 164, 79, 208, 124, 89, 212, 18, 81, 145, 151, 164, 217, 153, 91, 276 154, 102, 102, 159, 75, 164, 152, 136, 51, 213, 219, 186, 116, 193, 224, 277 186, 36, 231, 208, 84, 211, 155, 167, 35, 59, 42, 76, 216, 149, 73, 278 201, 78, 149, 184, 100, 96, 196, 189, 198, 188, 235, 195, 117, 129, 120, 279 129, 49, 25, 133, 113, 69, 221, 114, 70, 143, 99, 157, 108, 189, 140, 280 78, 6, 55, 65, 240, 255, 245, 184, 72, 90, 100, 116, 131, 39, 60, 281 234, 167, 33, 160, 88, 185, 200, 157, 159, 176, 127, 151, 138, 102, 168, 282 106, 170, 86, 82, 219, 189, 76, 33, 115, 197, 106, 96, 198, 136, 97, 283 141, 237, 151, 98, 137, 191, 185, 2, 57, 95, 142, 91, 255, 185, 97, 284 137, 76, 162, 94, 173, 131, 193, 161, 81, 106, 72, 135, 222, 234, 137, 285 66, 137, 106, 243, 210, 147, 95, 15, 137, 110, 85, 66, 16, 96, 167, 286 147, 150, 173, 203, 140, 118, 196, 84, 147, 160, 19, 95, 101, 123, 74, 287 132, 202, 82, 166, 12, 131, 166, 189, 170, 159, 85, 79, 66, 57, 152, 288 132, 203, 194, 0, 1, 56, 146, 180, 224, 156, 28, 83, 181, 79, 76, 289 80, 46, 160, 175, 59, 106, 43, 87, 75, 136, 85, 189, 46, 71, 200, 290 90 291 }; 292 293 ASM_REGISTER_STATE_CHECK( 294 predict_(const_cast<uint8_t *>(kTestData) + kSrcStride * 2 + 2, 295 kSrcStride, 2, 2, dst_, dst_stride_)); 296 297 ASSERT_TRUE( 298 CompareBuffers(kExpectedDst, kExpectedDstStride, dst_, dst_stride_)); 299 } 300 301 INSTANTIATE_TEST_CASE_P( 302 C, SixtapPredictTest, 303 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_c), 304 make_tuple(8, 8, &vp8_sixtap_predict8x8_c), 305 make_tuple(8, 4, &vp8_sixtap_predict8x4_c), 306 make_tuple(4, 4, &vp8_sixtap_predict4x4_c))); 307 #if HAVE_NEON 308 INSTANTIATE_TEST_CASE_P( 309 NEON, SixtapPredictTest, 310 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_neon), 311 make_tuple(8, 8, &vp8_sixtap_predict8x8_neon), 312 make_tuple(8, 4, &vp8_sixtap_predict8x4_neon), 313 make_tuple(4, 4, &vp8_sixtap_predict4x4_neon))); 314 #endif 315 #if HAVE_MMX 316 INSTANTIATE_TEST_CASE_P( 317 MMX, SixtapPredictTest, 318 ::testing::Values(make_tuple(4, 4, &vp8_sixtap_predict4x4_mmx))); 319 #endif 320 #if HAVE_SSE2 321 INSTANTIATE_TEST_CASE_P( 322 SSE2, SixtapPredictTest, 323 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_sse2), 324 make_tuple(8, 8, &vp8_sixtap_predict8x8_sse2), 325 make_tuple(8, 4, &vp8_sixtap_predict8x4_sse2))); 326 #endif 327 #if HAVE_SSSE3 328 INSTANTIATE_TEST_CASE_P( 329 SSSE3, SixtapPredictTest, 330 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_ssse3), 331 make_tuple(8, 8, &vp8_sixtap_predict8x8_ssse3), 332 make_tuple(8, 4, &vp8_sixtap_predict8x4_ssse3), 333 make_tuple(4, 4, &vp8_sixtap_predict4x4_ssse3))); 334 #endif 335 #if HAVE_MSA 336 INSTANTIATE_TEST_CASE_P( 337 MSA, SixtapPredictTest, 338 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_msa), 339 make_tuple(8, 8, &vp8_sixtap_predict8x8_msa), 340 make_tuple(8, 4, &vp8_sixtap_predict8x4_msa), 341 make_tuple(4, 4, &vp8_sixtap_predict4x4_msa))); 342 #endif 343 344 #if HAVE_MMI 345 INSTANTIATE_TEST_CASE_P( 346 MMI, SixtapPredictTest, 347 ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_mmi), 348 make_tuple(8, 8, &vp8_sixtap_predict8x8_mmi), 349 make_tuple(8, 4, &vp8_sixtap_predict8x4_mmi), 350 make_tuple(4, 4, &vp8_sixtap_predict4x4_mmi))); 351 #endif 352 353 class BilinearPredictTest : public PredictTestBase {}; 354 355 TEST_P(BilinearPredictTest, TestWithRandomData) { 356 TestWithRandomData(vp8_bilinear_predict16x16_c); 357 } 358 TEST_P(BilinearPredictTest, TestWithUnalignedDst) { 359 TestWithUnalignedDst(vp8_bilinear_predict16x16_c); 360 } 361 TEST_P(BilinearPredictTest, DISABLED_Speed) { 362 const int kCountSpeedTestBlock = 5000000 / (width_ * height_); 363 RunNTimes(kCountSpeedTestBlock); 364 365 char title[16]; 366 snprintf(title, sizeof(title), "%dx%d", width_, height_); 367 PrintMedian(title); 368 } 369 370 INSTANTIATE_TEST_CASE_P( 371 C, BilinearPredictTest, 372 ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_c), 373 make_tuple(8, 8, &vp8_bilinear_predict8x8_c), 374 make_tuple(8, 4, &vp8_bilinear_predict8x4_c), 375 make_tuple(4, 4, &vp8_bilinear_predict4x4_c))); 376 #if HAVE_NEON 377 INSTANTIATE_TEST_CASE_P( 378 NEON, BilinearPredictTest, 379 ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_neon), 380 make_tuple(8, 8, &vp8_bilinear_predict8x8_neon), 381 make_tuple(8, 4, &vp8_bilinear_predict8x4_neon), 382 make_tuple(4, 4, &vp8_bilinear_predict4x4_neon))); 383 #endif 384 #if HAVE_SSE2 385 INSTANTIATE_TEST_CASE_P( 386 SSE2, BilinearPredictTest, 387 ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_sse2), 388 make_tuple(8, 8, &vp8_bilinear_predict8x8_sse2), 389 make_tuple(8, 4, &vp8_bilinear_predict8x4_sse2), 390 make_tuple(4, 4, &vp8_bilinear_predict4x4_sse2))); 391 #endif 392 #if HAVE_SSSE3 393 INSTANTIATE_TEST_CASE_P( 394 SSSE3, BilinearPredictTest, 395 ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_ssse3), 396 make_tuple(8, 8, &vp8_bilinear_predict8x8_ssse3))); 397 #endif 398 #if HAVE_MSA 399 INSTANTIATE_TEST_CASE_P( 400 MSA, BilinearPredictTest, 401 ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_msa), 402 make_tuple(8, 8, &vp8_bilinear_predict8x8_msa), 403 make_tuple(8, 4, &vp8_bilinear_predict8x4_msa), 404 make_tuple(4, 4, &vp8_bilinear_predict4x4_msa))); 405 #endif 406 } // namespace 407