| /* |
| * Copyright (c) 2013 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 <stdlib.h> |
| #include <string.h> |
| #include <tuple> |
| |
| #include "third_party/googletest/src/include/gtest/gtest.h" |
| |
| #include "./vp8_rtcd.h" |
| #include "./vpx_config.h" |
| #include "test/acm_random.h" |
| #include "test/bench.h" |
| #include "test/clear_system_state.h" |
| #include "test/register_state_check.h" |
| #include "test/util.h" |
| #include "vpx/vpx_integer.h" |
| #include "vpx_mem/vpx_mem.h" |
| #include "vpx_ports/msvc.h" |
| |
| namespace { |
| |
| using libvpx_test::ACMRandom; |
| using std::make_tuple; |
| |
| typedef void (*PredictFunc)(uint8_t *src_ptr, int src_pixels_per_line, |
| int xoffset, int yoffset, uint8_t *dst_ptr, |
| int dst_pitch); |
| |
| typedef std::tuple<int, int, PredictFunc> PredictParam; |
| |
| class PredictTestBase : public AbstractBench, |
| public ::testing::TestWithParam<PredictParam> { |
| public: |
| PredictTestBase() |
| : width_(GET_PARAM(0)), height_(GET_PARAM(1)), predict_(GET_PARAM(2)), |
| src_(nullptr), padded_dst_(nullptr), dst_(nullptr), dst_c_(nullptr) {} |
| |
| virtual void SetUp() { |
| src_ = new uint8_t[kSrcSize]; |
| ASSERT_NE(src_, nullptr); |
| |
| // padded_dst_ provides a buffer of kBorderSize around the destination |
| // memory to facilitate detecting out of bounds writes. |
| dst_stride_ = kBorderSize + width_ + kBorderSize; |
| padded_dst_size_ = dst_stride_ * (kBorderSize + height_ + kBorderSize); |
| padded_dst_ = |
| reinterpret_cast<uint8_t *>(vpx_memalign(16, padded_dst_size_)); |
| ASSERT_NE(padded_dst_, nullptr); |
| dst_ = padded_dst_ + (kBorderSize * dst_stride_) + kBorderSize; |
| |
| dst_c_ = new uint8_t[16 * 16]; |
| ASSERT_NE(dst_c_, nullptr); |
| |
| memset(src_, 0, kSrcSize); |
| memset(padded_dst_, 128, padded_dst_size_); |
| memset(dst_c_, 0, 16 * 16); |
| } |
| |
| virtual void TearDown() { |
| delete[] src_; |
| src_ = nullptr; |
| vpx_free(padded_dst_); |
| padded_dst_ = nullptr; |
| dst_ = nullptr; |
| delete[] dst_c_; |
| dst_c_ = nullptr; |
| libvpx_test::ClearSystemState(); |
| } |
| |
| protected: |
| // Make reference arrays big enough for 16x16 functions. Six-tap filters need |
| // 5 extra pixels outside of the macroblock. |
| static const int kSrcStride = 21; |
| static const int kSrcSize = kSrcStride * kSrcStride; |
| static const int kBorderSize = 16; |
| |
| int width_; |
| int height_; |
| PredictFunc predict_; |
| uint8_t *src_; |
| uint8_t *padded_dst_; |
| uint8_t *dst_; |
| int padded_dst_size_; |
| uint8_t *dst_c_; |
| int dst_stride_; |
| |
| bool CompareBuffers(const uint8_t *a, int a_stride, const uint8_t *b, |
| int b_stride) const { |
| for (int height = 0; height < height_; ++height) { |
| EXPECT_EQ(0, memcmp(a + height * a_stride, b + height * b_stride, |
| sizeof(*a) * width_)) |
| << "Row " << height << " does not match."; |
| } |
| |
| return !HasFailure(); |
| } |
| |
| // Given a block of memory 'a' with size 'a_size', determine if all regions |
| // excepting block 'b' described by 'b_stride', 'b_height', and 'b_width' |
| // match pixel value 'c'. |
| bool CheckBorder(const uint8_t *a, int a_size, const uint8_t *b, int b_width, |
| int b_height, int b_stride, uint8_t c) const { |
| const uint8_t *a_end = a + a_size; |
| const int b_size = (b_stride * b_height) + b_width; |
| const uint8_t *b_end = b + b_size; |
| const int left_border = (b_stride - b_width) / 2; |
| const int right_border = left_border + ((b_stride - b_width) % 2); |
| |
| EXPECT_GE(b - left_border, a) << "'b' does not start within 'a'"; |
| EXPECT_LE(b_end + right_border, a_end) << "'b' does not end within 'a'"; |
| |
| // Top border. |
| for (int pixel = 0; pixel < b - a - left_border; ++pixel) { |
| EXPECT_EQ(c, a[pixel]) << "Mismatch at " << pixel << " in top border."; |
| } |
| |
| // Left border. |
| for (int height = 0; height < b_height; ++height) { |
| for (int width = left_border; width > 0; --width) { |
| EXPECT_EQ(c, b[height * b_stride - width]) |
| << "Mismatch at row " << height << " column " << left_border - width |
| << " in left border."; |
| } |
| } |
| |
| // Right border. |
| for (int height = 0; height < b_height; ++height) { |
| for (int width = b_width; width < b_width + right_border; ++width) { |
| EXPECT_EQ(c, b[height * b_stride + width]) |
| << "Mismatch at row " << height << " column " << width - b_width |
| << " in right border."; |
| } |
| } |
| |
| // Bottom border. |
| for (int pixel = static_cast<int>(b - a + b_size); pixel < a_size; |
| ++pixel) { |
| EXPECT_EQ(c, a[pixel]) << "Mismatch at " << pixel << " in bottom border."; |
| } |
| |
| return !HasFailure(); |
| } |
| |
| void TestWithRandomData(PredictFunc reference) { |
| ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| |
| // Run tests for almost all possible offsets. |
| for (int xoffset = 0; xoffset < 8; ++xoffset) { |
| for (int yoffset = 0; yoffset < 8; ++yoffset) { |
| if (xoffset == 0 && yoffset == 0) { |
| // This represents a copy which is not required to be handled by this |
| // module. |
| continue; |
| } |
| |
| for (int i = 0; i < kSrcSize; ++i) { |
| src_[i] = rnd.Rand8(); |
| } |
| reference(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, |
| dst_c_, 16); |
| |
| ASM_REGISTER_STATE_CHECK(predict_(&src_[kSrcStride * 2 + 2], kSrcStride, |
| xoffset, yoffset, dst_, dst_stride_)); |
| |
| ASSERT_TRUE(CompareBuffers(dst_c_, 16, dst_, dst_stride_)); |
| ASSERT_TRUE(CheckBorder(padded_dst_, padded_dst_size_, dst_, width_, |
| height_, dst_stride_, 128)); |
| } |
| } |
| } |
| |
| void TestWithUnalignedDst(PredictFunc reference) { |
| ACMRandom rnd(ACMRandom::DeterministicSeed()); |
| |
| // Only the 4x4 need to be able to handle unaligned writes. |
| if (width_ == 4 && height_ == 4) { |
| for (int xoffset = 0; xoffset < 8; ++xoffset) { |
| for (int yoffset = 0; yoffset < 8; ++yoffset) { |
| if (xoffset == 0 && yoffset == 0) { |
| continue; |
| } |
| for (int i = 0; i < kSrcSize; ++i) { |
| src_[i] = rnd.Rand8(); |
| } |
| reference(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, |
| dst_c_, 16); |
| |
| for (int i = 1; i < 4; ++i) { |
| memset(padded_dst_, 128, padded_dst_size_); |
| |
| ASM_REGISTER_STATE_CHECK(predict_(&src_[kSrcStride * 2 + 2], |
| kSrcStride, xoffset, yoffset, |
| dst_ + i, dst_stride_ + i)); |
| |
| ASSERT_TRUE(CompareBuffers(dst_c_, 16, dst_ + i, dst_stride_ + i)); |
| ASSERT_TRUE(CheckBorder(padded_dst_, padded_dst_size_, dst_ + i, |
| width_, height_, dst_stride_ + i, 128)); |
| } |
| } |
| } |
| } |
| } |
| |
| void Run() { |
| for (int xoffset = 0; xoffset < 8; ++xoffset) { |
| for (int yoffset = 0; yoffset < 8; ++yoffset) { |
| if (xoffset == 0 && yoffset == 0) { |
| continue; |
| } |
| |
| predict_(&src_[kSrcStride * 2 + 2], kSrcStride, xoffset, yoffset, dst_, |
| dst_stride_); |
| } |
| } |
| } |
| }; // namespace |
| |
| class SixtapPredictTest : public PredictTestBase {}; |
| |
| TEST_P(SixtapPredictTest, TestWithRandomData) { |
| TestWithRandomData(vp8_sixtap_predict16x16_c); |
| } |
| TEST_P(SixtapPredictTest, TestWithUnalignedDst) { |
| TestWithUnalignedDst(vp8_sixtap_predict16x16_c); |
| } |
| |
| TEST_P(SixtapPredictTest, TestWithPresetData) { |
| // Test input |
| static const uint8_t kTestData[kSrcSize] = { |
| 184, 4, 191, 82, 92, 41, 0, 1, 226, 236, 172, 20, 182, 42, 226, |
| 177, 79, 94, 77, 179, 203, 206, 198, 22, 192, 19, 75, 17, 192, 44, |
| 233, 120, 48, 168, 203, 141, 210, 203, 143, 180, 184, 59, 201, 110, 102, |
| 171, 32, 182, 10, 109, 105, 213, 60, 47, 236, 253, 67, 55, 14, 3, |
| 99, 247, 124, 148, 159, 71, 34, 114, 19, 177, 38, 203, 237, 239, 58, |
| 83, 155, 91, 10, 166, 201, 115, 124, 5, 163, 104, 2, 231, 160, 16, |
| 234, 4, 8, 103, 153, 167, 174, 187, 26, 193, 109, 64, 141, 90, 48, |
| 200, 174, 204, 36, 184, 114, 237, 43, 238, 242, 207, 86, 245, 182, 247, |
| 6, 161, 251, 14, 8, 148, 182, 182, 79, 208, 120, 188, 17, 6, 23, |
| 65, 206, 197, 13, 242, 126, 128, 224, 170, 110, 211, 121, 197, 200, 47, |
| 188, 207, 208, 184, 221, 216, 76, 148, 143, 156, 100, 8, 89, 117, 14, |
| 112, 183, 221, 54, 197, 208, 180, 69, 176, 94, 180, 131, 215, 121, 76, |
| 7, 54, 28, 216, 238, 249, 176, 58, 142, 64, 215, 242, 72, 49, 104, |
| 87, 161, 32, 52, 216, 230, 4, 141, 44, 181, 235, 224, 57, 195, 89, |
| 134, 203, 144, 162, 163, 126, 156, 84, 185, 42, 148, 145, 29, 221, 194, |
| 134, 52, 100, 166, 105, 60, 140, 110, 201, 184, 35, 181, 153, 93, 121, |
| 243, 227, 68, 131, 134, 232, 2, 35, 60, 187, 77, 209, 76, 106, 174, |
| 15, 241, 227, 115, 151, 77, 175, 36, 187, 121, 221, 223, 47, 118, 61, |
| 168, 105, 32, 237, 236, 167, 213, 238, 202, 17, 170, 24, 226, 247, 131, |
| 145, 6, 116, 117, 121, 11, 194, 41, 48, 126, 162, 13, 93, 209, 131, |
| 154, 122, 237, 187, 103, 217, 99, 60, 200, 45, 78, 115, 69, 49, 106, |
| 200, 194, 112, 60, 56, 234, 72, 251, 19, 120, 121, 182, 134, 215, 135, |
| 10, 114, 2, 247, 46, 105, 209, 145, 165, 153, 191, 243, 12, 5, 36, |
| 119, 206, 231, 231, 11, 32, 209, 83, 27, 229, 204, 149, 155, 83, 109, |
| 35, 93, 223, 37, 84, 14, 142, 37, 160, 52, 191, 96, 40, 204, 101, |
| 77, 67, 52, 53, 43, 63, 85, 253, 147, 113, 226, 96, 6, 125, 179, |
| 115, 161, 17, 83, 198, 101, 98, 85, 139, 3, 137, 75, 99, 178, 23, |
| 201, 255, 91, 253, 52, 134, 60, 138, 131, 208, 251, 101, 48, 2, 227, |
| 228, 118, 132, 245, 202, 75, 91, 44, 160, 231, 47, 41, 50, 147, 220, |
| 74, 92, 219, 165, 89, 16 |
| }; |
| |
| // Expected results for xoffset = 2 and yoffset = 2. |
| static const int kExpectedDstStride = 16; |
| static const uint8_t kExpectedDst[256] = { |
| 117, 102, 74, 135, 42, 98, 175, 206, 70, 73, 222, 197, 50, 24, 39, |
| 49, 38, 105, 90, 47, 169, 40, 171, 215, 200, 73, 109, 141, 53, 85, |
| 177, 164, 79, 208, 124, 89, 212, 18, 81, 145, 151, 164, 217, 153, 91, |
| 154, 102, 102, 159, 75, 164, 152, 136, 51, 213, 219, 186, 116, 193, 224, |
| 186, 36, 231, 208, 84, 211, 155, 167, 35, 59, 42, 76, 216, 149, 73, |
| 201, 78, 149, 184, 100, 96, 196, 189, 198, 188, 235, 195, 117, 129, 120, |
| 129, 49, 25, 133, 113, 69, 221, 114, 70, 143, 99, 157, 108, 189, 140, |
| 78, 6, 55, 65, 240, 255, 245, 184, 72, 90, 100, 116, 131, 39, 60, |
| 234, 167, 33, 160, 88, 185, 200, 157, 159, 176, 127, 151, 138, 102, 168, |
| 106, 170, 86, 82, 219, 189, 76, 33, 115, 197, 106, 96, 198, 136, 97, |
| 141, 237, 151, 98, 137, 191, 185, 2, 57, 95, 142, 91, 255, 185, 97, |
| 137, 76, 162, 94, 173, 131, 193, 161, 81, 106, 72, 135, 222, 234, 137, |
| 66, 137, 106, 243, 210, 147, 95, 15, 137, 110, 85, 66, 16, 96, 167, |
| 147, 150, 173, 203, 140, 118, 196, 84, 147, 160, 19, 95, 101, 123, 74, |
| 132, 202, 82, 166, 12, 131, 166, 189, 170, 159, 85, 79, 66, 57, 152, |
| 132, 203, 194, 0, 1, 56, 146, 180, 224, 156, 28, 83, 181, 79, 76, |
| 80, 46, 160, 175, 59, 106, 43, 87, 75, 136, 85, 189, 46, 71, 200, |
| 90 |
| }; |
| |
| ASM_REGISTER_STATE_CHECK( |
| predict_(const_cast<uint8_t *>(kTestData) + kSrcStride * 2 + 2, |
| kSrcStride, 2, 2, dst_, dst_stride_)); |
| |
| ASSERT_TRUE( |
| CompareBuffers(kExpectedDst, kExpectedDstStride, dst_, dst_stride_)); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| C, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_c), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_c), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_c), |
| make_tuple(4, 4, &vp8_sixtap_predict4x4_c))); |
| #if HAVE_NEON |
| INSTANTIATE_TEST_SUITE_P( |
| NEON, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_neon), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_neon), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_neon), |
| make_tuple(4, 4, &vp8_sixtap_predict4x4_neon))); |
| #endif |
| #if HAVE_MMX |
| INSTANTIATE_TEST_SUITE_P( |
| MMX, SixtapPredictTest, |
| ::testing::Values(make_tuple(4, 4, &vp8_sixtap_predict4x4_mmx))); |
| #endif |
| #if HAVE_SSE2 |
| INSTANTIATE_TEST_SUITE_P( |
| SSE2, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_sse2), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_sse2), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_sse2))); |
| #endif |
| #if HAVE_SSSE3 |
| INSTANTIATE_TEST_SUITE_P( |
| SSSE3, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_ssse3), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_ssse3), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_ssse3), |
| make_tuple(4, 4, &vp8_sixtap_predict4x4_ssse3))); |
| #endif |
| #if HAVE_MSA |
| INSTANTIATE_TEST_SUITE_P( |
| MSA, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_msa), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_msa), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_msa), |
| make_tuple(4, 4, &vp8_sixtap_predict4x4_msa))); |
| #endif |
| |
| #if HAVE_MMI |
| INSTANTIATE_TEST_SUITE_P( |
| MMI, SixtapPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_sixtap_predict16x16_mmi), |
| make_tuple(8, 8, &vp8_sixtap_predict8x8_mmi), |
| make_tuple(8, 4, &vp8_sixtap_predict8x4_mmi), |
| make_tuple(4, 4, &vp8_sixtap_predict4x4_mmi))); |
| #endif |
| |
| class BilinearPredictTest : public PredictTestBase {}; |
| |
| TEST_P(BilinearPredictTest, TestWithRandomData) { |
| TestWithRandomData(vp8_bilinear_predict16x16_c); |
| } |
| TEST_P(BilinearPredictTest, TestWithUnalignedDst) { |
| TestWithUnalignedDst(vp8_bilinear_predict16x16_c); |
| } |
| TEST_P(BilinearPredictTest, DISABLED_Speed) { |
| const int kCountSpeedTestBlock = 5000000 / (width_ * height_); |
| RunNTimes(kCountSpeedTestBlock); |
| |
| char title[16]; |
| snprintf(title, sizeof(title), "%dx%d", width_, height_); |
| PrintMedian(title); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P( |
| C, BilinearPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_c), |
| make_tuple(8, 8, &vp8_bilinear_predict8x8_c), |
| make_tuple(8, 4, &vp8_bilinear_predict8x4_c), |
| make_tuple(4, 4, &vp8_bilinear_predict4x4_c))); |
| #if HAVE_NEON |
| INSTANTIATE_TEST_SUITE_P( |
| NEON, BilinearPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_neon), |
| make_tuple(8, 8, &vp8_bilinear_predict8x8_neon), |
| make_tuple(8, 4, &vp8_bilinear_predict8x4_neon), |
| make_tuple(4, 4, &vp8_bilinear_predict4x4_neon))); |
| #endif |
| #if HAVE_SSE2 |
| INSTANTIATE_TEST_SUITE_P( |
| SSE2, BilinearPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_sse2), |
| make_tuple(8, 8, &vp8_bilinear_predict8x8_sse2), |
| make_tuple(8, 4, &vp8_bilinear_predict8x4_sse2), |
| make_tuple(4, 4, &vp8_bilinear_predict4x4_sse2))); |
| #endif |
| #if HAVE_SSSE3 |
| INSTANTIATE_TEST_SUITE_P( |
| SSSE3, BilinearPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_ssse3), |
| make_tuple(8, 8, &vp8_bilinear_predict8x8_ssse3))); |
| #endif |
| #if HAVE_MSA |
| INSTANTIATE_TEST_SUITE_P( |
| MSA, BilinearPredictTest, |
| ::testing::Values(make_tuple(16, 16, &vp8_bilinear_predict16x16_msa), |
| make_tuple(8, 8, &vp8_bilinear_predict8x8_msa), |
| make_tuple(8, 4, &vp8_bilinear_predict8x4_msa), |
| make_tuple(4, 4, &vp8_bilinear_predict4x4_msa))); |
| #endif |
| } // namespace |