scale_test.cc 14 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>

frkoenig@google.com's avatar
frkoenig@google.com committed
14 15
#include "libyuv/cpu_id.h"
#include "libyuv/scale.h"
16
#include "../unit_test/unit_test.h"
frkoenig@google.com's avatar
frkoenig@google.com committed
17

mikhal@webrtc.org's avatar
mikhal@webrtc.org committed
18
namespace libyuv {
19

20
// Test scaling with C vs Opt and return maximum pixel difference. 0 = exact.
21 22
static int TestFilter(int src_width, int src_height,
                      int dst_width, int dst_height,
23
                      FilterMode f, int benchmark_iterations) {
24
  int i, j;
25
  const int b = 0;  // 128 to test for padding/stride.
26 27
  int src_width_uv = (Abs(src_width) + 1) >> 1;
  int src_height_uv = (Abs(src_height) + 1) >> 1;
28

29
  int src_y_plane_size = (Abs(src_width) + b * 2) * (Abs(src_height) + b * 2);
30
  int src_uv_plane_size = (src_width_uv + b * 2) * (src_height_uv + b * 2);
31

fbarchard@google.com's avatar
fbarchard@google.com committed
32
  int src_stride_y = b * 2 + Abs(src_width);
33
  int src_stride_uv = b * 2 + src_width_uv;
34

35 36 37
  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)
38 39 40 41
  srandom(time(NULL));
  MemRandomize(src_y, src_y_plane_size);
  MemRandomize(src_u, src_uv_plane_size);
  MemRandomize(src_v, src_uv_plane_size);
42

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

46 47
  int dst_y_plane_size = (dst_width + b * 2) * (dst_height + b * 2);
  int dst_uv_plane_size = (dst_width_uv + b * 2) * (dst_height_uv + b * 2);
48

49 50
  int dst_stride_y = b * 2 + dst_width;
  int dst_stride_uv = b * 2 + dst_width_uv;
51

52 53 54 55 56 57
  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)
58

fbarchard@google.com's avatar
fbarchard@google.com committed
59 60

  MaskCpuFlags(0);  // Disable all CPU optimization.
61
  double c_time = get_time();
62 63 64 65 66 67 68 69 70
  I420Scale(src_y + (src_stride_y * b) + b, src_stride_y,
            src_u + (src_stride_uv * b) + b, src_stride_uv,
            src_v + (src_stride_uv * b) + b, src_stride_uv,
            src_width, src_height,
            dst_y_c + (dst_stride_y * b) + b, dst_stride_y,
            dst_u_c + (dst_stride_uv * b) + b, dst_stride_uv,
            dst_v_c + (dst_stride_uv * b) + b, dst_stride_uv,
            dst_width, dst_height, f);
  c_time = (get_time() - c_time);
71

fbarchard@google.com's avatar
fbarchard@google.com committed
72
  MaskCpuFlags(-1);  // Enable all CPU optimization.
73
  double opt_time = get_time();
74
  for (i = 0; i < benchmark_iterations; ++i) {
75 76 77 78 79 80 81
    I420Scale(src_y + (src_stride_y * b) + b, src_stride_y,
              src_u + (src_stride_uv * b) + b, src_stride_uv,
              src_v + (src_stride_uv * b) + b, src_stride_uv,
              src_width, src_height,
              dst_y_opt + (dst_stride_y * b) + b, dst_stride_y,
              dst_u_opt + (dst_stride_uv * b) + b, dst_stride_uv,
              dst_v_opt + (dst_stride_uv * b) + b, dst_stride_uv,
82
              dst_width, dst_height, f);
fbarchard@google.com's avatar
fbarchard@google.com committed
83
  }
84
  opt_time = (get_time() - opt_time) / benchmark_iterations;
fbarchard@google.com's avatar
fbarchard@google.com committed
85 86
  // Report performance of C vs OPT
  printf("filter %d - %8d us C - %8d us OPT\n",
87 88 89
         f,
         static_cast<int>(c_time * 1e6),
         static_cast<int>(opt_time * 1e6));
90

91 92
  // C version may be a little off from the optimized. Order of
  //  operations may introduce rounding somewhere. So do a difference
