Home | History | Annotate | Download | only in tools
      1 /*
      2  * Copyright 2012 Google Inc.
      3  *
      4  * Use of this source code is governed by a BSD-style license that can be
      5  * found in the LICENSE file.
      6  */
      7 
      8 #include "BenchTimer.h"
      9 #include "CopyTilesRenderer.h"
     10 #include "LazyDecodeBitmap.h"
     11 #include "PictureBenchmark.h"
     12 #include "PictureRenderingFlags.h"
     13 #include "SkBenchLogger.h"
     14 #include "SkCommandLineFlags.h"
     15 #include "SkGraphics.h"
     16 #include "SkImageDecoder.h"
     17 #if LAZY_CACHE_STATS
     18     #include "SkLazyPixelRef.h"
     19 #endif
     20 #include "SkLruImageCache.h"
     21 #include "SkMath.h"
     22 #include "SkOSFile.h"
     23 #include "SkPicture.h"
     24 #include "SkStream.h"
     25 #include "picture_utils.h"
     26 
     27 SkBenchLogger gLogger;
     28 
     29 // Flags used by this file, in alphabetical order.
     30 DEFINE_bool(countRAM, false, "Count the RAM used for bitmap pixels in each skp file");
     31 DECLARE_bool(deferImageDecoding);
     32 DEFINE_string(filter, "",
     33         "type:flag : Enable canvas filtering to disable a paint flag, "
     34         "use no blur or low quality blur, or use no hinting or "
     35         "slight hinting. For all flags except AAClip, specify the "
     36         "type of primitive to effect, or choose all. for AAClip "
     37         "alone, the filter affects all clips independent of type. "
     38         "Specific flags are listed above.");
     39 DEFINE_string(logFile, "", "Destination for writing log output, in addition to stdout.");
     40 DEFINE_bool(logPerIter, false, "Log each repeat timer instead of mean.");
     41 DEFINE_bool(min, false, "Print the minimum times (instead of average).");
     42 DECLARE_int32(multi);
     43 DECLARE_string(readPath);
     44 DEFINE_int32(repeat, 1, "Set the number of times to repeat each test.");
     45 DEFINE_bool(timeIndividualTiles, false, "Report times for drawing individual tiles, rather than "
     46             "times for drawing the whole page. Requires tiled rendering.");
     47 DEFINE_string(timers, "c", "[wcgWC]*: Display wall, cpu, gpu, truncated wall or truncated cpu time"
     48               " for each picture.");
     49 DEFINE_bool(trackDeferredCaching, false, "Only meaningful with --deferImageDecoding and "
     50             "LAZY_CACHE_STATS set to true. Report percentage of cache hits when using deferred "
     51             "image decoding.");
     52 
     53 static char const * const gFilterTypes[] = {
     54     "paint",
     55     "point",
     56     "line",
     57     "bitmap",
     58     "rect",
     59     "oval",
     60     "path",
     61     "text",
     62     "all",
     63 };
     64 
     65 static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
     66 
     67 static char const * const gFilterFlags[] = {
     68     "antiAlias",
     69     "filterBitmap",
     70     "dither",
     71     "underlineText",
     72     "strikeThruText",
     73     "fakeBoldText",
     74     "linearText",
     75     "subpixelText",
     76     "devKernText",
     77     "LCDRenderText",
     78     "embeddedBitmapText",
     79     "autoHinting",
     80     "verticalText",
     81     "genA8FromLCD",
     82     "blur",
     83     "hinting",
     84     "slightHinting",
     85     "AAClip",
     86 };
     87 
     88 static const size_t kFilterFlagsCount = sizeof(gFilterFlags) / sizeof(gFilterFlags[0]);
     89 
     90 static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
     91     int all = drawFilters[0];
     92     size_t tIndex;
     93     for (tIndex = 1; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
     94         all &= drawFilters[tIndex];
     95     }
     96     SkString result;
     97     for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
     98         SkString types;
     99         if (all & (1 << fIndex)) {
    100             types = gFilterTypes[SkDrawFilter::kTypeCount];
    101         } else {
    102             for (tIndex = 0; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
    103                 if (drawFilters[tIndex] & (1 << fIndex)) {
    104                     types += gFilterTypes[tIndex];
    105                 }
    106             }
    107         }
    108         if (!types.size()) {
    109             continue;
    110         }
    111         result += "_";
    112         result += types;
    113         result += ".";
    114         result += gFilterFlags[fIndex];
    115     }
    116     return result;
    117 }
    118 
    119 static SkString filterTypesUsage() {
    120     SkString result;
    121     for (size_t index = 0; index < kFilterTypesCount; ++index) {
    122         result += gFilterTypes[index];
    123         if (index < kFilterTypesCount - 1) {
    124             result += " | ";
    125         }
    126     }
    127     return result;
    128 }
    129 
    130 static SkString filterFlagsUsage() {
    131     SkString result;
    132     size_t len = 0;
    133     for (size_t index = 0; index < kFilterFlagsCount; ++index) {
    134         result += gFilterFlags[index];
    135         if (result.size() - len >= 72) {
    136             result += "\n\t\t";
    137             len = result.size();
    138         }
    139         if (index < kFilterFlagsCount - 1) {
    140             result += " | ";
    141         }
    142     }
    143     return result;
    144 }
    145 
    146 // Defined in LazyDecodeBitmap.cpp
    147 extern SkLruImageCache gLruImageCache;
    148 
    149 #if LAZY_CACHE_STATS
    150 static int32_t gTotalCacheHits;
    151 static int32_t gTotalCacheMisses;
    152 #endif
    153 
    154 static bool run_single_benchmark(const SkString& inputPath,
    155                                  sk_tools::PictureBenchmark& benchmark) {
    156     SkFILEStream inputStream;
    157 
    158     inputStream.setPath(inputPath.c_str());
    159     if (!inputStream.isValid()) {
    160         SkString err;
    161         err.printf("Could not open file %s\n", inputPath.c_str());
    162         gLogger.logError(err);
    163         return false;
    164     }
    165 
    166     // Since the old picture has been deleted, all pixels should be cleared.
    167     SkASSERT(gLruImageCache.getImageCacheUsed() == 0);
    168     if (FLAGS_countRAM) {
    169         // Set the limit to zero, so all pixels will be kept
    170       gLruImageCache.setImageCacheLimit(0);
    171     }
    172 
    173     SkPicture::InstallPixelRefProc proc;
    174     if (FLAGS_deferImageDecoding) {
    175         proc = &sk_tools::LazyDecodeBitmap;
    176     } else {
    177         proc = &SkImageDecoder::DecodeMemory;
    178     }
    179     SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
    180 
    181     if (NULL == picture.get()) {
    182         SkString err;
    183         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
    184         gLogger.logError(err);
    185         return false;
    186     }
    187 
    188     SkString filename;
    189     sk_tools::get_basename(&filename, inputPath);
    190 
    191     SkString result;
    192     result.printf("running bench [%i %i] %s ", picture->width(), picture->height(),
    193                   filename.c_str());
    194     gLogger.logProgress(result);
    195 
    196     benchmark.run(picture);
    197 
    198 #if LAZY_CACHE_STATS
    199     if (FLAGS_trackDeferredCaching) {
    200         int32_t cacheHits = SkLazyPixelRef::GetCacheHits();
    201         int32_t cacheMisses = SkLazyPixelRef::GetCacheMisses();
    202         SkLazyPixelRef::ResetCacheStats();
    203         SkString hitString;
    204         hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
    205         gLogger.logProgress(hitString);
    206         gTotalCacheHits += cacheHits;
    207         gTotalCacheMisses += cacheMisses;
    208     }
    209 #endif
    210     if (FLAGS_countRAM) {
    211         SkString ramCount("RAM used for bitmaps: ");
    212         size_t bytes = gLruImageCache.getImageCacheUsed();
    213         if (bytes > 1024) {
    214             size_t kb = bytes / 1024;
    215             if (kb > 1024) {
    216                 size_t mb = kb / 1024;
    217                 ramCount.appendf("%zi MB\n", mb);
    218             } else {
    219                 ramCount.appendf("%zi KB\n", kb);
    220             }
    221         } else {
    222             ramCount.appendf("%zi bytes\n", bytes);
    223         }
    224         gLogger.logProgress(ramCount);
    225     }
    226 
    227     return true;
    228 }
    229 
    230 static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
    231     sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
    232     sk_bzero(drawFilters, sizeof(drawFilters));
    233 
    234     if (FLAGS_filter.count() > 0) {
    235         const char* filters = FLAGS_filter[0];
    236         const char* colon = strchr(filters, ':');
    237         if (colon) {
    238             int32_t type = -1;
    239             size_t typeLen = colon - filters;
    240             for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
    241                 if (typeLen == strlen(gFilterTypes[tIndex])
    242                         && !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
    243                     type = SkToS32(tIndex);
    244                     break;
    245                 }
    246             }
    247             if (type < 0) {
    248                 SkString err;
    249                 err.printf("Unknown type for --filter %s\n", filters);
    250                 gLogger.logError(err);
    251                 exit(-1);
    252             }
    253             int flag = -1;
    254             size_t flagLen = strlen(filters) - typeLen - 1;
    255             for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
    256                 if (flagLen == strlen(gFilterFlags[fIndex])
    257                         && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
    258                     flag = 1 << fIndex;
    259                     break;
    260                 }
    261             }
    262             if (flag < 0) {
    263                 SkString err;
    264                 err.printf("Unknown flag for --filter %s\n", filters);
    265                 gLogger.logError(err);
    266                 exit(-1);
    267             }
    268             for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
    269                 if (type != SkDrawFilter::kTypeCount && index != type) {
    270                     continue;
    271                 }
    272                 drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
    273                         (drawFilters[index] | flag);
    274             }
    275         } else {
    276             SkString err;
    277             err.printf("Unknown arg for --filter %s : missing colon\n", filters);
    278             gLogger.logError(err);
    279             exit(-1);
    280         }
    281     }
    282 
    283     if (FLAGS_timers.count() > 0) {
    284         size_t index = 0;
    285         bool timerWall = false;
    286         bool truncatedTimerWall = false;
    287         bool timerCpu = false;
    288         bool truncatedTimerCpu = false;
    289         bool timerGpu = false;
    290         while (index < strlen(FLAGS_timers[0])) {
    291             switch (FLAGS_timers[0][index]) {
    292                 case 'w':
    293                     timerWall = true;
    294                     break;
    295                 case 'c':
    296                     timerCpu = true;
    297                     break;
    298                 case 'W':
    299                     truncatedTimerWall = true;
    300                     break;
    301                 case 'C':
    302                     truncatedTimerCpu = true;
    303                     break;
    304                 case 'g':
    305                     timerGpu = true;
    306                     break;
    307                 default:
    308                     SkDebugf("mystery character\n");
    309                     break;
    310             }
    311             index++;
    312         }
    313         benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
    314                                   timerGpu);
    315     }
    316 
    317     SkString errorString;
    318     SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
    319                                                                    kBench_PictureTool));
    320 
    321     if (errorString.size() > 0) {
    322         gLogger.logError(errorString);
    323     }
    324 
    325     if (NULL == renderer.get()) {
    326         exit(-1);
    327     }
    328 
    329     if (FLAGS_timeIndividualTiles) {
    330         if (FLAGS_multi > 1) {
    331             gLogger.logError("Cannot time individual tiles with more than one thread.\n");
    332             exit(-1);
    333         }
    334         sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
    335         if (NULL == tiledRenderer) {
    336             gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
    337             exit(-1);
    338         }
    339         if (!tiledRenderer->supportsTimingIndividualTiles()) {
    340             gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
    341             exit(-1);
    342         }
    343         benchmark->setTimeIndividualTiles(true);
    344     }
    345 
    346     if (FLAGS_readPath.count() < 1) {
    347         gLogger.logError(".skp files or directories are required.\n");
    348         exit(-1);
    349     }
    350 
    351     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
    352     if (FLAGS_logPerIter) {
    353         benchmark->setTimerResultType(TimerData::kPerIter_Result);
    354     } else if (FLAGS_min) {
    355         benchmark->setTimerResultType(TimerData::kMin_Result);
    356     } else {
    357         benchmark->setTimerResultType(TimerData::kAvg_Result);
    358     }
    359     benchmark->setRenderer(renderer);
    360     benchmark->setRepeats(FLAGS_repeat);
    361     benchmark->setLogger(&gLogger);
    362 }
    363 
    364 static int process_input(const char* input,
    365                          sk_tools::PictureBenchmark& benchmark) {
    366     SkString inputAsSkString(input);
    367     SkOSFile::Iter iter(input, "skp");
    368     SkString inputFilename;
    369     int failures = 0;
    370     if (iter.next(&inputFilename)) {
    371         do {
    372             SkString inputPath;
    373             sk_tools::make_filepath(&inputPath, inputAsSkString, inputFilename);
    374             if (!run_single_benchmark(inputPath, benchmark)) {
    375                 ++failures;
    376             }
    377         } while(iter.next(&inputFilename));
    378     } else if (SkStrEndsWith(input, ".skp")) {
    379         if (!run_single_benchmark(inputAsSkString, benchmark)) {
    380             ++failures;
    381         }
    382     } else {
    383         SkString warning;
    384         warning.printf("Warning: skipping %s\n", input);
    385         gLogger.logError(warning);
    386     }
    387     return failures;
    388 }
    389 
    390 int tool_main(int argc, char** argv);
    391 int tool_main(int argc, char** argv) {
    392     SkString usage;
    393     usage.printf("Time drawing .skp files.\n"
    394                  "\tPossible arguments for --filter: [%s]\n\t\t[%s]",
    395                  filterTypesUsage().c_str(), filterFlagsUsage().c_str());
    396     SkCommandLineFlags::SetUsage(usage.c_str());
    397     SkCommandLineFlags::Parse(argc, argv);
    398 
    399     if (FLAGS_repeat < 1) {
    400         SkString error;
    401         error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat);
    402         gLogger.logError(error);
    403         exit(-1);
    404     }
    405 
    406     if (FLAGS_logFile.count() == 1) {
    407         if (!gLogger.SetLogFile(FLAGS_logFile[0])) {
    408             SkString str;
    409             str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]);
    410             gLogger.logError(str);
    411             // TODO(borenet): We're disabling this for now, due to
    412             // write-protected Android devices.  The very short-term
    413             // solution is to ignore the fact that we have no log file.
    414             //exit(-1);
    415         }
    416     }
    417 
    418 
    419 #if SK_ENABLE_INST_COUNT
    420     gPrintInstCount = true;
    421 #endif
    422     SkAutoGraphics ag;
    423 
    424     sk_tools::PictureBenchmark benchmark;
    425 
    426     setup_benchmark(&benchmark);
    427 
    428     int failures = 0;
    429     for (int i = 0; i < FLAGS_readPath.count(); ++i) {
    430         failures += process_input(FLAGS_readPath[i], benchmark);
    431     }
    432 
    433     if (failures != 0) {
    434         SkString err;
    435         err.printf("Failed to run %i benchmarks.\n", failures);
    436         gLogger.logError(err);
    437         return 1;
    438     }
    439 #if LAZY_CACHE_STATS
    440     if (FLAGS_trackDeferredCaching) {
    441         SkDebugf("Total cache hit rate: %f\n",
    442                  (double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses));
    443     }
    444 #endif
    445     return 0;
    446 }
    447 
    448 #if !defined SK_BUILD_FOR_IOS
    449 int main(int argc, char * const argv[]) {
    450     return tool_main(argc, (char**) argv);
    451 }
    452 #endif
    453