scale_test.cc 23.2 KB
Newer Older
1
/*
2
 *  Copyright 2011 The LibYuv Project Authors. All rights reserved.
3 4 5 6
 *
 *  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
7
 *  in the file PATENTS. All contributing project authors may
8 9 10 11 12 13
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include <stdlib.h>
#include <time.h>

Frank Barchard's avatar
Frank Barchard committed
14
#include "../unit_test/unit_test.h"
frkoenig@google.com's avatar
frkoenig@google.com committed
15 16
#include "libyuv/cpu_id.h"
#include "libyuv/scale.h"
17
#include "libyuv/scale_row.h"  // For ScaleRowDown2Box_Odd_C
frkoenig@google.com's avatar
frkoenig@google.com committed
18

19 20 21
#define STRINGIZE(line) #line
#define FILELINESTR(file, line) file ":" STRINGIZE(line)

mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
22
namespace libyuv {
23

24
// Test scaling with C vs Opt and return maximum pixel difference. 0 = exact.
Frank Barchard's avatar
Frank Barchard committed
25 26 27 28 29 30 31 32
static int TestFilter(int src_width,
                      int src_height,
                      int dst_width,
                      int dst_height,
                      FilterMode f,
                      int benchmark_iterations,
                      int disable_cpu_flags,
                      int benchmark_cpu_info) {
33 34 35 36
  if (!SizeValid(src_width, src_height, dst_width, dst_height)) {
    return 0;
  }

37
  int i, j;
38 39
  int src_width_uv = (Abs(src_width) + 1) >> 1;
  int src_height_uv = (Abs(src_height) + 1) >> 1;
40

Frank Barchard's avatar
Frank Barchard committed
41 42
  int64_t src_y_plane_size = (Abs(src_width)) * (Abs(src_height));
  int64_t src_uv_plane_size = (src_width_uv) * (src_height_uv);
43

44 45
  int src_stride_y = Abs(src_width);
  int src_stride_uv = src_width_uv;
46

47 48 49 50
  align_buffer_page_end(src_y, src_y_plane_size);
  align_buffer_page_end(src_u, src_uv_plane_size);
  align_buffer_page_end(src_v, src_uv_plane_size);
  if (!src_y || !src_u || !src_v) {
51 52 53
    printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    return 0;
  }
54 55 56
  MemRandomize(src_y, src_y_plane_size);
  MemRandomize(src_u, src_uv_plane_size);
  MemRandomize(src_v, src_uv_plane_size);
57

58 59
  int dst_width_uv = (dst_width + 1) >> 1;
  int dst_height_uv = (dst_height + 1) >> 1;
60

Frank Barchard's avatar
Frank Barchard committed
61 62
  int64_t dst_y_plane_size = (dst_width) * (dst_height);
  int64_t dst_uv_plane_size = (dst_width_uv) * (dst_height_uv);
63

64 65
  int dst_stride_y = dst_width;
  int dst_stride_uv = dst_width_uv;
66

67 68 69 70 71 72 73 74
  align_buffer_page_end(dst_y_c, dst_y_plane_size);
  align_buffer_page_end(dst_u_c, dst_uv_plane_size);
  align_buffer_page_end(dst_v_c, dst_uv_plane_size);
  align_buffer_page_end(dst_y_opt, dst_y_plane_size);
  align_buffer_page_end(dst_u_opt, dst_uv_plane_size);
  align_buffer_page_end(dst_v_opt, dst_uv_plane_size);
  if (!dst_y_c || !dst_u_c || !dst_v_c || !dst_y_opt || !dst_u_opt ||
      !dst_v_opt) {
75 76 77
    printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    return 0;
  }
78

79
  MaskCpuFlags(disable_cpu_flags);  // Disable all CPU optimization.
80
  double c_time = get_time();
81 82 83
  I420Scale(src_y, src_stride_y, src_u, src_stride_uv, src_v, src_stride_uv,
            src_width, src_height, dst_y_c, dst_stride_y, dst_u_c,
            dst_stride_uv, dst_v_c, dst_stride_uv, dst_width, dst_height, f);
84
  c_time = (get_time() - c_time);
85

86
  MaskCpuFlags(benchmark_cpu_info);  // Enable all CPU optimization.
87
  double opt_time = get_time();
88
  for (i = 0; i < benchmark_iterations; ++i) {
89 90 91 92
    I420Scale(src_y, src_stride_y, src_u, src_stride_uv, src_v, src_stride_uv,
              src_width, src_height, dst_y_opt, dst_stride_y, dst_u_opt,
              dst_stride_uv, dst_v_opt, dst_stride_uv, dst_width, dst_height,
              f);
fbarchard@google.com's avatar
fbarchard@google.com committed
93
  }
94
  opt_time = (get_time() - opt_time) / benchmark_iterations;
95
  // Report performance of C vs OPT.
Frank Barchard's avatar
Frank Barchard committed
96 97
  printf("filter %d - %8d us C - %8d us OPT\n", f,
         static_cast<int>(c_time * 1e6), static_cast<int>(opt_time * 1e6));
98

99 100
  // C version may be a little off from the optimized. Order of
  //  operations may introduce rounding somewhere. So do a difference
101 102
  //  of the buffers and look to see that the max difference is not
  //  over 3.
103
  int max_diff = 0;
104 105
  for (i = 0; i < (dst_height); ++i) {
    for (j = 0; j < (dst_width); ++j) {
106
      int abs_diff = Abs(dst_y_c[(i * dst_stride_y) + j] -
107
                         dst_y_opt[(i * dst_stride_y) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
108
      if (abs_diff > max_diff) {
109
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
110
      }
111
    }
112
  }
113

114 115
  for (i = 0; i < (dst_height_uv); ++i) {
    for (j = 0; j < (dst_width_uv); ++j) {
116
      int abs_diff = Abs(dst_u_c[(i * dst_stride_uv) + j] -
117
                         dst_u_opt[(i * dst_stride_uv) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
118
      if (abs_diff > max_diff) {
119
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
120
      }
121
      abs_diff = Abs(dst_v_c[(i * dst_stride_uv) + j] -
122
                     dst_v_opt[(i * dst_stride_uv) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
123
      if (abs_diff > max_diff) {
124
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
125
      }
126
    }
127 128
  }

129 130 131 132 133 134 135 136 137 138 139
  free_aligned_buffer_page_end(dst_y_c);
  free_aligned_buffer_page_end(dst_u_c);
  free_aligned_buffer_page_end(dst_v_c);
  free_aligned_buffer_page_end(dst_y_opt);
  free_aligned_buffer_page_end(dst_u_opt);
  free_aligned_buffer_page_end(dst_v_opt);
  free_aligned_buffer_page_end(src_y);
  free_aligned_buffer_page_end(src_u);
  free_aligned_buffer_page_end(src_v);

  return max_diff;
140 141
}

142 143
// Test scaling with 8 bit C vs 16 bit C and return maximum pixel difference.
// 0 = exact.
Frank Barchard's avatar
Frank Barchard committed
144 145 146 147 148
static int TestFilter_16(int src_width,
                         int src_height,
                         int dst_width,
                         int dst_height,
                         FilterMode f,
149 150 151
                         int benchmark_iterations,
                         int disable_cpu_flags,
                         int benchmark_cpu_info) {
152 153 154 155
  if (!SizeValid(src_width, src_height, dst_width, dst_height)) {
    return 0;
  }

156
  int i;
157 158 159
  int src_width_uv = (Abs(src_width) + 1) >> 1;
  int src_height_uv = (Abs(src_height) + 1) >> 1;

Frank Barchard's avatar
Frank Barchard committed
160 161
  int64_t src_y_plane_size = (Abs(src_width)) * (Abs(src_height));
  int64_t src_uv_plane_size = (src_width_uv) * (src_height_uv);
162

163 164
  int src_stride_y = Abs(src_width);
  int src_stride_uv = src_width_uv;
165

166 167 168 169 170 171
  align_buffer_page_end(src_y, src_y_plane_size);
  align_buffer_page_end(src_u, src_uv_plane_size);
  align_buffer_page_end(src_v, src_uv_plane_size);
  align_buffer_page_end(src_y_16, src_y_plane_size * 2);
  align_buffer_page_end(src_u_16, src_uv_plane_size * 2);
  align_buffer_page_end(src_v_16, src_uv_plane_size * 2);
172 173 174 175
  if (!src_y || !src_u || !src_v || !src_y_16 || !src_u_16 || !src_v_16) {
    printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    return 0;
  }
Frank Barchard's avatar
Frank Barchard committed
176 177 178
  uint16_t* p_src_y_16 = reinterpret_cast<uint16_t*>(src_y_16);
  uint16_t* p_src_u_16 = reinterpret_cast<uint16_t*>(src_u_16);
  uint16_t* p_src_v_16 = reinterpret_cast<uint16_t*>(src_v_16);
179 180 181 182 183

  MemRandomize(src_y, src_y_plane_size);
  MemRandomize(src_u, src_uv_plane_size);
  MemRandomize(src_v, src_uv_plane_size);

184 185
  for (i = 0; i < src_y_plane_size; ++i) {
    p_src_y_16[i] = src_y[i];
186
  }
187 188 189
  for (i = 0; i < src_uv_plane_size; ++i) {
    p_src_u_16[i] = src_u[i];
    p_src_v_16[i] = src_v[i];
190 191 192 193 194
  }

  int dst_width_uv = (dst_width + 1) >> 1;
  int dst_height_uv = (dst_height + 1) >> 1;

195 196
  int dst_y_plane_size = (dst_width) * (dst_height);
  int dst_uv_plane_size = (dst_width_uv) * (dst_height_uv);
197

198 199
  int dst_stride_y = dst_width;
  int dst_stride_uv = dst_width_uv;
200

201 202 203 204 205 206
  align_buffer_page_end(dst_y_8, dst_y_plane_size);
  align_buffer_page_end(dst_u_8, dst_uv_plane_size);
  align_buffer_page_end(dst_v_8, dst_uv_plane_size);
  align_buffer_page_end(dst_y_16, dst_y_plane_size * 2);
  align_buffer_page_end(dst_u_16, dst_uv_plane_size * 2);
  align_buffer_page_end(dst_v_16, dst_uv_plane_size * 2);
Frank Barchard's avatar
Frank Barchard committed
207

Frank Barchard's avatar
Frank Barchard committed
208 209 210
  uint16_t* p_dst_y_16 = reinterpret_cast<uint16_t*>(dst_y_16);
  uint16_t* p_dst_u_16 = reinterpret_cast<uint16_t*>(dst_u_16);
  uint16_t* p_dst_v_16 = reinterpret_cast<uint16_t*>(dst_v_16);
211

212 213 214 215 216
  MaskCpuFlags(disable_cpu_flags);  // Disable all CPU optimization.
  I420Scale(src_y, src_stride_y, src_u, src_stride_uv, src_v, src_stride_uv,
            src_width, src_height, dst_y_8, dst_stride_y, dst_u_8,
            dst_stride_uv, dst_v_8, dst_stride_uv, dst_width, dst_height, f);
  MaskCpuFlags(benchmark_cpu_info);  // Enable all CPU optimization.
217
  for (i = 0; i < benchmark_iterations; ++i) {
218 219 220 221
    I420Scale_16(p_src_y_16, src_stride_y, p_src_u_16, src_stride_uv,
                 p_src_v_16, src_stride_uv, src_width, src_height, p_dst_y_16,
                 dst_stride_y, p_dst_u_16, dst_stride_uv, p_dst_v_16,
                 dst_stride_uv, dst_width, dst_height, f);
222 223
  }

224
  // Expect an exact match.
225
  int max_diff = 0;
226 227 228 229
  for (i = 0; i < dst_y_plane_size; ++i) {
    int abs_diff = Abs(dst_y_8[i] - p_dst_y_16[i]);
    if (abs_diff > max_diff) {
      max_diff = abs_diff;
230 231
    }
  }
232 233 234 235 236 237 238 239
  for (i = 0; i < dst_uv_plane_size; ++i) {
    int abs_diff = Abs(dst_u_8[i] - p_dst_u_16[i]);
    if (abs_diff > max_diff) {
      max_diff = abs_diff;
    }
    abs_diff = Abs(dst_v_8[i] - p_dst_v_16[i]);
    if (abs_diff > max_diff) {
      max_diff = abs_diff;
240 241 242
    }
  }

243 244 245 246 247 248 249 250 251 252 253 254 255 256
  free_aligned_buffer_page_end(dst_y_8);
  free_aligned_buffer_page_end(dst_u_8);
  free_aligned_buffer_page_end(dst_v_8);
  free_aligned_buffer_page_end(dst_y_16);
  free_aligned_buffer_page_end(dst_u_16);
  free_aligned_buffer_page_end(dst_v_16);
  free_aligned_buffer_page_end(src_y);
  free_aligned_buffer_page_end(src_u);
  free_aligned_buffer_page_end(src_v);
  free_aligned_buffer_page_end(src_y_16);
  free_aligned_buffer_page_end(src_u_16);
  free_aligned_buffer_page_end(src_v_16);

  return max_diff;
257 258
}

259 260
// The following adjustments in dimensions ensure the scale factor will be
// exactly achieved.
261
// 2 is chroma subsample.
262 263
#define DX(x, nom, denom) static_cast<int>(((Abs(x) / nom + 1) / 2) * nom * 2)
#define SX(x, nom, denom) static_cast<int>(((x / nom + 1) / 2) * denom * 2)
264

Frank Barchard's avatar
Frank Barchard committed
265 266 267 268 269 270 271 272 273
#define TEST_FACTOR1(name, filter, nom, denom, max_diff)                     \
  TEST_F(LibYUVScaleTest, ScaleDownBy##name##_##filter) {                    \
    int diff = TestFilter(                                                   \
        SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \
        DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \
        kFilter##filter, benchmark_iterations_, disable_cpu_flags_,          \
        benchmark_cpu_info_);                                                \
    EXPECT_LE(diff, max_diff);                                               \
  }                                                                          \
274
  TEST_F(LibYUVScaleTest, ScaleDownBy##name##_##filter##_16) {               \
Frank Barchard's avatar
Frank Barchard committed
275 276 277
    int diff = TestFilter_16(                                                \
        SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \
        DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \
278 279
        kFilter##filter, benchmark_iterations_, disable_cpu_flags_,          \
        benchmark_cpu_info_);                                                \
Frank Barchard's avatar
Frank Barchard committed
280 281
    EXPECT_LE(diff, max_diff);                                               \
  }
282

283
// Test a scale factor with all 4 filters.  Expect unfiltered to be exact, but
284
// filtering is different fixed point implementations for SSSE3, Neon and C.
Frank Barchard's avatar
Frank Barchard committed
285 286 287 288 289
#define TEST_FACTOR(name, nom, denom, boxdiff) \
  TEST_FACTOR1(name, None, nom, denom, 0)      \
  TEST_FACTOR1(name, Linear, nom, denom, 3)    \
  TEST_FACTOR1(name, Bilinear, nom, denom, 3)  \
  TEST_FACTOR1(name, Box, nom, denom, boxdiff)
290 291

TEST_FACTOR(2, 1, 2, 0)
292
TEST_FACTOR(4, 1, 4, 0)
293
TEST_FACTOR(8, 1, 8, 0)
294 295
TEST_FACTOR(3by4, 3, 4, 1)
TEST_FACTOR(3by8, 3, 8, 1)
296
TEST_FACTOR(3, 1, 3, 0)
297 298
#undef TEST_FACTOR1
#undef TEST_FACTOR
299 300
#undef SX
#undef DX
301

Frank Barchard's avatar
Frank Barchard committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315
#define TEST_SCALETO1(name, width, height, filter, max_diff)                  \
  TEST_F(LibYUVScaleTest, name##To##width##x##height##_##filter) {            \
    int diff = TestFilter(benchmark_width_, benchmark_height_, width, height, \
                          kFilter##filter, benchmark_iterations_,             \
                          disable_cpu_flags_, benchmark_cpu_info_);           \
    EXPECT_LE(diff, max_diff);                                                \
  }                                                                           \
  TEST_F(LibYUVScaleTest, name##From##width##x##height##_##filter) {          \
    int diff = TestFilter(width, height, Abs(benchmark_width_),               \
                          Abs(benchmark_height_), kFilter##filter,            \
                          benchmark_iterations_, disable_cpu_flags_,          \
                          benchmark_cpu_info_);                               \
    EXPECT_LE(diff, max_diff);                                                \
  }                                                                           \
316
  TEST_F(LibYUVScaleTest, name##To##width##x##height##_##filter##_16) {       \
Frank Barchard's avatar
Frank Barchard committed
317
    int diff = TestFilter_16(benchmark_width_, benchmark_height_, width,      \
318 319
                             height, kFilter##filter, benchmark_iterations_,  \
                             disable_cpu_flags_, benchmark_cpu_info_);        \
Frank Barchard's avatar
Frank Barchard committed
320 321
    EXPECT_LE(diff, max_diff);                                                \
  }                                                                           \
322
  TEST_F(LibYUVScaleTest, name##From##width##x##height##_##filter##_16) {     \
Frank Barchard's avatar
Frank Barchard committed
323 324
    int diff = TestFilter_16(width, height, Abs(benchmark_width_),            \
                             Abs(benchmark_height_), kFilter##filter,         \
325 326
                             benchmark_iterations_, disable_cpu_flags_,       \
                             benchmark_cpu_info_);                            \
Frank Barchard's avatar
Frank Barchard committed
327 328
    EXPECT_LE(diff, max_diff);                                                \
  }
329

330
// Test scale to a specified size with all 4 filters.
Frank Barchard's avatar
Frank Barchard committed
331 332
#define TEST_SCALETO(name, width, height)         \
  TEST_SCALETO1(name, width, height, None, 0)     \
333 334 335
  TEST_SCALETO1(name, width, height, Linear, 3)   \
  TEST_SCALETO1(name, width, height, Bilinear, 3) \
  TEST_SCALETO1(name, width, height, Box, 3)
336

337
TEST_SCALETO(Scale, 1, 1)
338
TEST_SCALETO(Scale, 320, 240)
339
TEST_SCALETO(Scale, 352, 288)
340
TEST_SCALETO(Scale, 569, 480)
fbarchard@google.com's avatar
fbarchard@google.com committed
341
TEST_SCALETO(Scale, 640, 360)
342
TEST_SCALETO(Scale, 1280, 720)
343 344
#undef TEST_SCALETO1
#undef TEST_SCALETO
345

346
#ifdef HAS_SCALEROWDOWN2_SSSE3
347
TEST_F(LibYUVScaleTest, TestScaleRowDown2Box_Odd_SSSE3) {
Frank Barchard's avatar
Frank Barchard committed
348 349 350
  SIMD_ALIGNED(uint8_t orig_pixels[128 * 2]);
  SIMD_ALIGNED(uint8_t dst_pixels_opt[64]);
  SIMD_ALIGNED(uint8_t dst_pixels_c[64]);
351 352 353 354
  memset(orig_pixels, 0, sizeof(orig_pixels));
  memset(dst_pixels_opt, 0, sizeof(dst_pixels_opt));
  memset(dst_pixels_c, 0, sizeof(dst_pixels_c));

355 356 357 358
  int has_ssse3 = TestCpuFlag(kCpuHasSSSE3);
  if (!has_ssse3) {
    printf("Warning SSSE3 not detected; Skipping test.\n");
  } else {
359
    // TL.
360 361 362 363
    orig_pixels[0] = 255u;
    orig_pixels[1] = 0u;
    orig_pixels[128 + 0] = 0u;
    orig_pixels[128 + 1] = 0u;
364
    // TR.
365 366 367 368
    orig_pixels[2] = 0u;
    orig_pixels[3] = 100u;
    orig_pixels[128 + 2] = 0u;
    orig_pixels[128 + 3] = 0u;
369
    // BL.
370 371 372 373
    orig_pixels[4] = 0u;
    orig_pixels[5] = 0u;
    orig_pixels[128 + 4] = 50u;
    orig_pixels[128 + 5] = 0u;
374
    // BR.
375 376 377 378
    orig_pixels[6] = 0u;
    orig_pixels[7] = 0u;
    orig_pixels[128 + 6] = 0u;
    orig_pixels[128 + 7] = 20u;
379
    // Odd.
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431
    orig_pixels[126] = 4u;
    orig_pixels[127] = 255u;
    orig_pixels[128 + 126] = 16u;
    orig_pixels[128 + 127] = 255u;

    // Test regular half size.
    ScaleRowDown2Box_C(orig_pixels, 128, dst_pixels_c, 64);

    EXPECT_EQ(64u, dst_pixels_c[0]);
    EXPECT_EQ(25u, dst_pixels_c[1]);
    EXPECT_EQ(13u, dst_pixels_c[2]);
    EXPECT_EQ(5u, dst_pixels_c[3]);
    EXPECT_EQ(0u, dst_pixels_c[4]);
    EXPECT_EQ(133u, dst_pixels_c[63]);

    // Test Odd width version - Last pixel is just 1 horizontal pixel.
    ScaleRowDown2Box_Odd_C(orig_pixels, 128, dst_pixels_c, 64);

    EXPECT_EQ(64u, dst_pixels_c[0]);
    EXPECT_EQ(25u, dst_pixels_c[1]);
    EXPECT_EQ(13u, dst_pixels_c[2]);
    EXPECT_EQ(5u, dst_pixels_c[3]);
    EXPECT_EQ(0u, dst_pixels_c[4]);
    EXPECT_EQ(10u, dst_pixels_c[63]);

    // Test one pixel less, should skip the last pixel.
    memset(dst_pixels_c, 0, sizeof(dst_pixels_c));
    ScaleRowDown2Box_Odd_C(orig_pixels, 128, dst_pixels_c, 63);

    EXPECT_EQ(64u, dst_pixels_c[0]);
    EXPECT_EQ(25u, dst_pixels_c[1]);
    EXPECT_EQ(13u, dst_pixels_c[2]);
    EXPECT_EQ(5u, dst_pixels_c[3]);
    EXPECT_EQ(0u, dst_pixels_c[4]);
    EXPECT_EQ(0u, dst_pixels_c[63]);

    // Test regular half size SSSE3.
    ScaleRowDown2Box_SSSE3(orig_pixels, 128, dst_pixels_opt, 64);

    EXPECT_EQ(64u, dst_pixels_opt[0]);
    EXPECT_EQ(25u, dst_pixels_opt[1]);
    EXPECT_EQ(13u, dst_pixels_opt[2]);
    EXPECT_EQ(5u, dst_pixels_opt[3]);
    EXPECT_EQ(0u, dst_pixels_opt[4]);
    EXPECT_EQ(133u, dst_pixels_opt[63]);

    // Compare C and SSSE3 match.
    ScaleRowDown2Box_Odd_C(orig_pixels, 128, dst_pixels_c, 64);
    ScaleRowDown2Box_Odd_SSSE3(orig_pixels, 128, dst_pixels_opt, 64);
    for (int i = 0; i < 64; ++i) {
      EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
    }
432 433 434 435
  }
}
#endif  // HAS_SCALEROWDOWN2_SSSE3

Frank Barchard's avatar
Frank Barchard committed
436
extern "C" void ScaleRowUp2_16_NEON(const uint16_t* src_ptr,
437
                                    ptrdiff_t src_stride,
Frank Barchard's avatar
Frank Barchard committed
438
                                    uint16_t* dst,
439
                                    int dst_width);
Frank Barchard's avatar
Frank Barchard committed
440
extern "C" void ScaleRowUp2_16_C(const uint16_t* src_ptr,
441
                                 ptrdiff_t src_stride,
Frank Barchard's avatar
Frank Barchard committed
442
                                 uint16_t* dst,
443
                                 int dst_width);
444

445
TEST_F(LibYUVScaleTest, TestScaleRowUp2_16) {
Frank Barchard's avatar
Frank Barchard committed
446 447 448
  SIMD_ALIGNED(uint16_t orig_pixels[640 * 2 + 1]);  // 2 rows + 1 pixel overrun.
  SIMD_ALIGNED(uint16_t dst_pixels_opt[1280]);
  SIMD_ALIGNED(uint16_t dst_pixels_c[1280]);
449 450 451 452 453 454 455 456

  memset(orig_pixels, 0, sizeof(orig_pixels));
  memset(dst_pixels_opt, 1, sizeof(dst_pixels_opt));
  memset(dst_pixels_c, 2, sizeof(dst_pixels_c));

  for (int i = 0; i < 640 * 2 + 1; ++i) {
    orig_pixels[i] = i;
  }
457
  ScaleRowUp2_16_C(&orig_pixels[0], 640, &dst_pixels_c[0], 1280);
458
  for (int i = 0; i < benchmark_pixels_div1280_; ++i) {
459 460 461 462 463 464 465 466 467 468
#if !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
    int has_neon = TestCpuFlag(kCpuHasNEON);
    if (has_neon) {
      ScaleRowUp2_16_NEON(&orig_pixels[0], 640, &dst_pixels_opt[0], 1280);
    } else {
      ScaleRowUp2_16_C(&orig_pixels[0], 640, &dst_pixels_opt[0], 1280);
    }
#else
    ScaleRowUp2_16_C(&orig_pixels[0], 640, &dst_pixels_opt[0], 1280);
#endif
469 470 471 472 473 474 475 476 477
  }

  for (int i = 0; i < 1280; ++i) {
    EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
  }
  EXPECT_EQ(dst_pixels_c[0], (0 * 9 + 1 * 3 + 640 * 3 + 641 * 1 + 8) / 16);
  EXPECT_EQ(dst_pixels_c[1279], 800);
}

Frank Barchard's avatar
Frank Barchard committed
478
extern "C" void ScaleRowDown2Box_16_NEON(const uint16_t* src_ptr,
479
                                         ptrdiff_t src_stride,
Frank Barchard's avatar
Frank Barchard committed
480
                                         uint16_t* dst,
481
                                         int dst_width);
482

483
TEST_F(LibYUVScaleTest, TestScaleRowDown2Box_16) {
Frank Barchard's avatar
Frank Barchard committed
484 485 486
  SIMD_ALIGNED(uint16_t orig_pixels[2560 * 2]);
  SIMD_ALIGNED(uint16_t dst_pixels_c[1280]);
  SIMD_ALIGNED(uint16_t dst_pixels_opt[1280]);
487 488

  memset(orig_pixels, 0, sizeof(orig_pixels));
489 490
  memset(dst_pixels_c, 1, sizeof(dst_pixels_c));
  memset(dst_pixels_opt, 2, sizeof(dst_pixels_opt));
491 492 493 494

  for (int i = 0; i < 2560 * 2; ++i) {
    orig_pixels[i] = i;
  }
495
  ScaleRowDown2Box_16_C(&orig_pixels[0], 2560, &dst_pixels_c[0], 1280);
496
  for (int i = 0; i < benchmark_pixels_div1280_; ++i) {
497 498 499
#if !defined(LIBYUV_DISABLE_NEON) && defined(__aarch64__)
    int has_neon = TestCpuFlag(kCpuHasNEON);
    if (has_neon) {
500
      ScaleRowDown2Box_16_NEON(&orig_pixels[0], 2560, &dst_pixels_opt[0], 1280);
501
    } else {
502
      ScaleRowDown2Box_16_C(&orig_pixels[0], 2560, &dst_pixels_opt[0], 1280);
503 504
    }
#else
505
    ScaleRowDown2Box_16_C(&orig_pixels[0], 2560, &dst_pixels_opt[0], 1280);
506
#endif
507 508 509 510 511
  }

  for (int i = 0; i < 1280; ++i) {
    EXPECT_EQ(dst_pixels_c[i], dst_pixels_opt[i]);
  }
512 513

  EXPECT_EQ(dst_pixels_c[0], (0 + 1 + 2560 + 2561 + 2) / 4);
514 515 516
  EXPECT_EQ(dst_pixels_c[1279], 3839);
}

517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
// Test scaling plane with 8 bit C vs 16 bit C and return maximum pixel
// difference.
// 0 = exact.
static int TestPlaneFilter_16(int src_width,
                              int src_height,
                              int dst_width,
                              int dst_height,
                              FilterMode f,
                              int benchmark_iterations,
                              int disable_cpu_flags,
                              int benchmark_cpu_info) {
  if (!SizeValid(src_width, src_height, dst_width, dst_height)) {
    return 0;
  }

  int i;
Frank Barchard's avatar
Frank Barchard committed
533
  int64_t src_y_plane_size = (Abs(src_width)) * (Abs(src_height));
534 535 536 537 538 539 540 541
  int src_stride_y = Abs(src_width);
  int dst_y_plane_size = dst_width * dst_height;
  int dst_stride_y = dst_width;

  align_buffer_page_end(src_y, src_y_plane_size);
  align_buffer_page_end(src_y_16, src_y_plane_size * 2);
  align_buffer_page_end(dst_y_8, dst_y_plane_size);
  align_buffer_page_end(dst_y_16, dst_y_plane_size * 2);
Frank Barchard's avatar
Frank Barchard committed
542 543
  uint16_t* p_src_y_16 = reinterpret_cast<uint16_t*>(src_y_16);
  uint16_t* p_dst_y_16 = reinterpret_cast<uint16_t*>(dst_y_16);
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613

  MemRandomize(src_y, src_y_plane_size);
  memset(dst_y_8, 0, dst_y_plane_size);
  memset(dst_y_16, 1, dst_y_plane_size * 2);

  for (i = 0; i < src_y_plane_size; ++i) {
    p_src_y_16[i] = src_y[i] & 255;
  }

  MaskCpuFlags(disable_cpu_flags);  // Disable all CPU optimization.
  ScalePlane(src_y, src_stride_y, src_width, src_height, dst_y_8, dst_stride_y,
             dst_width, dst_height, f);
  MaskCpuFlags(benchmark_cpu_info);  // Enable all CPU optimization.

  for (i = 0; i < benchmark_iterations; ++i) {
    ScalePlane_16(p_src_y_16, src_stride_y, src_width, src_height, p_dst_y_16,
                  dst_stride_y, dst_width, dst_height, f);
  }

  // Expect an exact match.
  int max_diff = 0;
  for (i = 0; i < dst_y_plane_size; ++i) {
    int abs_diff = Abs(dst_y_8[i] - p_dst_y_16[i]);
    if (abs_diff > max_diff) {
      max_diff = abs_diff;
    }
  }

  free_aligned_buffer_page_end(dst_y_8);
  free_aligned_buffer_page_end(dst_y_16);
  free_aligned_buffer_page_end(src_y);
  free_aligned_buffer_page_end(src_y_16);

  return max_diff;
}

// The following adjustments in dimensions ensure the scale factor will be
// exactly achieved.
// 2 is chroma subsample.
#define DX(x, nom, denom) static_cast<int>(((Abs(x) / nom + 1) / 2) * nom * 2)
#define SX(x, nom, denom) static_cast<int>(((x / nom + 1) / 2) * denom * 2)

#define TEST_FACTOR1(name, filter, nom, denom, max_diff)                     \
  TEST_F(LibYUVScaleTest, ScalePlaneDownBy##name##_##filter##_16) {          \
    int diff = TestPlaneFilter_16(                                           \
        SX(benchmark_width_, nom, denom), SX(benchmark_height_, nom, denom), \
        DX(benchmark_width_, nom, denom), DX(benchmark_height_, nom, denom), \
        kFilter##filter, benchmark_iterations_, disable_cpu_flags_,          \
        benchmark_cpu_info_);                                                \
    EXPECT_LE(diff, max_diff);                                               \
  }

// Test a scale factor with all 4 filters.  Expect unfiltered to be exact, but
// filtering is different fixed point implementations for SSSE3, Neon and C.
#define TEST_FACTOR(name, nom, denom, boxdiff)      \
  TEST_FACTOR1(name, None, nom, denom, 0)           \
  TEST_FACTOR1(name, Linear, nom, denom, boxdiff)   \
  TEST_FACTOR1(name, Bilinear, nom, denom, boxdiff) \
  TEST_FACTOR1(name, Box, nom, denom, boxdiff)

TEST_FACTOR(2, 1, 2, 0)
TEST_FACTOR(4, 1, 4, 0)
TEST_FACTOR(8, 1, 8, 0)
TEST_FACTOR(3by4, 3, 4, 1)
TEST_FACTOR(3by8, 3, 8, 1)
TEST_FACTOR(3, 1, 3, 0)
#undef TEST_FACTOR1
#undef TEST_FACTOR
#undef SX
#undef DX
frkoenig@google.com's avatar
frkoenig@google.com committed
614
}  // namespace libyuv