Home | History | Annotate | Download | only in unit_test
      1 /*
      2  *  Copyright 2011 The LibYuv 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 <time.h>
     13 
     14 #include "libyuv/convert_argb.h"
     15 #include "libyuv/cpu_id.h"
     16 #include "libyuv/scale_argb.h"
     17 #include "libyuv/video_common.h"
     18 #include "../unit_test/unit_test.h"
     19 
     20 namespace libyuv {
     21 
     22 #define STRINGIZE(line) #line
     23 #define FILELINESTR(file, line) file ":" STRINGIZE(line)
     24 
     25 // Test scaling with C vs Opt and return maximum pixel difference. 0 = exact.
     26 static int ARGBTestFilter(int src_width, int src_height,
     27                           int dst_width, int dst_height,
     28                           FilterMode f, int benchmark_iterations,
     29                           int disable_cpu_flags, int benchmark_cpu_info) {
     30   if (!SizeValid(src_width, src_height, dst_width, dst_height)) {
     31     return 0;
     32   }
     33 
     34   int i, j;
     35   const int b = 0;  // 128 to test for padding/stride.
     36   int64 src_argb_plane_size = (Abs(src_width) + b * 2) *
     37       (Abs(src_height) + b * 2) * 4LL;
     38   int src_stride_argb = (b * 2 + Abs(src_width)) * 4;
     39 
     40   align_buffer_page_end(src_argb, src_argb_plane_size);
     41   if (!src_argb) {
     42     printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
     43     return 0;
     44   }
     45   MemRandomize(src_argb, src_argb_plane_size);
     46 
     47   int64 dst_argb_plane_size = (dst_width + b * 2) * (dst_height + b * 2) * 4LL;
     48   int dst_stride_argb = (b * 2 + dst_width) * 4;
     49 
     50   align_buffer_page_end(dst_argb_c, dst_argb_plane_size);
     51   align_buffer_page_end(dst_argb_opt, dst_argb_plane_size);
     52   if (!dst_argb_c || !dst_argb_opt) {
     53     printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
     54     return 0;
     55   }
     56   memset(dst_argb_c, 2, dst_argb_plane_size);
     57   memset(dst_argb_opt, 3, dst_argb_plane_size);
     58 
     59   // Warm up both versions for consistent benchmarks.
     60   MaskCpuFlags(disable_cpu_flags);  // Disable all CPU optimization.
     61   ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
     62             src_width, src_height,
     63             dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb,
     64             dst_width, dst_height, f);
     65   MaskCpuFlags(benchmark_cpu_info);  // Enable all CPU optimization.
     66   ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
     67             src_width, src_height,
     68             dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb,
     69             dst_width, dst_height, f);
     70 
     71   MaskCpuFlags(disable_cpu_flags);  // Disable all CPU optimization.
     72   double c_time = get_time();
     73   ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
     74             src_width, src_height,
     75             dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb,
     76             dst_width, dst_height, f);
     77 
     78   c_time = (get_time() - c_time);
     79 
     80   MaskCpuFlags(benchmark_cpu_info);  // Enable all CPU optimization.
     81   double opt_time = get_time();
     82   for (i = 0; i < benchmark_iterations; ++i) {
     83     ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
     84               src_width, src_height,
     85               dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb,
     86               dst_width, dst_height, f);
     87   }
     88   opt_time = (get_time() - opt_time) / benchmark_iterations;
     89 
     90   // Report performance of C vs OPT
     91   printf("filter %d - %8d us C - %8d us OPT\n",
     92          f, static_cast<int>(c_time * 1e6), static_cast<int>(opt_time * 1e6));
     93 
     94   // C version may be a little off from the optimized. Order of
     95   //  operations may introduce rounding somewhere. So do a difference
     96   //  of the buffers and look to see that the max difference isn't
     97   //  over 2.
     98   int max_diff = 0;
     99   for (i = b; i < (dst_height + b); ++i) {
    100     for (j = b * 4; j < (dst_width + b) * 4; ++j) {
    101       int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] -
    102                          dst_argb_opt[(i * dst_stride_argb) + j]);
    103       if (abs_diff > max_diff) {
    104         max_diff = abs_diff;
    105       }
    106     }
    107   }
    108 
    109   free_aligned_buffer_page_end(dst_argb_c);
    110   free_aligned_buffer_page_end(dst_argb_opt);
    111   free_aligned_buffer_page_end(src_argb);
    112   return max_diff;
    113 }
    114 
    115 static const int kTileX = 8;
    116 static const int kTileY = 8;
    117 
    118 static int TileARGBScale(const uint8* src_argb, int src_stride_argb,
    119                          int src_width, int src_height,
    120                          uint8* dst_argb, int dst_stride_argb,
    121                          int dst_width, int dst_height,
    122                          FilterMode filtering) {
    123   for (int y = 0; y < dst_height; y += kTileY) {
    124     for (int x = 0; x < dst_width; x += kTileX) {
    125       int clip_width = kTileX;
    126       if (x + clip_width > dst_width) {
    127         clip_width = dst_width - x;
    128       }
    129       int clip_height = kTileY;
    130       if (y + clip_height > dst_height) {
    131         clip_height = dst_height - y;
    132       }
    133       int r = ARGBScaleClip(src_argb, src_stride_argb,
    134                             src_width, src_height,
    135                             dst_argb, dst_stride_argb,
    136                             dst_width, dst_height,
    137                             x, y, clip_width, clip_height, filtering);
    138       if (r) {
    139         return r;
    140       }
    141     }
    142   }
    143   return 0;
    144 }
    145 
    146 static int ARGBClipTestFilter(int src_width, int src_height,
    147                               int dst_width, int dst_height,
    148                               FilterMode f, int benchmark_iterations) {
    149   if (!SizeValid(src_width, src_height, dst_width, dst_height)) {
    150     return 0;
    151   }
    152 
    153   const int b = 128;
    154   int64 src_argb_plane_size = (Abs(src_width) + b * 2) *
    155       (Abs(src_height) + b * 2) * 4;
    156   int src_stride_argb = (b * 2 + Abs(src_width)) * 4;
    157 
    158   align_buffer_page_end(src_argb, src_argb_plane_size);
    159   if (!src_argb) {
    160     printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    161     return 0;
    162   }
    163   memset(src_argb, 1, src_argb_plane_size);
    164 
    165   int64 dst_argb_plane_size = (dst_width + b * 2) * (dst_height + b * 2) * 4;
    166   int dst_stride_argb = (b * 2 + dst_width) * 4;
    167 
    168   int i, j;
    169   for (i = b; i < (Abs(src_height) + b); ++i) {
    170     for (j = b; j < (Abs(src_width) + b) * 4; ++j) {
    171       src_argb[(i * src_stride_argb) + j] = (fastrand() & 0xff);
    172     }
    173   }
    174 
    175   align_buffer_page_end(dst_argb_c, dst_argb_plane_size);
    176   align_buffer_page_end(dst_argb_opt, dst_argb_plane_size);
    177   if (!dst_argb_c || !dst_argb_opt) {
    178     printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    179     return 0;
    180   }
    181   memset(dst_argb_c, 2, dst_argb_plane_size);
    182   memset(dst_argb_opt, 3, dst_argb_plane_size);
    183 
    184   // Do full image, no clipping.
    185   double c_time = get_time();
    186   ARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
    187             src_width, src_height,
    188             dst_argb_c + (dst_stride_argb * b) + b * 4, dst_stride_argb,
    189             dst_width, dst_height, f);
    190   c_time = (get_time() - c_time);
    191 
    192   // Do tiled image, clipping scale to a tile at a time.
    193   double opt_time = get_time();
    194   for (i = 0; i < benchmark_iterations; ++i) {
    195     TileARGBScale(src_argb + (src_stride_argb * b) + b * 4, src_stride_argb,
    196                   src_width, src_height,
    197                   dst_argb_opt + (dst_stride_argb * b) + b * 4, dst_stride_argb,
    198                   dst_width, dst_height, f);
    199   }
    200   opt_time = (get_time() - opt_time) / benchmark_iterations;
    201 
    202   // Report performance of Full vs Tiled.
    203   printf("filter %d - %8d us Full - %8d us Tiled\n",
    204          f, static_cast<int>(c_time * 1e6), static_cast<int>(opt_time * 1e6));
    205 
    206   // Compare full scaled image vs tiled image.
    207   int max_diff = 0;
    208   for (i = b; i < (dst_height + b); ++i) {
    209     for (j = b * 4; j < (dst_width + b) * 4; ++j) {
    210       int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] -
    211                          dst_argb_opt[(i * dst_stride_argb) + j]);
    212       if (abs_diff > max_diff) {
    213         max_diff = abs_diff;
    214       }
    215     }
    216   }
    217 
    218   free_aligned_buffer_page_end(dst_argb_c);
    219   free_aligned_buffer_page_end(dst_argb_opt);
    220   free_aligned_buffer_page_end(src_argb);
    221   return max_diff;
    222 }
    223 
    224 // The following adjustments in dimensions ensure the scale factor will be
    225 // exactly achieved.
    226 #define DX(x, nom, denom) static_cast<int>((Abs(x) / nom) * nom)
    227 #define SX(x, nom, denom) static_cast<int>((x / nom) * denom)
    228 
    229 #define TEST_FACTOR1(name, filter, nom, denom, max_diff)                       \
    230     TEST_F(LibYUVScaleTest, ARGBScaleDownBy##name##_##filter) {                \
    231       int diff = ARGBTestFilter(SX(benchmark_width_, nom, denom),              \
    232                                 SX(benchmark_height_, nom, denom),             \
    233                                 DX(benchmark_width_, nom, denom),              \
    234                                 DX(benchmark_height_, nom, denom),             \
    235                                 kFilter##filter, benchmark_iterations_,        \
    236                                 disable_cpu_flags_, benchmark_cpu_info_);      \
    237       EXPECT_LE(diff, max_diff);                                               \
    238     }                                                                          \
    239     TEST_F(LibYUVScaleTest, ARGBScaleDownClipBy##name##_##filter) {            \
    240       int diff = ARGBClipTestFilter(SX(benchmark_width_, nom, denom),          \
    241                                     SX(benchmark_height_, nom, denom),         \
    242                                     DX(benchmark_width_, nom, denom),          \
    243                                     DX(benchmark_height_, nom, denom),         \
    244                                     kFilter##filter, benchmark_iterations_);   \
    245       EXPECT_LE(diff, max_diff);                                               \
    246     }
    247 
    248 // Test a scale factor with all 4 filters.  Expect unfiltered to be exact, but
    249 // filtering is different fixed point implementations for SSSE3, Neon and C.
    250 #define TEST_FACTOR(name, nom, denom)                                          \
    251     TEST_FACTOR1(name, None, nom, denom, 0)                                    \
    252     TEST_FACTOR1(name, Linear, nom, denom, 3)                                  \
    253     TEST_FACTOR1(name, Bilinear, nom, denom, 3)                                \
    254     TEST_FACTOR1(name, Box, nom, denom, 3)
    255 
    256 TEST_FACTOR(2, 1, 2)
    257 TEST_FACTOR(4, 1, 4)
    258 TEST_FACTOR(8, 1, 8)
    259 TEST_FACTOR(3by4, 3, 4)
    260 TEST_FACTOR(3by8, 3, 8)
    261 TEST_FACTOR(3, 1, 3)
    262 #undef TEST_FACTOR1
    263 #undef TEST_FACTOR
    264 #undef SX
    265 #undef DX
    266 
    267 #define TEST_SCALETO1(name, width, height, filter, max_diff)                   \
    268     TEST_F(LibYUVScaleTest, name##To##width##x##height##_##filter) {           \
    269       int diff = ARGBTestFilter(benchmark_width_, benchmark_height_,           \
    270                                 width, height,                                 \
    271                                 kFilter##filter, benchmark_iterations_,        \
    272                                 disable_cpu_flags_, benchmark_cpu_info_);      \
    273       EXPECT_LE(diff, max_diff);                                               \
    274     }                                                                          \
    275     TEST_F(LibYUVScaleTest, name##From##width##x##height##_##filter) {         \
    276       int diff = ARGBTestFilter(width, height,                                 \
    277                                 Abs(benchmark_width_), Abs(benchmark_height_), \
    278                                 kFilter##filter, benchmark_iterations_,        \
    279                                 disable_cpu_flags_, benchmark_cpu_info_);      \
    280       EXPECT_LE(diff, max_diff);                                               \
    281     }                                                                          \
    282     TEST_F(LibYUVScaleTest, name##ClipTo##width##x##height##_##filter) {       \
    283       int diff = ARGBClipTestFilter(benchmark_width_, benchmark_height_,       \
    284                                     width, height,                             \
    285                                     kFilter##filter, benchmark_iterations_);   \
    286       EXPECT_LE(diff, max_diff);                                               \
    287     }                                                                          \
    288     TEST_F(LibYUVScaleTest, name##ClipFrom##width##x##height##_##filter) {     \
    289       int diff = ARGBClipTestFilter(width, height,                             \
    290                                     Abs(benchmark_width_),                     \
    291                                     Abs(benchmark_height_),                    \
    292                                     kFilter##filter, benchmark_iterations_);   \
    293       EXPECT_LE(diff, max_diff);                                               \
    294     }
    295 
    296 /// Test scale to a specified size with all 4 filters.
    297 #define TEST_SCALETO(name, width, height)                                      \
    298     TEST_SCALETO1(name, width, height, None, 0)                                \
    299     TEST_SCALETO1(name, width, height, Linear, 3)                              \
    300     TEST_SCALETO1(name, width, height, Bilinear, 3)
    301 
    302 TEST_SCALETO(ARGBScale, 1, 1)
    303 TEST_SCALETO(ARGBScale, 320, 240)
    304 TEST_SCALETO(ARGBScale, 352, 288)
    305 TEST_SCALETO(ARGBScale, 569, 480)
    306 TEST_SCALETO(ARGBScale, 640, 360)
    307 TEST_SCALETO(ARGBScale, 1280, 720)
    308 #undef TEST_SCALETO1
    309 #undef TEST_SCALETO
    310 
    311 // Scale with YUV conversion to ARGB and clipping.
    312 LIBYUV_API
    313 int YUVToARGBScaleReference2(const uint8* src_y, int src_stride_y,
    314                              const uint8* src_u, int src_stride_u,
    315                              const uint8* src_v, int src_stride_v,
    316                              uint32 src_fourcc,
    317                              int src_width, int src_height,
    318                              uint8* dst_argb, int dst_stride_argb,
    319                              uint32 dst_fourcc,
    320                              int dst_width, int dst_height,
    321                              int clip_x, int clip_y,
    322                              int clip_width, int clip_height,
    323                              enum FilterMode filtering) {
    324   uint8* argb_buffer = static_cast<uint8*>(malloc(src_width * src_height * 4));
    325   int r;
    326   I420ToARGB(src_y, src_stride_y,
    327              src_u, src_stride_u,
    328              src_v, src_stride_v,
    329              argb_buffer, src_width * 4,
    330              src_width, src_height);
    331 
    332   r = ARGBScaleClip(argb_buffer, src_width * 4,
    333                     src_width, src_height,
    334                     dst_argb, dst_stride_argb,
    335                     dst_width, dst_height,
    336                     clip_x, clip_y, clip_width, clip_height,
    337                     filtering);
    338   free(argb_buffer);
    339   return r;
    340 }
    341 
    342 static void FillRamp(uint8* buf, int width, int height, int v, int dx, int dy) {
    343   int rv = v;
    344   for (int y = 0; y < height; ++y) {
    345     for (int x = 0; x < width; ++x) {
    346       *buf++ = v;
    347       v += dx;
    348       if (v < 0 || v > 255) {
    349         dx = -dx;
    350         v += dx;
    351       }
    352     }
    353     v = rv + dy;
    354     if (v < 0 || v > 255) {
    355       dy = -dy;
    356       v += dy;
    357     }
    358     rv = v;
    359   }
    360 }
    361 
    362 // Test scaling with C vs Opt and return maximum pixel difference. 0 = exact.
    363 static int YUVToARGBTestFilter(int src_width, int src_height,
    364                                int dst_width, int dst_height,
    365                                FilterMode f, int benchmark_iterations,
    366                                int disable_cpu_flags, int benchmark_cpu_info) {
    367   int64 src_y_plane_size = Abs(src_width) * Abs(src_height);
    368   int64 src_uv_plane_size = ((Abs(src_width) + 1) / 2) *
    369       ((Abs(src_height) + 1) / 2);
    370   int src_stride_y = Abs(src_width);
    371   int src_stride_uv = (Abs(src_width) + 1) / 2;
    372 
    373   align_buffer_page_end(src_y, src_y_plane_size);
    374   align_buffer_page_end(src_u, src_uv_plane_size);
    375   align_buffer_page_end(src_v, src_uv_plane_size);
    376 
    377   int64 dst_argb_plane_size = (dst_width) * (dst_height) * 4LL;
    378   int dst_stride_argb = (dst_width) * 4;
    379   align_buffer_page_end(dst_argb_c, dst_argb_plane_size);
    380   align_buffer_page_end(dst_argb_opt, dst_argb_plane_size);
    381   if (!dst_argb_c || !dst_argb_opt || !src_y || !src_u || !src_v) {
    382     printf("Skipped.  Alloc failed " FILELINESTR(__FILE__, __LINE__) "\n");
    383     return 0;
    384   }
    385   // Fill YUV image with continuous ramp, which is less sensitive to
    386   // subsampling and filtering differences for test purposes.
    387   FillRamp(src_y, Abs(src_width), Abs(src_height), 128, 1, 1);
    388   FillRamp(src_u, (Abs(src_width) + 1) / 2, (Abs(src_height) + 1) / 2, 3, 1, 1);
    389   FillRamp(src_v, (Abs(src_width) + 1) / 2, (Abs(src_height) + 1) / 2, 4, 1, 1);
    390   memset(dst_argb_c, 2, dst_argb_plane_size);
    391   memset(dst_argb_opt, 3, dst_argb_plane_size);
    392 
    393   YUVToARGBScaleReference2(src_y, src_stride_y,
    394                            src_u, src_stride_uv,
    395                            src_v, src_stride_uv,
    396                            libyuv::FOURCC_I420,
    397                            src_width, src_height,
    398                            dst_argb_c, dst_stride_argb,
    399                            libyuv::FOURCC_I420,
    400                            dst_width, dst_height,
    401                            0, 0, dst_width, dst_height,
    402                            f);
    403 
    404   for (int i = 0; i < benchmark_iterations; ++i) {
    405     YUVToARGBScaleClip(src_y, src_stride_y,
    406                        src_u, src_stride_uv,
    407                        src_v, src_stride_uv,
    408                        libyuv::FOURCC_I420,
    409                        src_width, src_height,
    410                        dst_argb_opt, dst_stride_argb,
    411                        libyuv::FOURCC_I420,
    412                        dst_width, dst_height,
    413                        0, 0, dst_width, dst_height,
    414                        f);
    415   }
    416   int max_diff = 0;
    417   for (int i = 0; i < dst_height; ++i) {
    418     for (int j = 0; j < dst_width * 4; ++j) {
    419       int abs_diff = Abs(dst_argb_c[(i * dst_stride_argb) + j] -
    420                          dst_argb_opt[(i * dst_stride_argb) + j]);
    421       if (abs_diff > max_diff) {
    422         printf("error %d at %d,%d c %d opt %d",
    423                abs_diff,
    424                j, i,
    425                dst_argb_c[(i * dst_stride_argb) + j],
    426                dst_argb_opt[(i * dst_stride_argb) + j]);
    427         EXPECT_LE(abs_diff, 40);
    428         max_diff = abs_diff;
    429       }
    430     }
    431   }
    432 
    433   free_aligned_buffer_page_end(dst_argb_c);
    434   free_aligned_buffer_page_end(dst_argb_opt);
    435   free_aligned_buffer_page_end(src_y);
    436   free_aligned_buffer_page_end(src_u);
    437   free_aligned_buffer_page_end(src_v);
    438   return max_diff;
    439 }
    440 
    441 TEST_F(LibYUVScaleTest, YUVToRGBScaleUp) {
    442   int diff = YUVToARGBTestFilter(benchmark_width_, benchmark_height_,
    443                                  benchmark_width_ * 3 / 2,
    444                                  benchmark_height_ * 3 / 2,
    445                                  libyuv::kFilterBilinear,
    446                                  benchmark_iterations_,
    447                                  disable_cpu_flags_, benchmark_cpu_info_);
    448   EXPECT_LE(diff, 10);
    449 }
    450 
    451 TEST_F(LibYUVScaleTest, YUVToRGBScaleDown) {
    452   int diff = YUVToARGBTestFilter(benchmark_width_ * 3 / 2,
    453                                  benchmark_height_ * 3 / 2,
    454                                  benchmark_width_, benchmark_height_,
    455                                  libyuv::kFilterBilinear,
    456                                  benchmark_iterations_,
    457                                  disable_cpu_flags_, benchmark_cpu_info_);
    458   EXPECT_LE(diff, 10);
    459 }
    460 
    461 
    462 }  // namespace libyuv
    463