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 "PictureBenchmark.h"
     11 #include "SkBenchLogger.h"
     12 #include "SkCanvas.h"
     13 #include "SkGraphics.h"
     14 #include "SkImageDecoder.h"
     15 #include "SkMath.h"
     16 #include "SkOSFile.h"
     17 #include "SkPicture.h"
     18 #include "SkStream.h"
     19 #include "SkTArray.h"
     20 #include "picture_utils.h"
     21 
     22 const int DEFAULT_REPEATS = 1;
     23 
     24 static char const * const gFilterTypes[] = {
     25     "paint",
     26     "point",
     27     "line",
     28     "bitmap",
     29     "rect",
     30     "oval",
     31     "path",
     32     "text",
     33     "all",
     34 };
     35 
     36 static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
     37 
     38 static char const * const gFilterFlags[] = {
     39     "antiAlias",
     40     "filterBitmap",
     41     "dither",
     42     "underlineText",
     43     "strikeThruText",
     44     "fakeBoldText",
     45     "linearText",
     46     "subpixelText",
     47     "devKernText",
     48     "LCDRenderText",
     49     "embeddedBitmapText",
     50     "autoHinting",
     51     "verticalText",
     52     "genA8FromLCD",
     53     "blur",
     54     "hinting",
     55     "slightHinting",
     56     "AAClip",
     57 };
     58 
     59 static const size_t kFilterFlagsCount = sizeof(gFilterFlags) / sizeof(gFilterFlags[0]);
     60 
     61 static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
     62     int all = drawFilters[0];
     63     size_t tIndex;
     64     for (tIndex = 1; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
     65         all &= drawFilters[tIndex];
     66     }
     67     SkString result;
     68     for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
     69         SkString types;
     70         if (all & (1 << fIndex)) {
     71             types = gFilterTypes[SkDrawFilter::kTypeCount];
     72         } else {
     73             for (tIndex = 0; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
     74                 if (drawFilters[tIndex] & (1 << fIndex)) {
     75                     types += gFilterTypes[tIndex];
     76                 }
     77             }
     78         }
     79         if (!types.size()) {
     80             continue;
     81         }
     82         result += "_";
     83         result += types;
     84         result += ".";
     85         result += gFilterFlags[fIndex];
     86     }
     87     return result;
     88 }
     89 
     90 static SkString filterTypesUsage() {
     91     SkString result;
     92     for (size_t index = 0; index < kFilterTypesCount; ++index) {
     93         result += gFilterTypes[index];
     94         if (index < kFilterTypesCount - 1) {
     95             result += " | ";
     96         }
     97     }
     98     return result;
     99 }
    100 
    101 static SkString filterFlagsUsage() {
    102     SkString result;
    103     size_t len = 0;
    104     for (size_t index = 0; index < kFilterFlagsCount; ++index) {
    105         result += gFilterFlags[index];
    106         if (result.size() - len >= 72) {
    107             result += "\n           ";
    108             len = result.size();
    109         }
    110         if (index < kFilterFlagsCount - 1) {
    111             result += " | ";
    112         }
    113     }
    114     return result;
    115 }
    116 
    117 static void usage(const char* argv0) {
    118     SkDebugf("SkPicture benchmarking tool\n");
    119     SkDebugf("\n"
    120 "Usage: \n"
    121 "     %s <inputDir>...\n"
    122 "     [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
    123 "     [--repeat][--timeIndividualTiles] \n"
    124 "     [--mode pow2tile minWidth height | record | simple\n"
    125 "             | tile width height | playbackCreation]\n"
    126 "     [--pipe]\n"
    127 "     [--bbh bbhType]\n"
    128 "     [--multi numThreads]\n"
    129 "     [--viewport width height][--scale sf]\n"
    130 "     [--device bitmap"
    131 #if SK_SUPPORT_GPU
    132 " | gpu"
    133 #endif
    134 "]\n"
    135 "     [--filter [%s]:\n            [%s]]\n"
    136 , argv0, filterTypesUsage().c_str(), filterFlagsUsage().c_str());
    137     SkDebugf("\n");
    138     SkDebugf(
    139 "     inputDir:  A list of directories and files to use as input. Files are\n"
    140 "                expected to have the .skp extension.\n\n"
    141 "     --logFile filename : destination for writing log output, in addition to stdout.\n");
    142     SkDebugf("     --logPerIter 1|0 : "
    143              "Log each repeat timer instead of mean, default is disabled.\n");
    144     SkDebugf("     --min : Print the minimum times (instead of average).\n");
    145     SkDebugf("     --timers [wcgWC]* : "
    146              "Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
    147     SkDebugf("     --timeIndividualTiles : Report times for drawing individual tiles, rather than\n"
    148 "                                          times for drawing the whole page.\n"
    149 "                                          Requires --mode tile\n");
    150     SkDebugf(
    151 "     --mode pow2tile minWidth height | copyTile width height | record | simple\n"
    152 "            | tile width height | playbackCreation:\n"
    153 "            Run in the corresponding mode.\n"
    154 "            Default is simple.\n");
    155     SkDebugf(
    156 "                     pow2tile minWidth height, Creates tiles with widths\n"
    157 "                                                 that are all a power of two\n"
    158 "                                                 such that they minimize the\n"
    159 "                                                 amount of wasted tile space.\n"
    160 "                                                 minWidth is the minimum width\n"
    161 "                                                 of these tiles and must be a\n"
    162 "                                                 power of two. Simple\n"
    163 "                                                 rendering using these tiles\n"
    164 "                                                 is benchmarked.\n");
    165     SkDebugf(
    166 "                     record, Benchmark picture to picture recording.\n");
    167     SkDebugf(
    168 "                     simple, Benchmark a simple rendering.\n");
    169     SkDebugf(
    170 "                     tile width height, Benchmark simple rendering using\n"
    171 "                                            tiles with the given dimensions.\n"
    172 "                     copyTile width height, Draw the picture, then copy it into tiles.\n"
    173 "                                                Does not support percentages.\n"
    174 "                                                If the picture is large enough, breaks it into\n"
    175 "                                                larger tiles (and draws the picture once per\n"
    176 "                                                larger tile) to avoid creating a large canvas.\n"
    177 "                                                Add --tiles x y to specify the number of tiles\n"
    178 "                                                per larger tile in the x and y direction.\n"
    179              );
    180     SkDebugf(
    181 "                     playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
    182     SkDebugf("\n");
    183     SkDebugf(
    184 "     --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
    185 "                          than 1. Only works with tiled rendering.\n"
    186 "     --viewport width height : Set the viewport.\n"
    187 "     --scale sf : Scale drawing by sf.\n"
    188 "     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
    189     SkDebugf(
    190 "     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
    191 "                     be used. Accepted values are: none, rtree, grid. Default\n"
    192 "                     value is none. Not compatible with --pipe. With value\n"
    193 "                     'grid', width and height must be specified. 'grid' can\n"
    194 "                     only be used with modes tile, record, and\n"
    195 "                     playbackCreation.");
    196     SkDebugf(
    197 "     --device bitmap"
    198 #if SK_SUPPORT_GPU
    199 " | gpu"
    200 #endif
    201 ": Use the corresponding device. Default is bitmap.\n");
    202     SkDebugf(
    203 "                     bitmap, Render to a bitmap.\n");
    204 #if SK_SUPPORT_GPU
    205     SkDebugf(
    206 "                     gpu, Render to the GPU.\n");
    207 #endif
    208     SkDebugf("\n");
    209     SkDebugf(
    210 "     --repeat:  "
    211 "Set the number of times to repeat each test."
    212 " Default is %i.\n", DEFAULT_REPEATS);
    213     SkDebugf(
    214 "     --filter type:flag : Enable canvas filtering to disable a paint flag,\n"
    215 "                     use no blur or low quality blur, or use no hinting or\n"
    216 "                     slight hinting. For all flags except AAClip, specify the\n"
    217 "                     type of primitive to effect, or choose all. for AAClip\n"
    218 "                     alone, the filter affects all clips independent of type.\n");
    219 }
    220 
    221 SkBenchLogger gLogger;
    222 
    223 static bool run_single_benchmark(const SkString& inputPath,
    224                                  sk_tools::PictureBenchmark& benchmark) {
    225     SkFILEStream inputStream;
    226 
    227     inputStream.setPath(inputPath.c_str());
    228     if (!inputStream.isValid()) {
    229         SkString err;
    230         err.printf("Could not open file %s\n", inputPath.c_str());
    231         gLogger.logError(err);
    232         return false;
    233     }
    234 
    235     bool success = false;
    236     SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
    237     if (!success) {
    238         SkString err;
    239         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
    240         gLogger.logError(err);
    241         return false;
    242     }
    243 
    244     SkString filename;
    245     sk_tools::get_basename(&filename, inputPath);
    246 
    247     SkString result;
    248     result.printf("running bench [%i %i] %s ", picture.width(),
    249                   picture.height(), filename.c_str());
    250     gLogger.logProgress(result);
    251 
    252     benchmark.run(&picture);
    253     return true;
    254 }
    255 
    256 #define PRINT_USAGE_AND_EXIT \
    257     do {                     \
    258         usage(argv0);        \
    259         exit(-1);            \
    260     } while (0)
    261 
    262 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
    263                               sk_tools::PictureBenchmark* benchmark) {
    264     const char* argv0 = argv[0];
    265     char* const* stop = argv + argc;
    266 
    267     int repeats = DEFAULT_REPEATS;
    268     sk_tools::PictureRenderer::SkDeviceTypes deviceType =
    269         sk_tools::PictureRenderer::kBitmap_DeviceType;
    270 
    271     SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
    272 
    273     // Create a string to show our current settings.
    274     // TODO: Make it prettier. Currently it just repeats the command line.
    275     SkString commandLine("bench_pictures:");
    276     for (int i = 1; i < argc; i++) {
    277         commandLine.appendf(" %s", *(argv+i));
    278     }
    279     commandLine.append("\n");
    280 
    281     bool usePipe = false;
    282     int numThreads = 1;
    283     bool useTiles = false;
    284     const char* widthString = NULL;
    285     const char* heightString = NULL;
    286     int gridWidth = 0;
    287     int gridHeight = 0;
    288     bool isPowerOf2Mode = false;
    289     bool isCopyMode = false;
    290     const char* xTilesString = NULL;
    291     const char* yTilesString = NULL;
    292     const char* mode = NULL;
    293     bool gridSupported = false;
    294     sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
    295         sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
    296     sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
    297     sk_bzero(drawFilters, sizeof(drawFilters));
    298     SkISize viewport;
    299     viewport.setEmpty();
    300     SkScalar scaleFactor = SK_Scalar1;
    301     for (++argv; argv < stop; ++argv) {
    302         if (0 == strcmp(*argv, "--repeat")) {
    303             ++argv;
    304             if (argv < stop) {
    305                 repeats = atoi(*argv);
    306                 if (repeats < 1) {
    307                     gLogger.logError("--repeat must be given a value > 0\n");
    308                     PRINT_USAGE_AND_EXIT;
    309                 }
    310             } else {
    311                 gLogger.logError("Missing arg for --repeat\n");
    312                 PRINT_USAGE_AND_EXIT;
    313             }
    314         } else if (0 == strcmp(*argv, "--pipe")) {
    315             usePipe = true;
    316         } else if (0 == strcmp(*argv, "--logFile")) {
    317             argv++;
    318             if (argv < stop) {
    319                 if (!gLogger.SetLogFile(*argv)) {
    320                     SkString str;
    321                     str.printf("Could not open %s for writing.", *argv);
    322                     gLogger.logError(str);
    323                     usage(argv0);
    324                     // TODO(borenet): We're disabling this for now, due to
    325                     // write-protected Android devices.  The very short-term
    326                     // solution is to ignore the fact that we have no log file.
    327                     //exit(-1);
    328                 }
    329             } else {
    330                 gLogger.logError("Missing arg for --logFile\n");
    331                 PRINT_USAGE_AND_EXIT;
    332             }
    333         } else if (0 == strcmp(*argv, "--multi")) {
    334             ++argv;
    335             if (argv >= stop) {
    336                 gLogger.logError("Missing arg for --multi\n");
    337                 PRINT_USAGE_AND_EXIT;
    338             }
    339             numThreads = atoi(*argv);
    340             if (numThreads < 2) {
    341                 gLogger.logError("Number of threads must be at least 2.\n");
    342                 PRINT_USAGE_AND_EXIT;
    343             }
    344         } else if (0 == strcmp(*argv, "--bbh")) {
    345             ++argv;
    346             if (argv >= stop) {
    347                 gLogger.logError("Missing value for --bbh\n");
    348                 PRINT_USAGE_AND_EXIT;
    349             }
    350             if (0 == strcmp(*argv, "none")) {
    351                 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
    352             } else if (0 == strcmp(*argv, "rtree")) {
    353                 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
    354             } else if (0 == strcmp(*argv, "grid")) {
    355                 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
    356                 ++argv;
    357                 if (argv >= stop) {
    358                     gLogger.logError("Missing width for --bbh grid\n");
    359                     PRINT_USAGE_AND_EXIT;
    360                 }
    361                 gridWidth = atoi(*argv);
    362                 ++argv;
    363                 if (argv >= stop) {
    364                     gLogger.logError("Missing height for --bbh grid\n");
    365                     PRINT_USAGE_AND_EXIT;
    366                 }
    367                 gridHeight = atoi(*argv);
    368             } else {
    369                 SkString err;
    370                 err.printf("%s is not a valid value for --bbhType\n", *argv);
    371                 gLogger.logError(err);
    372                 PRINT_USAGE_AND_EXIT;
    373             }
    374 
    375         } else if (0 == strcmp(*argv, "--mode")) {
    376             if (renderer.get() != NULL) {
    377                 SkDebugf("Cannot combine modes.\n");
    378                 PRINT_USAGE_AND_EXIT;
    379             }
    380 
    381             ++argv;
    382             if (argv >= stop) {
    383                 gLogger.logError("Missing mode for --mode\n");
    384                 PRINT_USAGE_AND_EXIT;
    385             }
    386 
    387             if (0 == strcmp(*argv, "record")) {
    388                 renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
    389                 gridSupported = true;
    390             } else if (0 == strcmp(*argv, "clone")) {
    391                 renderer.reset(sk_tools::CreatePictureCloneRenderer());
    392             } else if (0 == strcmp(*argv, "simple")) {
    393                 renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
    394             } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
    395                        || 0 == strcmp(*argv, "copyTile")) {
    396                 useTiles = true;
    397                 mode = *argv;
    398 
    399                 if (0 == strcmp(*argv, "pow2tile")) {
    400                     isPowerOf2Mode = true;
    401                 } else if (0 == strcmp(*argv, "copyTile")) {
    402                     isCopyMode = true;
    403                 } else {
    404                     gridSupported = true;
    405                 }
    406 
    407                 ++argv;
    408                 if (argv >= stop) {
    409                     SkString err;
    410                     err.printf("Missing width for --mode %s\n", mode);
    411                     gLogger.logError(err);
    412                     PRINT_USAGE_AND_EXIT;
    413                 }
    414 
    415                 widthString = *argv;
    416                 ++argv;
    417                 if (argv >= stop) {
    418                     SkString err;
    419                     err.appendf("Missing height for --mode %s\n", mode);
    420                     gLogger.logError(err);
    421                     PRINT_USAGE_AND_EXIT;
    422                 }
    423                 heightString = *argv;
    424             } else if (0 == strcmp(*argv, "playbackCreation")) {
    425                 renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
    426                 gridSupported = true;
    427             } else if (0 == strcmp(*argv, "gatherPixelRefs")) {
    428                 renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
    429             } else {
    430                 SkString err;
    431                 err.printf("%s is not a valid mode for --mode\n", *argv);
    432                 gLogger.logError(err);
    433                 PRINT_USAGE_AND_EXIT;
    434             }
    435         } else if (0 == strcmp(*argv, "--viewport")) {
    436             ++argv;
    437             if (argv >= stop) {
    438                 gLogger.logError("Missing width for --viewport\n");
    439                 PRINT_USAGE_AND_EXIT;
    440             }
    441             viewport.fWidth = atoi(*argv);
    442             ++argv;
    443             if (argv >= stop) {
    444                 gLogger.logError("Missing height for --viewport\n");
    445                 PRINT_USAGE_AND_EXIT;
    446             }
    447             viewport.fHeight = atoi(*argv);
    448         } else if (0 == strcmp(*argv, "--scale")) {
    449             ++argv;
    450             if (argv >= stop) {
    451                 gLogger.logError("Missing scaleFactor for --scale\n");
    452                 PRINT_USAGE_AND_EXIT;
    453             }
    454             scaleFactor = SkDoubleToScalar(atof(*argv));
    455         } else if (0 == strcmp(*argv, "--tiles")) {
    456             ++argv;
    457             if (argv >= stop) {
    458                 gLogger.logError("Missing x for --tiles\n");
    459                 PRINT_USAGE_AND_EXIT;
    460             }
    461             xTilesString = *argv;
    462             ++argv;
    463             if (argv >= stop) {
    464                 gLogger.logError("Missing y for --tiles\n");
    465                 PRINT_USAGE_AND_EXIT;
    466             }
    467             yTilesString = *argv;
    468         }  else if (0 == strcmp(*argv, "--device")) {
    469             ++argv;
    470             if (argv >= stop) {
    471                 gLogger.logError("Missing mode for --device\n");
    472                 PRINT_USAGE_AND_EXIT;
    473             }
    474 
    475             if (0 == strcmp(*argv, "bitmap")) {
    476                 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
    477             }
    478 #if SK_SUPPORT_GPU
    479             else if (0 == strcmp(*argv, "gpu")) {
    480                 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
    481             }
    482 #endif
    483             else {
    484                 SkString err;
    485                 err.printf("%s is not a valid mode for --device\n", *argv);
    486                 gLogger.logError(err);
    487                 PRINT_USAGE_AND_EXIT;
    488             }
    489         } else if (0 == strcmp(*argv, "--timers")) {
    490             ++argv;
    491             if (argv < stop) {
    492                 bool timerWall = false;
    493                 bool truncatedTimerWall = false;
    494                 bool timerCpu = false;
    495                 bool truncatedTimerCpu = false;
    496                 bool timerGpu = false;
    497                 for (char* t = *argv; *t; ++t) {
    498                     switch (*t) {
    499                         case 'w':
    500                             timerWall = true;
    501                             break;
    502                         case 'c':
    503                             timerCpu = true;
    504                             break;
    505                         case 'W':
    506                             truncatedTimerWall = true;
    507                             break;
    508                         case 'C':
    509                             truncatedTimerCpu = true;
    510                             break;
    511                         case 'g':
    512                             timerGpu = true;
    513                             break;
    514                         default: {
    515                             break;
    516                         }
    517                     }
    518                 }
    519                 benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu,
    520                                            truncatedTimerCpu, timerGpu);
    521             } else {
    522                 gLogger.logError("Missing arg for --timers\n");
    523                 PRINT_USAGE_AND_EXIT;
    524             }
    525         } else if (0 == strcmp(*argv, "--timeIndividualTiles")) {
    526             benchmark->setTimeIndividualTiles(true);
    527         } else if (0 == strcmp(*argv, "--min")) {
    528             benchmark->setPrintMin(true);
    529         } else if (0 == strcmp(*argv, "--logPerIter")) {
    530             ++argv;
    531             if (argv < stop) {
    532                 bool log = atoi(*argv) != 0;
    533                 benchmark->setLogPerIter(log);
    534             } else {
    535                 gLogger.logError("Missing arg for --logPerIter\n");
    536                 PRINT_USAGE_AND_EXIT;
    537             }
    538         } else if (0 == strcmp(*argv, "--filter")) {
    539             ++argv;
    540             if (argv < stop) {
    541                 const char* colon = strchr(*argv, ':');
    542                 if (colon) {
    543                     int type = -1;
    544                     size_t typeLen = colon - *argv;
    545                     for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
    546                         if (typeLen == strlen(gFilterTypes[tIndex])
    547                                 && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) {
    548                             type = tIndex;
    549                             break;
    550                         }
    551                     }
    552                     if (type < 0) {
    553                         SkString err;
    554                         err.printf("Unknown type for --filter %s\n", *argv);
    555                         gLogger.logError(err);
    556                         PRINT_USAGE_AND_EXIT;
    557                     }
    558                     int flag = -1;
    559                     size_t flagLen = strlen(*argv) - typeLen - 1;
    560                     for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
    561                         if (flagLen == strlen(gFilterFlags[fIndex])
    562                                 && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
    563                             flag = 1 << fIndex;
    564                             break;
    565                         }
    566                     }
    567                     if (flag < 0) {
    568                         SkString err;
    569                         err.printf("Unknown flag for --filter %s\n", *argv);
    570                         gLogger.logError(err);
    571                         PRINT_USAGE_AND_EXIT;
    572                     }
    573                     for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
    574                         if (type != SkDrawFilter::kTypeCount && index != type) {
    575                             continue;
    576                         }
    577                         drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
    578                                 (drawFilters[index] | flag);
    579                     }
    580                 } else {
    581                     SkString err;
    582                     err.printf("Unknown arg for --filter %s : missing colon\n", *argv);
    583                     gLogger.logError(err);
    584                     PRINT_USAGE_AND_EXIT;
    585                 }
    586             } else {
    587                 gLogger.logError("Missing arg for --filter\n");
    588                 PRINT_USAGE_AND_EXIT;
    589             }
    590         } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
    591             PRINT_USAGE_AND_EXIT;
    592         } else {
    593             inputs->push_back(SkString(*argv));
    594         }
    595     }
    596 
    597     if (numThreads > 1 && !useTiles) {
    598         gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
    599         PRINT_USAGE_AND_EXIT;
    600     }
    601 
    602     if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
    603         gLogger.logError("--pipe and --bbh cannot be used together\n");
    604         PRINT_USAGE_AND_EXIT;
    605     }
    606 
    607     if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
    608         !gridSupported) {
    609         gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n");
    610         PRINT_USAGE_AND_EXIT;
    611     }
    612 
    613     if (useTiles) {
    614         SkASSERT(NULL == renderer);
    615         sk_tools::TiledPictureRenderer* tiledRenderer;
    616         if (isCopyMode) {
    617             int x, y;
    618             if (xTilesString != NULL) {
    619                 SkASSERT(yTilesString != NULL);
    620                 x = atoi(xTilesString);
    621                 y = atoi(yTilesString);
    622                 if (x <= 0 || y <= 0) {
    623                     gLogger.logError("--tiles must be given values > 0\n");
    624                     PRINT_USAGE_AND_EXIT;
    625                 }
    626             } else {
    627                 x = y = 4;
    628             }
    629             tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
    630             if (benchmark->timeIndividualTiles()) {
    631                 gLogger.logError("timeIndividualTiles is not compatible with copyTile\n");
    632                 PRINT_USAGE_AND_EXIT;
    633             }
    634         } else if (numThreads > 1) {
    635             tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
    636         } else {
    637             tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
    638         }
    639         if (isPowerOf2Mode) {
    640             int minWidth = atoi(widthString);
    641             if (!SkIsPow2(minWidth) || minWidth < 0) {
    642                 tiledRenderer->unref();
    643                 SkString err;
    644                 err.printf("-mode %s must be given a width"
    645                          " value that is a power of two\n", mode);
    646                 gLogger.logError(err);
    647                 PRINT_USAGE_AND_EXIT;
    648             }
    649             tiledRenderer->setTileMinPowerOf2Width(minWidth);
    650         } else if (sk_tools::is_percentage(widthString)) {
    651             if (isCopyMode) {
    652                 tiledRenderer->unref();
    653                 SkString err;
    654                 err.printf("--mode %s does not support percentages.\n", mode);
    655                 gLogger.logError(err.c_str());
    656                 PRINT_USAGE_AND_EXIT;
    657             }
    658             tiledRenderer->setTileWidthPercentage(atof(widthString));
    659             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
    660                 tiledRenderer->unref();
    661                 SkString err;
    662                 err.appendf("--mode %s must be given a width percentage > 0\n", mode);
    663                 gLogger.logError(err);
    664                 PRINT_USAGE_AND_EXIT;
    665             }
    666         } else {
    667             tiledRenderer->setTileWidth(atoi(widthString));
    668             if (!(tiledRenderer->getTileWidth() > 0)) {
    669                 tiledRenderer->unref();
    670                 SkString err;
    671                 err.appendf("--mode %s must be given a width > 0\n", mode);
    672                 gLogger.logError(err);
    673                 PRINT_USAGE_AND_EXIT;
    674             }
    675         }
    676 
    677         if (sk_tools::is_percentage(heightString)) {
    678             if (isCopyMode) {
    679                 tiledRenderer->unref();
    680                 SkString err;
    681                 err.printf("--mode %s does not support percentages.\n", mode);
    682                 gLogger.logError(err.c_str());
    683                 PRINT_USAGE_AND_EXIT;
    684             }
    685             tiledRenderer->setTileHeightPercentage(atof(heightString));
    686             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
    687                 tiledRenderer->unref();
    688                 SkString err;
    689                 err.appendf("--mode %s must be given a height percentage > 0\n", mode);
    690                 gLogger.logError(err);
    691                 PRINT_USAGE_AND_EXIT;
    692             }
    693         } else {
    694             tiledRenderer->setTileHeight(atoi(heightString));
    695             if (!(tiledRenderer->getTileHeight() > 0)) {
    696                 tiledRenderer->unref();
    697                 SkString err;
    698                 err.appendf("--mode %s must be given a height > 0\n", mode);
    699                 gLogger.logError(err);
    700                 PRINT_USAGE_AND_EXIT;
    701             }
    702         }
    703         if (numThreads > 1) {
    704 #if SK_SUPPORT_GPU
    705             if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
    706                 tiledRenderer->unref();
    707                 gLogger.logError("GPU not compatible with multithreaded tiling.\n");
    708                 PRINT_USAGE_AND_EXIT;
    709             }
    710 #endif
    711         }
    712         renderer.reset(tiledRenderer);
    713         if (usePipe) {
    714             gLogger.logError("Pipe rendering is currently not compatible with tiling.\n"
    715                      "Turning off pipe.\n");
    716         }
    717     } else {
    718         if (benchmark->timeIndividualTiles()) {
    719             gLogger.logError("timeIndividualTiles requires tiled rendering.\n");
    720             PRINT_USAGE_AND_EXIT;
    721         }
    722         if (usePipe) {
    723             if (renderer.get() != NULL) {
    724                 gLogger.logError("Pipe is incompatible with other modes.\n");
    725                 PRINT_USAGE_AND_EXIT;
    726             }
    727             renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
    728         }
    729     }
    730     if (inputs->count() < 1) {
    731         PRINT_USAGE_AND_EXIT;
    732     }
    733 
    734     if (NULL == renderer) {
    735         renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
    736     }
    737 
    738     renderer->setBBoxHierarchyType(bbhType);
    739     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
    740     renderer->setGridSize(gridWidth, gridHeight);
    741     renderer->setViewport(viewport);
    742     renderer->setScaleFactor(scaleFactor);
    743     benchmark->setRenderer(renderer);
    744     benchmark->setRepeats(repeats);
    745     benchmark->setDeviceType(deviceType);
    746     benchmark->setLogger(&gLogger);
    747     // Report current settings:
    748     gLogger.logProgress(commandLine);
    749 }
    750 
    751 static int process_input(const SkString& input,
    752                          sk_tools::PictureBenchmark& benchmark) {
    753     SkOSFile::Iter iter(input.c_str(), "skp");
    754     SkString inputFilename;
    755     int failures = 0;
    756     if (iter.next(&inputFilename)) {
    757         do {
    758             SkString inputPath;
    759             sk_tools::make_filepath(&inputPath, input, inputFilename);
    760             if (!run_single_benchmark(inputPath, benchmark)) {
    761                 ++failures;
    762             }
    763         } while(iter.next(&inputFilename));
    764     } else if (SkStrEndsWith(input.c_str(), ".skp")) {
    765         if (!run_single_benchmark(input, benchmark)) {
    766             ++failures;
    767         }
    768     } else {
    769         SkString warning;
    770         warning.printf("Warning: skipping %s\n", input.c_str());
    771         gLogger.logError(warning);
    772     }
    773     return failures;
    774 }
    775 
    776 int tool_main(int argc, char** argv);
    777 int tool_main(int argc, char** argv) {
    778 #if SK_ENABLE_INST_COUNT
    779     gPrintInstCount = true;
    780 #endif
    781     SkAutoGraphics ag;
    782 
    783     SkTArray<SkString> inputs;
    784     sk_tools::PictureBenchmark benchmark;
    785 
    786     parse_commandline(argc, argv, &inputs, &benchmark);
    787 
    788     int failures = 0;
    789     for (int i = 0; i < inputs.count(); ++i) {
    790         failures += process_input(inputs[i], benchmark);
    791     }
    792 
    793     if (failures != 0) {
    794         SkString err;
    795         err.printf("Failed to run %i benchmarks.\n", failures);
    796         gLogger.logError(err);
    797         return 1;
    798     }
    799     return 0;
    800 }
    801 
    802 #if !defined SK_BUILD_FOR_IOS
    803 int main(int argc, char * const argv[]) {
    804     return tool_main(argc, (char**) argv);
    805 }
    806 #endif
    807