93 94 95 96 97
  //  of the buffers and look to see that the max difference isn't
  //  over 2.
  int max_diff = 0;
  for (i = b; i < (dst_height + b); ++i) {
    for (j = b; j < (dst_width + b); ++j) {
98
      int abs_diff = Abs(dst_y_c[(i * dst_stride_y) + j] -
99
                         dst_y_opt[(i * dst_stride_y) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
100
      if (abs_diff > max_diff) {
101
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
102
      }
103
    }
104
  }
105

106 107
  for (i = b; i < (dst_height_uv + b); ++i) {
    for (j = b; j < (dst_width_uv + b); ++j) {
108
      int abs_diff = Abs(dst_u_c[(i * dst_stride_uv) + j] -
109
                         dst_u_opt[(i * dst_stride_uv) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
110
      if (abs_diff > max_diff) {
111
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
112
      }
113
      abs_diff = Abs(dst_v_c[(i * dst_stride_uv) + j] -
114
                     dst_v_opt[(i * dst_stride_uv) + j]);
fbarchard@google.com's avatar
fbarchard@google.com committed
115
      if (abs_diff > max_diff) {
116
        max_diff = abs_diff;
fbarchard@google.com's avatar
fbarchard@google.com committed
117
      }
118
    }
119 120
  }

121 122 123 124 125 126
  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)
127

128 129 130
  free_aligned_buffer_page_end(src_y)
  free_aligned_buffer_page_end(src_u)
  free_aligned_buffer_page_end(src_v)
131

fbarchard@google.com's avatar
fbarchard@google.com committed
132
  return max_diff;
133 134
}

135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
// Test scaling with 8 bit C vs 16 bit C and return maximum pixel difference.
// 0 = exact.
static int TestFilter_16(int src_width, int src_height,
                         int dst_width, int dst_height,
                         FilterMode f, int benchmark_iterations) {
  int i, j;
  const int b = 0;  // 128 to test for padding/stride.
  int src_width_uv = (Abs(src_width) + 1) >> 1;
  int src_height_uv = (Abs(src_height) + 1) >> 1;

  int src_y_plane_size = (Abs(src_width) + b * 2) * (Abs(src_height) + b * 2);
  int src_uv_plane_size = (src_width_uv + b * 2) * (src_height_uv + b * 2);

  int src_stride_y = b * 2 + Abs(src_width);
  int src_stride_uv = b * 2 + src_width_uv;

  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)
  uint16* p_src_y_16 = reinterpret_cast<uint16*>(src_y_16);
  uint16* p_src_u_16 = reinterpret_cast<uint16*>(src_u_16);
  uint16* p_src_v_16 = reinterpret_cast<uint16*>(src_v_16);

  srandom(time(NULL));
  MemRandomize(src_y, src_y_plane_size);
  MemRandomize(src_u, src_uv_plane_size);
  MemRandomize(src_v, src_uv_plane_size);

  for (i = b; i < src_height + b; ++i) {
    for (j = b; j < src_width + b; ++j) {
      p_src_y_16[(i * src_stride_y) + j] = src_y[(i * src_stride_y) + j];
    }
  }

  for (i = b; i < (src_height_uv + b); ++i) {
    for (j = b; j < (src_width_uv + b); ++j) {
      p_src_u_16[(i * src_stride_uv) + j] = src_u[(i * src_stride_uv) + j];
      p_src_v_16[(i * src_stride_uv) + j] = src_v[(i * src_stride_uv) + j];
    }
  }

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

  int dst_y_plane_size = (dst_width + b * 2) * (dst_height + b * 2);
  int dst_uv_plane_size = (dst_width_uv + b * 2) * (dst_height_uv + b * 2);

  int dst_stride_y = b * 2 + dst_width;
  int dst_stride_uv = b * 2 + dst_width_uv;

  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)

  uint16* p_dst_y_16 = reinterpret_cast<uint16*>(dst_y_16);
  uint16* p_dst_u_16 = reinterpret_cast<uint16*>(dst_u_16);
  uint16* p_dst_v_16 = reinterpret_cast<uint16*>(dst_v_16);

  I420Scale(src_y + (src_stride_y * b) + b, src_stride_y,
            src_u + (src_stride_uv * b) + b, src_stride_uv,
            src_v + (src_stride_uv * b) + b, src_stride_uv,
            src_width, src_height,
            dst_y_8 + (dst_stride_y * b) + b, dst_stride_y,
            dst_u_8 + (dst_stride_uv * b) + b, dst_stride_uv,
            dst_v_8 + (dst_stride_uv * b) + b, dst_stride_uv,
            dst_width, dst_height, f);

  for (i = 0; i < benchmark_iterations; ++i) {
    I420Scale_16(p_src_y_16 + (src_stride_y * b) + b, src_stride_y,
                 p_src_u_16 + (src_stride_uv * b) + b, src_stride_uv,
                 p_src_v_16 + (src_stride_uv * b) + b, src_stride_uv,
                 src_width, src_height,
                 p_dst_y_16 + (dst_stride_y * b) + b, dst_stride_y,
                 p_dst_u_16 + (dst_stride_uv * b) + b, dst_stride_uv,
                 p_dst_v_16 + (dst_stride_uv * b) + b, dst_stride_uv,
                 dst_width, dst_height, f);
  }

  // Expect an exact match
  int max_diff = 0;
  for (i = b; i < (dst_height + b); ++i) {
    for (j = b; j < (dst_width + b); ++j) {
      int abs_diff = Abs(dst_y_8[(i * dst_stride_y) + j] -
                         p_dst_y_16[(i * dst_stride_y) + j]);
      if (abs_diff > max_diff) {
        max_diff = abs_diff;
      }
    }
  }

  for (i = b; i < (dst_height_uv + b); ++i) {
    for (j = b; j < (dst_width_uv + b); ++j) {
      int abs_diff = Abs(dst_u_8[(i * dst_stride_uv) + j] -
                         p_dst_u_16[(i * dst_stride_uv) + j]);
      if (abs_diff > max_diff) {
        max_diff = abs_diff;
      }
      abs_diff = Abs(dst_v_8[(i * dst_stride_uv) + j] -
                     p_dst_v_16[(i * dst_stride_uv) + j]);
      if (abs_diff > max_diff) {
        max_diff = abs_diff;
      }
    }
  }

  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;
}

