Provide an option to bench drawing individual tiles in bench_pictures.
[platform/upstream/libSkiaSharp.git] / tools / bench_pictures_main.cpp
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     "path",
31     "text",
32     "all",
33 };
34
35 static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
36
37 static char const * const gFilterFlags[] = {
38     "antiAlias",
39     "filterBitmap",
40     "dither",
41     "underlineText",
42     "strikeThruText",
43     "fakeBoldText",
44     "linearText",
45     "subpixelText",
46     "devKernText",
47     "LCDRenderText",
48     "embeddedBitmapText",
49     "autoHinting",
50     "verticalText",
51     "genA8FromLCD",
52     "blur",
53     "lowBlur",
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]\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 "     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
188     SkDebugf(
189 "     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
190 "                     be used. Accepted values are: none, rtree, grid. Default\n"
191 "                     value is none. Not compatible with --pipe. With value\n"
192 "                     'grid', width and height must be specified. 'grid' can\n"
193 "                     only be used with modes tile, record, and\n"
194 "                     playbackCreation.");
195     SkDebugf(
196 "     --device bitmap"
197 #if SK_SUPPORT_GPU
198 " | gpu"
199 #endif
200 ": Use the corresponding device. Default is bitmap.\n");
201     SkDebugf(
202 "                     bitmap, Render to a bitmap.\n");
203 #if SK_SUPPORT_GPU
204     SkDebugf(
205 "                     gpu, Render to the GPU.\n");
206 #endif
207     SkDebugf("\n");
208     SkDebugf(
209 "     --repeat:  "
210 "Set the number of times to repeat each test."
211 " Default is %i.\n", DEFAULT_REPEATS);
212     SkDebugf(
213 "     --filter type:flag : Enable canvas filtering to disable a paint flag,\n"
214 "                     use no blur or low quality blur, or use no hinting or\n"
215 "                     slight hinting. For all flags except AAClip, specify the\n"
216 "                     type of primitive to effect, or choose all. for AAClip\n"
217 "                     alone, the filter affects all clips independent of type.\n");
218 }
219
220 SkBenchLogger gLogger;
221
222 static bool run_single_benchmark(const SkString& inputPath,
223                                  sk_tools::PictureBenchmark& benchmark) {
224     SkFILEStream inputStream;
225
226     inputStream.setPath(inputPath.c_str());
227     if (!inputStream.isValid()) {
228         SkString err;
229         err.printf("Could not open file %s\n", inputPath.c_str());
230         gLogger.logError(err);
231         return false;
232     }
233
234     bool success = false;
235     SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
236     if (!success) {
237         SkString err;
238         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
239         gLogger.logError(err);
240         return false;
241     }
242
243     SkString filename;
244     sk_tools::get_basename(&filename, inputPath);
245
246     SkString result;
247     result.printf("running bench [%i %i] %s ", picture.width(),
248                   picture.height(), filename.c_str());
249     gLogger.logProgress(result);
250
251     benchmark.run(&picture);
252     return true;
253 }
254
255 #define PRINT_USAGE_AND_EXIT \
256     do {                     \
257         usage(argv0);        \
258         exit(-1);            \
259     } while (0)
260
261 static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
262                               sk_tools::PictureBenchmark* benchmark) {
263     const char* argv0 = argv[0];
264     char* const* stop = argv + argc;
265
266     int repeats = DEFAULT_REPEATS;
267     sk_tools::PictureRenderer::SkDeviceTypes deviceType =
268         sk_tools::PictureRenderer::kBitmap_DeviceType;
269
270     SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
271
272     // Create a string to show our current settings.
273     // TODO: Make it prettier. Currently it just repeats the command line.
274     SkString commandLine("bench_pictures:");
275     for (int i = 1; i < argc; i++) {
276         commandLine.appendf(" %s", *(argv+i));
277     }
278     commandLine.append("\n");
279
280     bool usePipe = false;
281     int numThreads = 1;
282     bool useTiles = false;
283     const char* widthString = NULL;
284     const char* heightString = NULL;
285     int gridWidth = 0;
286     int gridHeight = 0;
287     bool isPowerOf2Mode = false;
288     bool isCopyMode = false;
289     const char* xTilesString = NULL;
290     const char* yTilesString = NULL;
291     const char* mode = NULL;
292     bool gridSupported = false;
293     sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
294         sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
295     sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
296     sk_bzero(drawFilters, sizeof(drawFilters));
297     SkISize viewport;
298     viewport.setEmpty();
299     for (++argv; argv < stop; ++argv) {
300         if (0 == strcmp(*argv, "--repeat")) {
301             ++argv;
302             if (argv < stop) {
303                 repeats = atoi(*argv);
304                 if (repeats < 1) {
305                     gLogger.logError("--repeat must be given a value > 0\n");
306                     PRINT_USAGE_AND_EXIT;
307                 }
308             } else {
309                 gLogger.logError("Missing arg for --repeat\n");
310                 PRINT_USAGE_AND_EXIT;
311             }
312         } else if (0 == strcmp(*argv, "--pipe")) {
313             usePipe = true;
314         } else if (0 == strcmp(*argv, "--logFile")) {
315             argv++;
316             if (argv < stop) {
317                 if (!gLogger.SetLogFile(*argv)) {
318                     SkString str;
319                     str.printf("Could not open %s for writing.", *argv);
320                     gLogger.logError(str);
321                     usage(argv0);
322                     // TODO(borenet): We're disabling this for now, due to
323                     // write-protected Android devices.  The very short-term
324                     // solution is to ignore the fact that we have no log file.
325                     //exit(-1);
326                 }
327             } else {
328                 gLogger.logError("Missing arg for --logFile\n");
329                 PRINT_USAGE_AND_EXIT;
330             }
331         } else if (0 == strcmp(*argv, "--multi")) {
332             ++argv;
333             if (argv >= stop) {
334                 gLogger.logError("Missing arg for --multi\n");
335                 PRINT_USAGE_AND_EXIT;
336             }
337             numThreads = atoi(*argv);
338             if (numThreads < 2) {
339                 gLogger.logError("Number of threads must be at least 2.\n");
340                 PRINT_USAGE_AND_EXIT;
341             }
342         } else if (0 == strcmp(*argv, "--bbh")) {
343             ++argv;
344             if (argv >= stop) {
345                 gLogger.logError("Missing value for --bbh\n");
346                 PRINT_USAGE_AND_EXIT;
347             }
348             if (0 == strcmp(*argv, "none")) {
349                 bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
350             } else if (0 == strcmp(*argv, "rtree")) {
351                 bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
352             } else if (0 == strcmp(*argv, "grid")) {
353                 bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
354                 ++argv;
355                 if (argv >= stop) {
356                     gLogger.logError("Missing width for --bbh grid\n");
357                     PRINT_USAGE_AND_EXIT;
358                 }
359                 gridWidth = atoi(*argv);
360                 ++argv;
361                 if (argv >= stop) {
362                     gLogger.logError("Missing height for --bbh grid\n");
363                     PRINT_USAGE_AND_EXIT;
364                 }
365                 gridHeight = atoi(*argv);
366             } else {
367                 SkString err;
368                 err.printf("%s is not a valid value for --bbhType\n", *argv);
369                 gLogger.logError(err);
370                 PRINT_USAGE_AND_EXIT;
371             }
372
373         } else if (0 == strcmp(*argv, "--mode")) {
374             if (renderer.get() != NULL) {
375                 SkDebugf("Cannot combine modes.\n");
376                 PRINT_USAGE_AND_EXIT;
377             }
378
379             ++argv;
380             if (argv >= stop) {
381                 gLogger.logError("Missing mode for --mode\n");
382                 PRINT_USAGE_AND_EXIT;
383             }
384
385             if (0 == strcmp(*argv, "record")) {
386                 renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
387                 gridSupported = true;
388             } else if (0 == strcmp(*argv, "clone")) {
389                 renderer.reset(sk_tools::CreatePictureCloneRenderer());
390             } else if (0 == strcmp(*argv, "simple")) {
391                 renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
392             } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
393                        || 0 == strcmp(*argv, "copyTile")) {
394                 useTiles = true;
395                 mode = *argv;
396
397                 if (0 == strcmp(*argv, "pow2tile")) {
398                     isPowerOf2Mode = true;
399                 } else if (0 == strcmp(*argv, "copyTile")) {
400                     isCopyMode = true;
401                 } else {
402                     gridSupported = true;
403                 }
404
405                 ++argv;
406                 if (argv >= stop) {
407                     SkString err;
408                     err.printf("Missing width for --mode %s\n", mode);
409                     gLogger.logError(err);
410                     PRINT_USAGE_AND_EXIT;
411                 }
412
413                 widthString = *argv;
414                 ++argv;
415                 if (argv >= stop) {
416                     SkString err;
417                     err.appendf("Missing height for --mode %s\n", mode);
418                     gLogger.logError(err);
419                     PRINT_USAGE_AND_EXIT;
420                 }
421                 heightString = *argv;
422             } else if (0 == strcmp(*argv, "playbackCreation")) {
423                 renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
424                 gridSupported = true;
425             } else if (0 == strcmp(*argv, "gatherPixelRefs")) {
426                 renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
427             } else {
428                 SkString err;
429                 err.printf("%s is not a valid mode for --mode\n", *argv);
430                 gLogger.logError(err);
431                 PRINT_USAGE_AND_EXIT;
432             }
433         } else if (0 == strcmp(*argv, "--viewport")) {
434             ++argv;
435             if (argv >= stop) {
436                 gLogger.logError("Missing width for --viewport\n");
437                 PRINT_USAGE_AND_EXIT;
438             }
439             viewport.fWidth = atoi(*argv);
440             ++argv;
441             if (argv >= stop) {
442                 gLogger.logError("Missing height for --viewport\n");
443                 PRINT_USAGE_AND_EXIT;
444             }
445             viewport.fHeight = atoi(*argv);
446         } else if (0 == strcmp(*argv, "--tiles")) {
447             ++argv;
448             if (argv >= stop) {
449                 gLogger.logError("Missing x for --tiles\n");
450                 PRINT_USAGE_AND_EXIT;
451             }
452             xTilesString = *argv;
453             ++argv;
454             if (argv >= stop) {
455                 gLogger.logError("Missing y for --tiles\n");
456                 PRINT_USAGE_AND_EXIT;
457             }
458             yTilesString = *argv;
459         }  else if (0 == strcmp(*argv, "--device")) {
460             ++argv;
461             if (argv >= stop) {
462                 gLogger.logError("Missing mode for --device\n");
463                 PRINT_USAGE_AND_EXIT;
464             }
465
466             if (0 == strcmp(*argv, "bitmap")) {
467                 deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
468             }
469 #if SK_SUPPORT_GPU
470             else if (0 == strcmp(*argv, "gpu")) {
471                 deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
472             }
473 #endif
474             else {
475                 SkString err;
476                 err.printf("%s is not a valid mode for --device\n", *argv);
477                 gLogger.logError(err);
478                 PRINT_USAGE_AND_EXIT;
479             }
480         } else if (0 == strcmp(*argv, "--timers")) {
481             ++argv;
482             if (argv < stop) {
483                 bool timerWall = false;
484                 bool truncatedTimerWall = false;
485                 bool timerCpu = false;
486                 bool truncatedTimerCpu = false;
487                 bool timerGpu = false;
488                 for (char* t = *argv; *t; ++t) {
489                     switch (*t) {
490                         case 'w':
491                             timerWall = true;
492                             break;
493                         case 'c':
494                             timerCpu = true;
495                             break;
496                         case 'W':
497                             truncatedTimerWall = true;
498                             break;
499                         case 'C':
500                             truncatedTimerCpu = true;
501                             break;
502                         case 'g':
503                             timerGpu = true;
504                             break;
505                         default: {
506                             break;
507                         }
508                     }
509                 }
510                 benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu,
511                                            truncatedTimerCpu, timerGpu);
512             } else {
513                 gLogger.logError("Missing arg for --timers\n");
514                 PRINT_USAGE_AND_EXIT;
515             }
516         } else if (0 == strcmp(*argv, "--timeIndividualTiles")) {
517             benchmark->setTimeIndividualTiles(true);
518         } else if (0 == strcmp(*argv, "--min")) {
519             benchmark->setPrintMin(true);
520         } else if (0 == strcmp(*argv, "--logPerIter")) {
521             ++argv;
522             if (argv < stop) {
523                 bool log = atoi(*argv) != 0;
524                 benchmark->setLogPerIter(log);
525             } else {
526                 gLogger.logError("Missing arg for --logPerIter\n");
527                 PRINT_USAGE_AND_EXIT;
528             }
529         } else if (0 == strcmp(*argv, "--filter")) {
530             ++argv;
531             if (argv < stop) {
532                 const char* colon = strchr(*argv, ':');
533                 if (colon) {
534                     int type = -1;
535                     size_t typeLen = colon - *argv;
536                     for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
537                         if (typeLen == strlen(gFilterTypes[tIndex])
538                                 && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) {
539                             type = tIndex;
540                             break;
541                         }
542                     }
543                     if (type < 0) {
544                         SkString err;
545                         err.printf("Unknown type for --filter %s\n", *argv);
546                         gLogger.logError(err);
547                         PRINT_USAGE_AND_EXIT;
548                     }
549                     int flag = -1;
550                     size_t flagLen = strlen(*argv) - typeLen - 1;
551                     for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
552                         if (flagLen == strlen(gFilterFlags[fIndex])
553                                 && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
554                             flag = 1 << fIndex;
555                             break;
556                         }
557                     }
558                     if (flag < 0) {
559                         SkString err;
560                         err.printf("Unknown flag for --filter %s\n", *argv);
561                         gLogger.logError(err);
562                         PRINT_USAGE_AND_EXIT;
563                     }
564                     for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
565                         if (type != SkDrawFilter::kTypeCount && index != type) {
566                             continue;
567                         }
568                         drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
569                                 (drawFilters[index] | flag);
570                     }
571                 } else {
572                     SkString err;
573                     err.printf("Unknown arg for --filter %s : missing colon\n", *argv);
574                     gLogger.logError(err);
575                     PRINT_USAGE_AND_EXIT;
576                 }
577             } else {
578                 gLogger.logError("Missing arg for --filter\n");
579                 PRINT_USAGE_AND_EXIT;
580             }
581         } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
582             PRINT_USAGE_AND_EXIT;
583         } else {
584             inputs->push_back(SkString(*argv));
585         }
586     }
587
588     if (numThreads > 1 && !useTiles) {
589         gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
590         PRINT_USAGE_AND_EXIT;
591     }
592
593     if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
594         gLogger.logError("--pipe and --bbh cannot be used together\n");
595         PRINT_USAGE_AND_EXIT;
596     }
597
598     if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
599         !gridSupported) {
600         gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n");
601         PRINT_USAGE_AND_EXIT;
602     }
603
604     if (useTiles) {
605         SkASSERT(NULL == renderer);
606         sk_tools::TiledPictureRenderer* tiledRenderer;
607         if (isCopyMode) {
608             int x, y;
609             if (xTilesString != NULL) {
610                 SkASSERT(yTilesString != NULL);
611                 x = atoi(xTilesString);
612                 y = atoi(yTilesString);
613                 if (x <= 0 || y <= 0) {
614                     gLogger.logError("--tiles must be given values > 0\n");
615                     PRINT_USAGE_AND_EXIT;
616                 }
617             } else {
618                 x = y = 4;
619             }
620             tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
621             if (benchmark->timeIndividualTiles()) {
622                 gLogger.logError("timeIndividualTiles is not compatible with copyTile\n");
623                 PRINT_USAGE_AND_EXIT;
624             }
625         } else if (numThreads > 1) {
626             tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
627         } else {
628             tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
629         }
630         if (isPowerOf2Mode) {
631             int minWidth = atoi(widthString);
632             if (!SkIsPow2(minWidth) || minWidth < 0) {
633                 tiledRenderer->unref();
634                 SkString err;
635                 err.printf("-mode %s must be given a width"
636                          " value that is a power of two\n", mode);
637                 gLogger.logError(err);
638                 PRINT_USAGE_AND_EXIT;
639             }
640             tiledRenderer->setTileMinPowerOf2Width(minWidth);
641         } else if (sk_tools::is_percentage(widthString)) {
642             if (isCopyMode) {
643                 tiledRenderer->unref();
644                 SkString err;
645                 err.printf("--mode %s does not support percentages.\n", mode);
646                 gLogger.logError(err.c_str());
647                 PRINT_USAGE_AND_EXIT;
648             }
649             tiledRenderer->setTileWidthPercentage(atof(widthString));
650             if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
651                 tiledRenderer->unref();
652                 SkString err;
653                 err.appendf("--mode %s must be given a width percentage > 0\n", mode);
654                 gLogger.logError(err);
655                 PRINT_USAGE_AND_EXIT;
656             }
657         } else {
658             tiledRenderer->setTileWidth(atoi(widthString));
659             if (!(tiledRenderer->getTileWidth() > 0)) {
660                 tiledRenderer->unref();
661                 SkString err;
662                 err.appendf("--mode %s must be given a width > 0\n", mode);
663                 gLogger.logError(err);
664                 PRINT_USAGE_AND_EXIT;
665             }
666         }
667
668         if (sk_tools::is_percentage(heightString)) {
669             if (isCopyMode) {
670                 tiledRenderer->unref();
671                 SkString err;
672                 err.printf("--mode %s does not support percentages.\n", mode);
673                 gLogger.logError(err.c_str());
674                 PRINT_USAGE_AND_EXIT;
675             }
676             tiledRenderer->setTileHeightPercentage(atof(heightString));
677             if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
678                 tiledRenderer->unref();
679                 SkString err;
680                 err.appendf("--mode %s must be given a height percentage > 0\n", mode);
681                 gLogger.logError(err);
682                 PRINT_USAGE_AND_EXIT;
683             }
684         } else {
685             tiledRenderer->setTileHeight(atoi(heightString));
686             if (!(tiledRenderer->getTileHeight() > 0)) {
687                 tiledRenderer->unref();
688                 SkString err;
689                 err.appendf("--mode %s must be given a height > 0\n", mode);
690                 gLogger.logError(err);
691                 PRINT_USAGE_AND_EXIT;
692             }
693         }
694         if (numThreads > 1) {
695 #if SK_SUPPORT_GPU
696             if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
697                 tiledRenderer->unref();
698                 gLogger.logError("GPU not compatible with multithreaded tiling.\n");
699                 PRINT_USAGE_AND_EXIT;
700             }
701 #endif
702         }
703         renderer.reset(tiledRenderer);
704         if (usePipe) {
705             gLogger.logError("Pipe rendering is currently not compatible with tiling.\n"
706                      "Turning off pipe.\n");
707         }
708     } else {
709         if (benchmark->timeIndividualTiles()) {
710             gLogger.logError("timeIndividualTiles requires tiled rendering.\n");
711             PRINT_USAGE_AND_EXIT;
712         }
713         if (usePipe) {
714             if (renderer.get() != NULL) {
715                 gLogger.logError("Pipe is incompatible with other modes.\n");
716                 PRINT_USAGE_AND_EXIT;
717             }
718             renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
719         }
720     }
721     if (inputs->count() < 1) {
722         PRINT_USAGE_AND_EXIT;
723     }
724
725     if (NULL == renderer) {
726         renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
727     }
728
729     renderer->setBBoxHierarchyType(bbhType);
730     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
731     renderer->setGridSize(gridWidth, gridHeight);
732     renderer->setViewport(viewport);
733     benchmark->setRenderer(renderer);
734     benchmark->setRepeats(repeats);
735     benchmark->setDeviceType(deviceType);
736     benchmark->setLogger(&gLogger);
737     // Report current settings:
738     gLogger.logProgress(commandLine);
739 }
740
741 static int process_input(const SkString& input,
742                          sk_tools::PictureBenchmark& benchmark) {
743     SkOSFile::Iter iter(input.c_str(), "skp");
744     SkString inputFilename;
745     int failures = 0;
746     if (iter.next(&inputFilename)) {
747         do {
748             SkString inputPath;
749             sk_tools::make_filepath(&inputPath, input, inputFilename);
750             if (!run_single_benchmark(inputPath, benchmark)) {
751                 ++failures;
752             }
753         } while(iter.next(&inputFilename));
754     } else if (SkStrEndsWith(input.c_str(), ".skp")) {
755         if (!run_single_benchmark(input, benchmark)) {
756             ++failures;
757         }
758     } else {
759         SkString warning;
760         warning.printf("Warning: skipping %s\n", input.c_str());
761         gLogger.logError(warning);
762     }
763     return failures;
764 }
765
766 int tool_main(int argc, char** argv);
767 int tool_main(int argc, char** argv) {
768 #ifdef SK_ENABLE_INST_COUNT
769     gPrintInstCount = true;
770 #endif
771     SkAutoGraphics ag;
772
773     SkTArray<SkString> inputs;
774     sk_tools::PictureBenchmark benchmark;
775
776     parse_commandline(argc, argv, &inputs, &benchmark);
777
778     int failures = 0;
779     for (int i = 0; i < inputs.count(); ++i) {
780         failures += process_input(inputs[i], benchmark);
781     }
782
783     if (failures != 0) {
784         SkString err;
785         err.printf("Failed to run %i benchmarks.\n", failures);
786         gLogger.logError(err);
787         return 1;
788     }
789     return 0;
790 }
791
792 #if !defined SK_BUILD_FOR_IOS
793 int main(int argc, char * const argv[]) {
794     return tool_main(argc, (char**) argv);
795 }
796 #endif