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 #include <limits.h> 11 #include "./vpx_config.h" 12 #include "./vpx_dsp_rtcd.h" 13 #include "test/acm_random.h" 14 #include "test/buffer.h" 15 #include "test/clear_system_state.h" 16 #include "test/register_state_check.h" 17 #include "third_party/googletest/src/include/gtest/gtest.h" 18 #include "vpx/vpx_integer.h" 19 #include "vpx_mem/vpx_mem.h" 20 21 using libvpx_test::ACMRandom; 22 using libvpx_test::Buffer; 23 24 typedef void (*VpxPostProcDownAndAcrossMbRowFunc)( 25 unsigned char *src_ptr, unsigned char *dst_ptr, int src_pixels_per_line, 26 int dst_pixels_per_line, int cols, unsigned char *flimit, int size); 27 28 typedef void (*VpxMbPostProcAcrossIpFunc)(unsigned char *src, int pitch, 29 int rows, int cols, int flimit); 30 31 typedef void (*VpxMbPostProcDownFunc)(unsigned char *dst, int pitch, int rows, 32 int cols, int flimit); 33 34 namespace { 35 36 // Compute the filter level used in post proc from the loop filter strength 37 int q2mbl(int x) { 38 if (x < 20) x = 20; 39 40 x = 50 + (x - 50) * 10 / 8; 41 return x * x / 3; 42 } 43 44 class VpxPostProcDownAndAcrossMbRowTest 45 : public ::testing::TestWithParam<VpxPostProcDownAndAcrossMbRowFunc> { 46 public: 47 virtual void TearDown() { libvpx_test::ClearSystemState(); } 48 }; 49 50 // Test routine for the VPx post-processing function 51 // vpx_post_proc_down_and_across_mb_row_c. 52 53 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckFilterOutput) { 54 // Size of the underlying data block that will be filtered. 55 const int block_width = 16; 56 const int block_height = 16; 57 58 // 5-tap filter needs 2 padding rows above and below the block in the input. 59 Buffer<uint8_t> src_image = Buffer<uint8_t>(block_width, block_height, 2); 60 61 // Filter extends output block by 8 samples at left and right edges. 62 // Though the left padding is only 8 bytes, the assembly code tries to 63 // read 16 bytes before the pointer. 64 Buffer<uint8_t> dst_image = 65 Buffer<uint8_t>(block_width, block_height, 8, 16, 8, 8); 66 67 uint8_t *const flimits = 68 reinterpret_cast<uint8_t *>(vpx_memalign(16, block_width)); 69 (void)memset(flimits, 255, block_width); 70 71 // Initialize pixels in the input: 72 // block pixels to value 1, 73 // border pixels to value 10. 74 src_image.SetPadding(10); 75 src_image.Set(1); 76 77 // Initialize pixels in the output to 99. 78 dst_image.Set(99); 79 80 ASM_REGISTER_STATE_CHECK(GetParam()( 81 src_image.TopLeftPixel(), dst_image.TopLeftPixel(), src_image.stride(), 82 dst_image.stride(), block_width, flimits, 16)); 83 84 static const uint8_t kExpectedOutput[block_height] = { 85 4, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 4 86 }; 87 88 uint8_t *pixel_ptr = dst_image.TopLeftPixel(); 89 for (int i = 0; i < block_height; ++i) { 90 for (int j = 0; j < block_width; ++j) { 91 ASSERT_EQ(kExpectedOutput[i], pixel_ptr[j]) << "at (" << i << ", " << j 92 << ")"; 93 } 94 pixel_ptr += dst_image.stride(); 95 } 96 97 vpx_free(flimits); 98 }; 99 100 TEST_P(VpxPostProcDownAndAcrossMbRowTest, CheckCvsAssembly) { 101 // Size of the underlying data block that will be filtered. 102 // Y blocks are always a multiple of 16 wide and exactly 16 high. U and V 103 // blocks are always a multiple of 8 wide and exactly 8 high. 104 const int block_width = 136; 105 const int block_height = 16; 106 107 // 5-tap filter needs 2 padding rows above and below the block in the input. 108 // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16. 109 Buffer<uint8_t> src_image = 110 Buffer<uint8_t>(block_width, block_height, 2, 2, 10, 2); 111 112 // Filter extends output block by 8 samples at left and right edges. 113 // Though the left padding is only 8 bytes, there is 'above' padding as well 114 // so when the assembly code tries to read 16 bytes before the pointer it is 115 // not a problem. 116 // SSE2 reads in blocks of 16. Pad an extra 8 in case the width is not %16. 117 Buffer<uint8_t> dst_image = 118 Buffer<uint8_t>(block_width, block_height, 8, 8, 16, 8); 119 Buffer<uint8_t> dst_image_ref = Buffer<uint8_t>(block_width, block_height, 8); 120 121 // Filter values are set in blocks of 16 for Y and 8 for U/V. Each macroblock 122 // can have a different filter. SSE2 assembly reads flimits in blocks of 16 so 123 // it must be padded out. 124 const int flimits_width = block_width % 16 ? block_width + 8 : block_width; 125 uint8_t *const flimits = 126 reinterpret_cast<uint8_t *>(vpx_memalign(16, flimits_width)); 127 128 ACMRandom rnd; 129 rnd.Reset(ACMRandom::DeterministicSeed()); 130 // Initialize pixels in the input: 131 // block pixels to random values. 132 // border pixels to value 10. 133 src_image.SetPadding(10); 134 src_image.Set(&rnd, &ACMRandom::Rand8); 135 136 for (int blocks = 0; blocks < block_width; blocks += 8) { 137 (void)memset(flimits, 0, sizeof(*flimits) * flimits_width); 138 139 for (int f = 0; f < 255; f++) { 140 (void)memset(flimits + blocks, f, sizeof(*flimits) * 8); 141 142 dst_image.Set(0); 143 dst_image_ref.Set(0); 144 145 vpx_post_proc_down_and_across_mb_row_c( 146 src_image.TopLeftPixel(), dst_image_ref.TopLeftPixel(), 147 src_image.stride(), dst_image_ref.stride(), block_width, flimits, 148 block_height); 149 ASM_REGISTER_STATE_CHECK( 150 GetParam()(src_image.TopLeftPixel(), dst_image.TopLeftPixel(), 151 src_image.stride(), dst_image.stride(), block_width, 152 flimits, block_height)); 153 154 ASSERT_TRUE(dst_image.CheckValues(dst_image_ref)); 155 } 156 } 157 158 vpx_free(flimits); 159 } 160 161 class VpxMbPostProcAcrossIpTest 162 : public ::testing::TestWithParam<VpxMbPostProcAcrossIpFunc> { 163 public: 164 virtual void TearDown() { libvpx_test::ClearSystemState(); } 165 166 protected: 167 void SetCols(unsigned char *s, int rows, int cols, int src_width) { 168 for (int r = 0; r < rows; r++) { 169 for (int c = 0; c < cols; c++) { 170 s[c] = c; 171 } 172 s += src_width; 173 } 174 } 175 176 void RunComparison(const unsigned char *expected_output, unsigned char *src_c, 177 int rows, int cols, int src_pitch) { 178 for (int r = 0; r < rows; r++) { 179 for (int c = 0; c < cols; c++) { 180 ASSERT_EQ(expected_output[c], src_c[c]) << "at (" << r << ", " << c 181 << ")"; 182 } 183 src_c += src_pitch; 184 } 185 } 186 187 void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width, 188 int filter_level, const unsigned char *expected_output) { 189 ASM_REGISTER_STATE_CHECK( 190 GetParam()(s, src_width, rows, cols, filter_level)); 191 RunComparison(expected_output, s, rows, cols, src_width); 192 } 193 }; 194 195 TEST_P(VpxMbPostProcAcrossIpTest, CheckLowFilterOutput) { 196 const int rows = 16; 197 const int cols = 16; 198 199 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8); 200 src.SetPadding(10); 201 SetCols(src.TopLeftPixel(), rows, cols, src.stride()); 202 203 Buffer<uint8_t> expected_output = Buffer<uint8_t>(cols, rows, 0); 204 SetCols(expected_output.TopLeftPixel(), rows, cols, expected_output.stride()); 205 206 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(0), 207 expected_output.TopLeftPixel()); 208 } 209 210 TEST_P(VpxMbPostProcAcrossIpTest, CheckMediumFilterOutput) { 211 const int rows = 16; 212 const int cols = 16; 213 214 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8); 215 src.SetPadding(10); 216 SetCols(src.TopLeftPixel(), rows, cols, src.stride()); 217 218 static const unsigned char kExpectedOutput[cols] = { 219 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 13 220 }; 221 222 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(70), 223 kExpectedOutput); 224 } 225 226 TEST_P(VpxMbPostProcAcrossIpTest, CheckHighFilterOutput) { 227 const int rows = 16; 228 const int cols = 16; 229 230 Buffer<uint8_t> src = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8); 231 src.SetPadding(10); 232 SetCols(src.TopLeftPixel(), rows, cols, src.stride()); 233 234 static const unsigned char kExpectedOutput[cols] = { 235 2, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 13 236 }; 237 238 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), INT_MAX, 239 kExpectedOutput); 240 241 SetCols(src.TopLeftPixel(), rows, cols, src.stride()); 242 243 RunFilterLevel(src.TopLeftPixel(), rows, cols, src.stride(), q2mbl(100), 244 kExpectedOutput); 245 } 246 247 TEST_P(VpxMbPostProcAcrossIpTest, CheckCvsAssembly) { 248 const int rows = 16; 249 const int cols = 16; 250 251 Buffer<uint8_t> c_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8); 252 Buffer<uint8_t> asm_mem = Buffer<uint8_t>(cols, rows, 8, 8, 17, 8); 253 254 // When level >= 100, the filter behaves the same as the level = INT_MAX 255 // When level < 20, it behaves the same as the level = 0 256 for (int level = 0; level < 100; level++) { 257 c_mem.SetPadding(10); 258 asm_mem.SetPadding(10); 259 SetCols(c_mem.TopLeftPixel(), rows, cols, c_mem.stride()); 260 SetCols(asm_mem.TopLeftPixel(), rows, cols, asm_mem.stride()); 261 262 vpx_mbpost_proc_across_ip_c(c_mem.TopLeftPixel(), c_mem.stride(), rows, 263 cols, q2mbl(level)); 264 ASM_REGISTER_STATE_CHECK(GetParam()( 265 asm_mem.TopLeftPixel(), asm_mem.stride(), rows, cols, q2mbl(level))); 266 267 ASSERT_TRUE(asm_mem.CheckValues(c_mem)); 268 } 269 } 270 271 class VpxMbPostProcDownTest 272 : public ::testing::TestWithParam<VpxMbPostProcDownFunc> { 273 public: 274 virtual void TearDown() { libvpx_test::ClearSystemState(); } 275 276 protected: 277 void SetRows(unsigned char *src_c, int rows, int cols, int src_width) { 278 for (int r = 0; r < rows; r++) { 279 memset(src_c, r, cols); 280 src_c += src_width; 281 } 282 } 283 284 void RunComparison(const unsigned char *expected_output, unsigned char *src_c, 285 int rows, int cols, int src_pitch) { 286 for (int r = 0; r < rows; r++) { 287 for (int c = 0; c < cols; c++) { 288 ASSERT_EQ(expected_output[r * rows + c], src_c[c]) << "at (" << r 289 << ", " << c << ")"; 290 } 291 src_c += src_pitch; 292 } 293 } 294 295 void RunFilterLevel(unsigned char *s, int rows, int cols, int src_width, 296 int filter_level, const unsigned char *expected_output) { 297 ASM_REGISTER_STATE_CHECK( 298 GetParam()(s, src_width, rows, cols, filter_level)); 299 RunComparison(expected_output, s, rows, cols, src_width); 300 } 301 }; 302 303 TEST_P(VpxMbPostProcDownTest, CheckHighFilterOutput) { 304 const int rows = 16; 305 const int cols = 16; 306 307 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17); 308 src_c.SetPadding(10); 309 310 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); 311 312 static const unsigned char kExpectedOutput[rows * cols] = { 313 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 314 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 3, 3, 3, 3, 3, 3, 315 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 3, 4, 4, 3, 3, 3, 316 4, 4, 3, 4, 4, 3, 3, 4, 5, 4, 4, 4, 4, 4, 4, 4, 5, 4, 4, 317 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 318 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 319 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 320 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 8, 9, 9, 8, 8, 8, 9, 321 9, 8, 9, 9, 8, 8, 8, 9, 9, 10, 10, 9, 9, 9, 10, 10, 9, 10, 10, 322 9, 9, 9, 10, 10, 10, 11, 10, 10, 10, 11, 10, 11, 10, 11, 10, 10, 10, 11, 323 10, 11, 11, 11, 11, 11, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 12, 11, 12, 324 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 12, 325 13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13, 326 13, 13, 13, 13, 14, 13, 13, 13, 13 327 }; 328 329 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), INT_MAX, 330 kExpectedOutput); 331 332 src_c.SetPadding(10); 333 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); 334 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(100), 335 kExpectedOutput); 336 } 337 338 TEST_P(VpxMbPostProcDownTest, CheckMediumFilterOutput) { 339 const int rows = 16; 340 const int cols = 16; 341 342 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17); 343 src_c.SetPadding(10); 344 345 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); 346 347 static const unsigned char kExpectedOutput[rows * cols] = { 348 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 349 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 350 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 351 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 352 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 353 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 354 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 355 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 356 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 357 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 358 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 13, 359 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 12, 12, 13, 12, 360 13, 12, 13, 12, 12, 12, 13, 12, 13, 12, 13, 12, 13, 13, 13, 14, 13, 13, 13, 361 13, 13, 13, 13, 14, 13, 13, 13, 13 362 }; 363 364 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(70), 365 kExpectedOutput); 366 } 367 368 TEST_P(VpxMbPostProcDownTest, CheckLowFilterOutput) { 369 const int rows = 16; 370 const int cols = 16; 371 372 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17); 373 src_c.SetPadding(10); 374 375 SetRows(src_c.TopLeftPixel(), rows, cols, src_c.stride()); 376 377 unsigned char *expected_output = new unsigned char[rows * cols]; 378 ASSERT_TRUE(expected_output != NULL); 379 SetRows(expected_output, rows, cols, cols); 380 381 RunFilterLevel(src_c.TopLeftPixel(), rows, cols, src_c.stride(), q2mbl(0), 382 expected_output); 383 384 delete[] expected_output; 385 } 386 387 TEST_P(VpxMbPostProcDownTest, CheckCvsAssembly) { 388 const int rows = 16; 389 const int cols = 16; 390 391 ACMRandom rnd; 392 rnd.Reset(ACMRandom::DeterministicSeed()); 393 394 Buffer<uint8_t> src_c = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17); 395 Buffer<uint8_t> src_asm = Buffer<uint8_t>(cols, rows, 8, 8, 8, 17); 396 397 for (int level = 0; level < 100; level++) { 398 src_c.SetPadding(10); 399 src_asm.SetPadding(10); 400 src_c.Set(&rnd, &ACMRandom::Rand8); 401 src_asm.CopyFrom(src_c); 402 403 vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols, 404 q2mbl(level)); 405 ASM_REGISTER_STATE_CHECK(GetParam()( 406 src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level))); 407 ASSERT_TRUE(src_asm.CheckValues(src_c)); 408 409 src_c.SetPadding(10); 410 src_asm.SetPadding(10); 411 src_c.Set(&rnd, &ACMRandom::Rand8Extremes); 412 src_asm.CopyFrom(src_c); 413 414 vpx_mbpost_proc_down_c(src_c.TopLeftPixel(), src_c.stride(), rows, cols, 415 q2mbl(level)); 416 ASM_REGISTER_STATE_CHECK(GetParam()( 417 src_asm.TopLeftPixel(), src_asm.stride(), rows, cols, q2mbl(level))); 418 ASSERT_TRUE(src_asm.CheckValues(src_c)); 419 } 420 } 421 422 INSTANTIATE_TEST_CASE_P( 423 C, VpxPostProcDownAndAcrossMbRowTest, 424 ::testing::Values(vpx_post_proc_down_and_across_mb_row_c)); 425 426 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcAcrossIpTest, 427 ::testing::Values(vpx_mbpost_proc_across_ip_c)); 428 429 INSTANTIATE_TEST_CASE_P(C, VpxMbPostProcDownTest, 430 ::testing::Values(vpx_mbpost_proc_down_c)); 431 432 #if HAVE_SSE2 433 INSTANTIATE_TEST_CASE_P( 434 SSE2, VpxPostProcDownAndAcrossMbRowTest, 435 ::testing::Values(vpx_post_proc_down_and_across_mb_row_sse2)); 436 437 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcAcrossIpTest, 438 ::testing::Values(vpx_mbpost_proc_across_ip_sse2)); 439 440 INSTANTIATE_TEST_CASE_P(SSE2, VpxMbPostProcDownTest, 441 ::testing::Values(vpx_mbpost_proc_down_sse2)); 442 #endif // HAVE_SSE2 443 444 #if HAVE_NEON 445 INSTANTIATE_TEST_CASE_P( 446 NEON, VpxPostProcDownAndAcrossMbRowTest, 447 ::testing::Values(vpx_post_proc_down_and_across_mb_row_neon)); 448 449 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcAcrossIpTest, 450 ::testing::Values(vpx_mbpost_proc_across_ip_neon)); 451 452 INSTANTIATE_TEST_CASE_P(NEON, VpxMbPostProcDownTest, 453 ::testing::Values(vpx_mbpost_proc_down_neon)); 454 #endif // HAVE_NEON 455 456 #if HAVE_MSA 457 INSTANTIATE_TEST_CASE_P( 458 MSA, VpxPostProcDownAndAcrossMbRowTest, 459 ::testing::Values(vpx_post_proc_down_and_across_mb_row_msa)); 460 461 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcAcrossIpTest, 462 ::testing::Values(vpx_mbpost_proc_across_ip_msa)); 463 464 INSTANTIATE_TEST_CASE_P(MSA, VpxMbPostProcDownTest, 465 ::testing::Values(vpx_mbpost_proc_down_msa)); 466 #endif // HAVE_MSA 467 468 } // namespace 469