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:
PredictTestBase()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
SetUp()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
TearDown()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
CompareBuffers(const uint8_t * a,int a_stride,const uint8_t * b,int b_stride) const95 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'.
CheckBorder(const uint8_t * a,int a_size,const uint8_t * b,int b_width,int b_height,int b_stride,uint8_t c) const109 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
TestWithRandomData(PredictFunc reference)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
TestWithUnalignedDst(PredictFunc reference)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
Run()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
TEST_P(SixtapPredictTest,TestWithRandomData)228 TEST_P(SixtapPredictTest, TestWithRandomData) {
229 TestWithRandomData(vp8_sixtap_predict16x16_c);
230 }
TEST_P(SixtapPredictTest,TestWithUnalignedDst)231 TEST_P(SixtapPredictTest, TestWithUnalignedDst) {
232 TestWithUnalignedDst(vp8_sixtap_predict16x16_c);
233 }
234
TEST_P(SixtapPredictTest,TestWithPresetData)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
TEST_P(BilinearPredictTest,TestWithRandomData)355 TEST_P(BilinearPredictTest, TestWithRandomData) {
356 TestWithRandomData(vp8_bilinear_predict16x16_c);
357 }
TEST_P(BilinearPredictTest,TestWithUnalignedDst)358 TEST_P(BilinearPredictTest, TestWithUnalignedDst) {
359 TestWithUnalignedDst(vp8_bilinear_predict16x16_c);
360 }
TEST_P(BilinearPredictTest,DISABLED_Speed)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