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