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