263
#define TEST_FACTOR1(name, filter, hfactor, vfactor, max_diff)                 \
264 265
    TEST_F(libyuvTest, ScaleDownBy##name##_##filter) {                         \
      int diff = TestFilter(benchmark_width_, benchmark_height_,               \
266 267
                            Abs(benchmark_width_) * hfactor,                   \
                            Abs(benchmark_height_) * vfactor,                  \
268 269
                            kFilter##filter, benchmark_iterations_);           \
      EXPECT_LE(diff, max_diff);                                               \
270 271 272 273 274 275 276
    }                                                                          \
    TEST_F(libyuvTest, ScaleDownBy##name##_##filter##_16) {                    \
      int diff = TestFilter_16(benchmark_width_, benchmark_height_,            \
                               Abs(benchmark_width_) * hfactor,                \
                               Abs(benchmark_height_) * vfactor,               \
                               kFilter##filter, benchmark_iterations_);        \
      EXPECT_LE(diff, max_diff);                                               \
277
    }
278

279
// Test a scale factor with all 4 filters.  Expect unfiltered to be exact, but
280
// filtering is different fixed point implementations for SSSE3, Neon and C.
281 282
#define TEST_FACTOR(name, hfactor, vfactor)                                    \
    TEST_FACTOR1(name, None, hfactor, vfactor, 0)                              \
283 284 285
    TEST_FACTOR1(name, Linear, hfactor, vfactor, 3)                            \
    TEST_FACTOR1(name, Bilinear, hfactor, vfactor, 3)                          \
    TEST_FACTOR1(name, Box, hfactor, vfactor, 3)                               \
286

287
TEST_FACTOR(2, 1 / 2, 1 / 2)
fbarchard@google.com's avatar
fbarchard@google.com committed
288 289 290
TEST_FACTOR(4, 1 / 4, 1 / 4)
TEST_FACTOR(8, 1 / 8, 1 / 8)
TEST_FACTOR(3by4, 3 / 4, 3 / 4)
291
TEST_FACTOR(3by8, 3 / 8, 3 / 8)
292 293 294
#undef TEST_FACTOR1
#undef TEST_FACTOR

295
#define TEST_SCALETO1(name, width, height, filter, max_diff)                   \
296
    TEST_F(libyuvTest, name##To##width##x##height##_##filter) {                \
297 298 299 300 301
      int diff = TestFilter(benchmark_width_, benchmark_height_,               \
                            width, height,                                     \
                            kFilter##filter, benchmark_iterations_);           \
      EXPECT_LE(diff, max_diff);                                               \
    }                                                                          \
302
    TEST_F(libyuvTest, name##From##width##x##height##_##filter) {              \
303 304 305 306
      int diff = TestFilter(width, height,                                     \
                            Abs(benchmark_width_), Abs(benchmark_height_),     \
                            kFilter##filter, benchmark_iterations_);           \
      EXPECT_LE(diff, max_diff);                                               \
307 308 309 310 311 312 313 314 315 316 317 318
    }                                                                          \
    TEST_F(libyuvTest, name##To##width##x##height##_##filter##_16) {           \
      int diff = TestFilter_16(benchmark_width_, benchmark_height_,            \
                               width, height,                                  \
                               kFilter##filter, benchmark_iterations_);        \
      EXPECT_LE(diff, max_diff);                                               \
    }                                                                          \
    TEST_F(libyuvTest, name##From##width##x##height##_##filter##_16) {         \
      int diff = TestFilter_16(width, height,                                  \
                               Abs(benchmark_width_), Abs(benchmark_height_),  \
                               kFilter##filter, benchmark_iterations_);        \
      EXPECT_LE(diff, max_diff);                                               \
319
    }
320

321
// Test scale to a specified size with all 4 filters.
322 323
#define TEST_SCALETO(name, width, height)                                      \
    TEST_SCALETO1(name, width, height, None, 0)                                \
324 325 326
    TEST_SCALETO1(name, width, height, Linear, 3)                              \
    TEST_SCALETO1(name, width, height, Bilinear, 3)                            \
    TEST_SCALETO1(name, width, height, Box, 3)
327

328
TEST_SCALETO(Scale, 1, 1)
329
TEST_SCALETO(Scale, 320, 240)
330
TEST_SCALETO(Scale, 352, 288)
331
TEST_SCALETO(Scale, 569, 480)
fbarchard@google.com's avatar
fbarchard@google.com committed
332
TEST_SCALETO(Scale, 640, 360)
333
TEST_SCALETO(Scale, 1280, 720)
334 335
#undef TEST_SCALETO1
#undef TEST_SCALETO
336

frkoenig@google.com's avatar
frkoenig@google.com committed
337
}  // namespace libyuv