Cleanup: Rename SkOSPath functions.
[platform/upstream/libSkiaSharp.git] / tools / bench_pictures_main.cpp
index c1d0e1c..b740877 100644 (file)
@@ -5,21 +5,64 @@
  * found in the LICENSE file.
  */
 
-#include "BenchTimer.h"
+#include "BenchLogger.h"
+#include "Timer.h"
 #include "CopyTilesRenderer.h"
+#include "CrashHandler.h"
+#include "LazyDecodeBitmap.h"
 #include "PictureBenchmark.h"
-#include "SkBenchLogger.h"
-#include "SkCanvas.h"
+#include "PictureRenderingFlags.h"
+#include "PictureResultsWriter.h"
+#include "SkCommandLineFlags.h"
+#include "SkData.h"
+#include "SkDiscardableMemoryPool.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
 #include "SkMath.h"
 #include "SkOSFile.h"
 #include "SkPicture.h"
 #include "SkStream.h"
-#include "SkTArray.h"
 #include "picture_utils.h"
 
-const int DEFAULT_REPEATS = 1;
+BenchLogger gLogger;
+PictureResultsLoggerWriter gLogWriter(&gLogger);
+PictureResultsMultiWriter gWriter;
+
+// Flags used by this file, in alphabetical order.
+DEFINE_bool(countRAM, false, "Count the RAM used for bitmap pixels in each skp file");
+DECLARE_bool(deferImageDecoding);
+DEFINE_string(filter, "",
+        "type:flag : Enable canvas filtering to disable a paint flag, "
+        "use no blur or low quality blur, or use no hinting or "
+        "slight hinting. For all flags except AAClip, specify the "
+        "type of primitive to effect, or choose all. for AAClip "
+        "alone, the filter affects all clips independent of type. "
+        "Specific flags are listed above.");
+DEFINE_string(logFile, "", "Destination for writing log output, in addition to stdout.");
+DEFINE_bool(logPerIter, false, "Log each repeat timer instead of mean.");
+DEFINE_string(jsonLog, "", "Destination for writing JSON data.");
+DEFINE_bool(min, false, "Print the minimum times (instead of average).");
+DECLARE_string(readPath);
+DEFINE_int32(repeat, 1, "Set the number of times to repeat each test.");
+DEFINE_bool(timeIndividualTiles, false, "Report times for drawing individual tiles, rather than "
+            "times for drawing the whole page. Requires tiled rendering.");
+DEFINE_bool(purgeDecodedTex, false, "Purge decoded and GPU-uploaded textures "
+            "after each iteration.");
+DEFINE_string(timers, "c", "[wcgWC]*: Display wall, cpu, gpu, truncated wall or truncated cpu time"
+              " for each picture.");
+DEFINE_bool(trackDeferredCaching, false, "Only meaningful with --deferImageDecoding and "
+            "SK_LAZY_CACHE_STATS set to true. Report percentage of cache hits when using "
+            "deferred image decoding.");
+
+DEFINE_bool(preprocess, false, "If true, perform device specific preprocessing before timing.");
+
+// Buildbot-specific parameters
+DEFINE_string(builderName, "", "Name of the builder this is running on.");
+DEFINE_int32(buildNumber, -1, "Build number of the build this test is running on");
+DEFINE_int32(timestamp, 0, "Timestamp of the revision of Skia being tested.");
+DEFINE_string(gitHash, "", "Commit hash of the revision of Skia being run.");
+DEFINE_int32(gitNumber, -1, "Git number of the revision of Skia being run.");
+
 
 static char const * const gFilterTypes[] = {
     "paint",
@@ -27,6 +70,7 @@ static char const * const gFilterTypes[] = {
     "line",
     "bitmap",
     "rect",
+    "oval",
     "path",
     "text",
     "all",
@@ -50,7 +94,6 @@ static char const * const gFilterFlags[] = {
     "verticalText",
     "genA8FromLCD",
     "blur",
-    "lowBlur",
     "hinting",
     "slightHinting",
     "AAClip",
@@ -104,7 +147,7 @@ static SkString filterFlagsUsage() {
     for (size_t index = 0; index < kFilterFlagsCount; ++index) {
         result += gFilterFlags[index];
         if (result.size() - len >= 72) {
-            result += "\n           ";
+            result += "\n\t\t";
             len = result.size();
         }
         if (index < kFilterFlagsCount - 1) {
@@ -114,105 +157,10 @@ static SkString filterFlagsUsage() {
     return result;
 }
 
-static void usage(const char* argv0) {
-    SkDebugf("SkPicture benchmarking tool\n");
-    SkDebugf("\n"
-"Usage: \n"
-"     %s <inputDir>...\n"
-"     [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
-"     [--repeat] \n"
-"     [--mode pow2tile minWidth height | record | simple\n"
-"             | tile width height | playbackCreation]\n"
-"     [--pipe]\n"
-"     [--bbh bbhType]\n"
-"     [--multi numThreads]\n"
-"     [--device bitmap"
-#if SK_SUPPORT_GPU
-" | gpu"
-#endif
-"]\n"
-"     [--filter [%s]:\n            [%s]]\n"
-, argv0, filterTypesUsage().c_str(), filterFlagsUsage().c_str());
-    SkDebugf("\n");
-    SkDebugf(
-"     inputDir:  A list of directories and files to use as input. Files are\n"
-"                expected to have the .skp extension.\n\n"
-"     --logFile filename : destination for writing log output, in addition to stdout.\n");
-    SkDebugf("     --logPerIter 1|0 : "
-             "Log each repeat timer instead of mean, default is disabled.\n");
-    SkDebugf("     --min : Print the minimum times (instead of average).\n");
-    SkDebugf("     --timers [wcgWC]* : "
-             "Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
-    SkDebugf(
-"     --mode pow2tile minWidth height | copyTile width height | record | simple\n"
-"            | tile width height | playbackCreation:\n"
-"            Run in the corresponding mode.\n"
-"            Default is simple.\n");
-    SkDebugf(
-"                     pow2tile minWidth height, Creates tiles with widths\n"
-"                                                 that are all a power of two\n"
-"                                                 such that they minimize the\n"
-"                                                 amount of wasted tile space.\n"
-"                                                 minWidth is the minimum width\n"
-"                                                 of these tiles and must be a\n"
-"                                                 power of two. Simple\n"
-"                                                 rendering using these tiles\n"
-"                                                 is benchmarked.\n");
-    SkDebugf(
-"                     record, Benchmark picture to picture recording.\n");
-    SkDebugf(
-"                     simple, Benchmark a simple rendering.\n");
-    SkDebugf(
-"                     tile width height, Benchmark simple rendering using\n"
-"                                            tiles with the given dimensions.\n"
-"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
-"                                                Does not support percentages.\n"
-"                                                If the picture is large enough, breaks it into\n"
-"                                                larger tiles (and draws the picture once per\n"
-"                                                larger tile) to avoid creating a large canvas.\n"
-"                                                Add --tiles x y to specify the number of tiles\n"
-"                                                per larger tile in the x and y direction.\n"
-             );
-    SkDebugf(
-"                     playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
-    SkDebugf("\n");
-    SkDebugf(
-"     --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
-"                          than 1. Only works with tiled rendering.\n"
-"     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
-    SkDebugf(
-"     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
-"                     be used. Accepted values are: none, rtree, grid. Default\n"
-"                     value is none. Not compatible with --pipe. With value\n"
-"                     'grid', width and height must be specified. 'grid' can\n"
-"                     only be used with modes tile, record, and\n"
-"                     playbackCreation.");
-    SkDebugf(
-"     --device bitmap"
-#if SK_SUPPORT_GPU
-" | gpu"
+#if SK_LAZY_CACHE_STATS
+static int32_t gTotalCacheHits;
+static int32_t gTotalCacheMisses;
 #endif
-": Use the corresponding device. Default is bitmap.\n");
-    SkDebugf(
-"                     bitmap, Render to a bitmap.\n");
-#if SK_SUPPORT_GPU
-    SkDebugf(
-"                     gpu, Render to the GPU.\n");
-#endif
-    SkDebugf("\n");
-    SkDebugf(
-"     --repeat:  "
-"Set the number of times to repeat each test."
-" Default is %i.\n", DEFAULT_REPEATS);
-    SkDebugf(
-"     --filter type:flag : Enable canvas filtering to disable a paint flag,\n"
-"                     use no blur or low quality blur, or use no hinting or\n"
-"                     slight hinting. For all flags except AAClip, specify the\n"
-"                     type of primitive to effect, or choose all. for AAClip\n"
-"                     alone, the filter affects all clips independent of type.\n");
-}
-
-SkBenchLogger gLogger;
 
 static bool run_single_benchmark(const SkString& inputPath,
                                  sk_tools::PictureBenchmark& benchmark) {
@@ -226,503 +174,220 @@ static bool run_single_benchmark(const SkString& inputPath,
         return false;
     }
 
-    bool success = false;
-    SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
-    if (!success) {
+    SkDiscardableMemoryPool* pool = SkGetGlobalDiscardableMemoryPool();
+    // Since the old picture has been deleted, all pixels should be cleared.
+    SkASSERT(pool->getRAMUsed() == 0);
+    if (FLAGS_countRAM) {
+        pool->setRAMBudget(SK_MaxU32);
+        // Set the limit to max, so all pixels will be kept
+    }
+
+    SkPicture::InstallPixelRefProc proc;
+    if (FLAGS_deferImageDecoding) {
+        proc = &sk_tools::LazyDecodeBitmap;
+    } else {
+        proc = &SkImageDecoder::DecodeMemory;
+    }
+    SkAutoTUnref<SkPicture> picture(SkPicture::CreateFromStream(&inputStream, proc));
+
+    if (NULL == picture.get()) {
         SkString err;
         err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
         gLogger.logError(err);
         return false;
     }
 
-    SkString filename;
-    sk_tools::get_basename(&filename, inputPath);
-
-    SkString result;
-    result.printf("running bench [%i %i] %s ", picture.width(),
-                  picture.height(), filename.c_str());
-    gLogger.logProgress(result);
-
-    benchmark.run(&picture);
-    return true;
-}
-
-#define PRINT_USAGE_AND_EXIT \
-    do {                     \
-        usage(argv0);        \
-        exit(-1);            \
-    } while (0)
+    SkString filename = SkOSPath::Basename(inputPath.c_str());
 
-static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
-                              sk_tools::PictureBenchmark* benchmark) {
-    const char* argv0 = argv[0];
-    char* const* stop = argv + argc;
+    gWriter.bench(filename.c_str(), picture->width(), picture->height());
 
-    int repeats = DEFAULT_REPEATS;
-    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
-        sk_tools::PictureRenderer::kBitmap_DeviceType;
+    benchmark.run(picture);
 
-    SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
-
-    // Create a string to show our current settings.
-    // TODO: Make it prettier. Currently it just repeats the command line.
-    SkString commandLine("bench_pictures:");
-    for (int i = 1; i < argc; i++) {
-        commandLine.appendf(" %s", *(argv+i));
+#if SK_LAZY_CACHE_STATS
+    if (FLAGS_trackDeferredCaching) {
+        int cacheHits = pool->getCacheHits();
+        int cacheMisses = pool->getCacheMisses();
+        pool->resetCacheHitsAndMisses();
+        SkString hitString;
+        hitString.printf("Cache hit rate: %f\n", (double) cacheHits / (cacheHits + cacheMisses));
+        gLogger.logProgress(hitString);
+        gTotalCacheHits += cacheHits;
+        gTotalCacheMisses += cacheMisses;
     }
-    commandLine.append("\n");
-
-    bool usePipe = false;
-    int numThreads = 1;
-    bool useTiles = false;
-    const char* widthString = NULL;
-    const char* heightString = NULL;
-    int gridWidth = 0;
-    int gridHeight = 0;
-    bool isPowerOf2Mode = false;
-    bool isCopyMode = false;
-    const char* xTilesString = NULL;
-    const char* yTilesString = NULL;
-    const char* mode = NULL;
-    bool gridSupported = false;
-    sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
-        sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
-    sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
-    sk_bzero(drawFilters, sizeof(drawFilters));
-    for (++argv; argv < stop; ++argv) {
-        if (0 == strcmp(*argv, "--repeat")) {
-            ++argv;
-            if (argv < stop) {
-                repeats = atoi(*argv);
-                if (repeats < 1) {
-                    gLogger.logError("--repeat must be given a value > 0\n");
-                    PRINT_USAGE_AND_EXIT;
-                }
-            } else {
-                gLogger.logError("Missing arg for --repeat\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-        } else if (0 == strcmp(*argv, "--pipe")) {
-            usePipe = true;
-        } else if (0 == strcmp(*argv, "--logFile")) {
-            argv++;
-            if (argv < stop) {
-                if (!gLogger.SetLogFile(*argv)) {
-                    SkString str;
-                    str.printf("Could not open %s for writing.", *argv);
-                    gLogger.logError(str);
-                    usage(argv0);
-                    // TODO(borenet): We're disabling this for now, due to
-                    // write-protected Android devices.  The very short-term
-                    // solution is to ignore the fact that we have no log file.
-                    //exit(-1);
-                }
-            } else {
-                gLogger.logError("Missing arg for --logFile\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-        } else if (0 == strcmp(*argv, "--multi")) {
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing arg for --multi\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-            numThreads = atoi(*argv);
-            if (numThreads < 2) {
-                gLogger.logError("Number of threads must be at least 2.\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-        } else if (0 == strcmp(*argv, "--bbh")) {
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing value for --bbh\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-            if (0 == strcmp(*argv, "none")) {
-                bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
-            } else if (0 == strcmp(*argv, "rtree")) {
-                bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
-            } else if (0 == strcmp(*argv, "grid")) {
-                bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
-                ++argv;
-                if (argv >= stop) {
-                    gLogger.logError("Missing width for --bbh grid\n");
-                    PRINT_USAGE_AND_EXIT;
-                }
-                gridWidth = atoi(*argv);
-                ++argv;
-                if (argv >= stop) {
-                    gLogger.logError("Missing height for --bbh grid\n");
-                    PRINT_USAGE_AND_EXIT;
-                }
-                gridHeight = atoi(*argv);
+#endif
+    if (FLAGS_countRAM) {
+        SkString ramCount("RAM used for bitmaps: ");
+        size_t bytes = pool->getRAMUsed();
+        if (bytes > 1024) {
+            size_t kb = bytes / 1024;
+            if (kb > 1024) {
+                size_t mb = kb / 1024;
+                ramCount.appendf("%zi MB\n", mb);
             } else {
-                SkString err;
-                err.printf("%s is not a valid value for --bbhType\n", *argv);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
-
-        } else if (0 == strcmp(*argv, "--mode")) {
-            if (renderer.get() != NULL) {
-                SkDebugf("Cannot combine modes.\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing mode for --mode\n");
-                PRINT_USAGE_AND_EXIT;
+                ramCount.appendf("%zi KB\n", kb);
             }
+        } else {
+            ramCount.appendf("%zi bytes\n", bytes);
+        }
+        gLogger.logProgress(ramCount);
+    }
 
-            if (0 == strcmp(*argv, "record")) {
-                renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
-                gridSupported = true;
-            } else if (0 == strcmp(*argv, "simple")) {
-                renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
-            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
-                       || 0 == strcmp(*argv, "copyTile")) {
-                useTiles = true;
-                mode = *argv;
-
-                if (0 == strcmp(*argv, "pow2tile")) {
-                    isPowerOf2Mode = true;
-                } else if (0 == strcmp(*argv, "copyTile")) {
-                    isCopyMode = true;
-                } else {
-                    gridSupported = true;
-                }
+    return true;
+}
 
-                ++argv;
-                if (argv >= stop) {
-                    SkString err;
-                    err.printf("Missing width for --mode %s\n", mode);
-                    gLogger.logError(err);
-                    PRINT_USAGE_AND_EXIT;
-                }
+static void setup_benchmark(sk_tools::PictureBenchmark* benchmark) {
+    sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
+    sk_bzero(drawFilters, sizeof(drawFilters));
 
-                widthString = *argv;
-                ++argv;
-                if (argv >= stop) {
-                    SkString err;
-                    err.appendf("Missing height for --mode %s\n", mode);
-                    gLogger.logError(err);
-                    PRINT_USAGE_AND_EXIT;
+    if (FLAGS_filter.count() > 0) {
+        const char* filters = FLAGS_filter[0];
+        const char* colon = strchr(filters, ':');
+        if (colon) {
+            int32_t type = -1;
+            size_t typeLen = colon - filters;
+            for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
+                if (typeLen == strlen(gFilterTypes[tIndex])
+                        && !strncmp(filters, gFilterTypes[tIndex], typeLen)) {
+                    type = SkToS32(tIndex);
+                    break;
                 }
-                heightString = *argv;
-            } else if (0 == strcmp(*argv, "playbackCreation")) {
-                renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
-                gridSupported = true;
-            } else if (0 == strcmp(*argv, "gatherPixelRefs")) {
-                renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
-            } else {
-                SkString err;
-                err.printf("%s is not a valid mode for --mode\n", *argv);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
             }
-        } else if (0 == strcmp(*argv, "--tiles")) {
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing x for --tiles\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-            xTilesString = *argv;
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing y for --tiles\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-            yTilesString = *argv;
-        }  else if (0 == strcmp(*argv, "--device")) {
-            ++argv;
-            if (argv >= stop) {
-                gLogger.logError("Missing mode for --device\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-
-            if (0 == strcmp(*argv, "bitmap")) {
-                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
-            }
-#if SK_SUPPORT_GPU
-            else if (0 == strcmp(*argv, "gpu")) {
-                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
-            }
-#endif
-            else {
+            if (type < 0) {
                 SkString err;
-                err.printf("%s is not a valid mode for --device\n", *argv);
+                err.printf("Unknown type for --filter %s\n", filters);
                 gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
+                exit(-1);
             }
-        } else if (0 == strcmp(*argv, "--timers")) {
-            ++argv;
-            if (argv < stop) {
-                bool timerWall = false;
-                bool truncatedTimerWall = false;
-                bool timerCpu = false;
-                bool truncatedTimerCpu = false;
-                bool timerGpu = false;
-                for (char* t = *argv; *t; ++t) {
-                    switch (*t) {
-                        case 'w':
-                            timerWall = true;
-                            break;
-                        case 'c':
-                            timerCpu = true;
-                            break;
-                        case 'W':
-                            truncatedTimerWall = true;
-                            break;
-                        case 'C':
-                            truncatedTimerCpu = true;
-                            break;
-                        case 'g':
-                            timerGpu = true;
-                            break;
-                        default: {
-                            break;
-                        }
-                    }
+            int flag = -1;
+            size_t flagLen = strlen(filters) - typeLen - 1;
+            for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
+                if (flagLen == strlen(gFilterFlags[fIndex])
+                        && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
+                    flag = 1 << fIndex;
+                    break;
                 }
-                benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu,
-                                           truncatedTimerCpu, timerGpu);
-            } else {
-                gLogger.logError("Missing arg for --timers\n");
-                PRINT_USAGE_AND_EXIT;
             }
-        } else if (0 == strcmp(*argv, "--min")) {
-            benchmark->setPrintMin(true);
-        } else if (0 == strcmp(*argv, "--logPerIter")) {
-            ++argv;
-            if (argv < stop) {
-                bool log = atoi(*argv) != 0;
-                benchmark->setLogPerIter(log);
-            } else {
-                gLogger.logError("Missing arg for --logPerIter\n");
-                PRINT_USAGE_AND_EXIT;
+            if (flag < 0) {
+                SkString err;
+                err.printf("Unknown flag for --filter %s\n", filters);
+                gLogger.logError(err);
+                exit(-1);
             }
-        } else if (0 == strcmp(*argv, "--filter")) {
-            ++argv;
-            if (argv < stop) {
-                const char* colon = strchr(*argv, ':');
-                if (colon) {
-                    int type = -1;
-                    size_t typeLen = colon - *argv;
-                    for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
-                        if (typeLen == strlen(gFilterTypes[tIndex])
-                                && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) {
-                            type = tIndex;
-                            break;
-                        }
-                    }
-                    if (type < 0) {
-                        SkString err;
-                        err.printf("Unknown type for --filter %s\n", *argv);
-                        gLogger.logError(err);
-                        PRINT_USAGE_AND_EXIT;
-                    }
-                    int flag = -1;
-                    size_t flagLen = strlen(*argv) - typeLen - 1;
-                    for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
-                        if (flagLen == strlen(gFilterFlags[fIndex])
-                                && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
-                            flag = 1 << fIndex;
-                            break;
-                        }
-                    }
-                    if (flag < 0) {
-                        SkString err;
-                        err.printf("Unknown flag for --filter %s\n", *argv);
-                        gLogger.logError(err);
-                        PRINT_USAGE_AND_EXIT;
-                    }
-                    for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
-                        if (type != SkDrawFilter::kTypeCount && index != type) {
-                            continue;
-                        }
-                        drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
-                                (drawFilters[index] | flag);
-                    }
-                } else {
-                    SkString err;
-                    err.printf("Unknown arg for --filter %s : missing colon\n", *argv);
-                    gLogger.logError(err);
-                    PRINT_USAGE_AND_EXIT;
+            for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
+                if (type != SkDrawFilter::kTypeCount && index != type) {
+                    continue;
                 }
-            } else {
-                gLogger.logError("Missing arg for --filter\n");
-                PRINT_USAGE_AND_EXIT;
+                drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
+                        (drawFilters[index] | flag);
             }
-        } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
-            PRINT_USAGE_AND_EXIT;
         } else {
-            inputs->push_back(SkString(*argv));
+            SkString err;
+            err.printf("Unknown arg for --filter %s : missing colon\n", filters);
+            gLogger.logError(err);
+            exit(-1);
         }
     }
 
-    if (numThreads > 1 && !useTiles) {
-        gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
-        PRINT_USAGE_AND_EXIT;
+    if (FLAGS_timers.count() > 0) {
+        size_t index = 0;
+        bool timerWall = false;
+        bool truncatedTimerWall = false;
+        bool timerCpu = false;
+        bool truncatedTimerCpu = false;
+        bool timerGpu = false;
+        while (index < strlen(FLAGS_timers[0])) {
+            switch (FLAGS_timers[0][index]) {
+                case 'w':
+                    timerWall = true;
+                    break;
+                case 'c':
+                    timerCpu = true;
+                    break;
+                case 'W':
+                    truncatedTimerWall = true;
+                    break;
+                case 'C':
+                    truncatedTimerCpu = true;
+                    break;
+                case 'g':
+                    timerGpu = true;
+                    break;
+                default:
+                    SkDebugf("mystery character\n");
+                    break;
+            }
+            index++;
+        }
+        benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu, truncatedTimerCpu,
+                                  timerGpu);
     }
 
-    if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
-        gLogger.logError("--pipe and --bbh cannot be used together\n");
-        PRINT_USAGE_AND_EXIT;
+    SkString errorString;
+    SkAutoTUnref<sk_tools::PictureRenderer> renderer(parseRenderer(errorString,
+                                                                   kBench_PictureTool));
+
+    if (errorString.size() > 0) {
+        gLogger.logError(errorString);
     }
 
-    if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
-        !gridSupported) {
-        gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n");
-        PRINT_USAGE_AND_EXIT;
+    if (NULL == renderer.get()) {
+        exit(-1);
     }
 
-    if (useTiles) {
-        SkASSERT(NULL == renderer);
-        sk_tools::TiledPictureRenderer* tiledRenderer;
-        if (isCopyMode) {
-            int x, y;
-            if (xTilesString != NULL) {
-                SkASSERT(yTilesString != NULL);
-                x = atoi(xTilesString);
-                y = atoi(yTilesString);
-                if (x <= 0 || y <= 0) {
-                    gLogger.logError("--tiles must be given values > 0\n");
-                    PRINT_USAGE_AND_EXIT;
-                }
-            } else {
-                x = y = 4;
-            }
-            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
-        } else if (numThreads > 1) {
-            tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
-        } else {
-            tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
+    if (FLAGS_timeIndividualTiles) {
+        sk_tools::TiledPictureRenderer* tiledRenderer = renderer->getTiledRenderer();
+        if (NULL == tiledRenderer) {
+            gLogger.logError("--timeIndividualTiles requires tiled rendering.\n");
+            exit(-1);
         }
-        if (isPowerOf2Mode) {
-            int minWidth = atoi(widthString);
-            if (!SkIsPow2(minWidth) || minWidth < 0) {
-                tiledRenderer->unref();
-                SkString err;
-                err.printf("-mode %s must be given a width"
-                         " value that is a power of two\n", mode);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
-            tiledRenderer->setTileMinPowerOf2Width(minWidth);
-        } else if (sk_tools::is_percentage(widthString)) {
-            if (isCopyMode) {
-                tiledRenderer->unref();
-                SkString err;
-                err.printf("--mode %s does not support percentages.\n", mode);
-                gLogger.logError(err.c_str());
-                PRINT_USAGE_AND_EXIT;
-            }
-            tiledRenderer->setTileWidthPercentage(atof(widthString));
-            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
-                tiledRenderer->unref();
-                SkString err;
-                err.appendf("--mode %s must be given a width percentage > 0\n", mode);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
-        } else {
-            tiledRenderer->setTileWidth(atoi(widthString));
-            if (!(tiledRenderer->getTileWidth() > 0)) {
-                tiledRenderer->unref();
-                SkString err;
-                err.appendf("--mode %s must be given a width > 0\n", mode);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
+        if (!tiledRenderer->supportsTimingIndividualTiles()) {
+            gLogger.logError("This renderer does not support --timeIndividualTiles.\n");
+            exit(-1);
         }
-
-        if (sk_tools::is_percentage(heightString)) {
-            if (isCopyMode) {
-                tiledRenderer->unref();
-                SkString err;
-                err.printf("--mode %s does not support percentages.\n", mode);
-                gLogger.logError(err.c_str());
-                PRINT_USAGE_AND_EXIT;
-            }
-            tiledRenderer->setTileHeightPercentage(atof(heightString));
-            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
-                tiledRenderer->unref();
-                SkString err;
-                err.appendf("--mode %s must be given a height percentage > 0\n", mode);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
-        } else {
-            tiledRenderer->setTileHeight(atoi(heightString));
-            if (!(tiledRenderer->getTileHeight() > 0)) {
-                tiledRenderer->unref();
-                SkString err;
-                err.appendf("--mode %s must be given a height > 0\n", mode);
-                gLogger.logError(err);
-                PRINT_USAGE_AND_EXIT;
-            }
-        }
-        if (numThreads > 1) {
-#if SK_SUPPORT_GPU
-            if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
-                tiledRenderer->unref();
-                gLogger.logError("GPU not compatible with multithreaded tiling.\n");
-                PRINT_USAGE_AND_EXIT;
-            }
-#endif
-        }
-        renderer.reset(tiledRenderer);
-        if (usePipe) {
-            SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
-                     "Turning off pipe.\n");
-        }
-    } else if (usePipe) {
-        if (renderer.get() != NULL) {
-            SkDebugf("Pipe is incompatible with other modes.\n");
-            PRINT_USAGE_AND_EXIT;
-        }
-        renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
-    }
-    if (inputs->count() < 1) {
-        PRINT_USAGE_AND_EXIT;
+        benchmark->setTimeIndividualTiles(true);
     }
 
-    if (NULL == renderer) {
-        renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
+    benchmark->setPurgeDecodedTex(FLAGS_purgeDecodedTex);
+    benchmark->setPreprocess(FLAGS_preprocess);
+
+    if (FLAGS_readPath.count() < 1) {
+        gLogger.logError(".skp files or directories are required.\n");
+        exit(-1);
     }
 
-    renderer->setBBoxHierarchyType(bbhType);
     renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
-    renderer->setGridSize(gridWidth, gridHeight);
+    if (FLAGS_logPerIter) {
+        benchmark->setTimerResultType(TimerData::kPerIter_Result);
+    } else if (FLAGS_min) {
+        benchmark->setTimerResultType(TimerData::kMin_Result);
+    } else {
+        benchmark->setTimerResultType(TimerData::kAvg_Result);
+    }
     benchmark->setRenderer(renderer);
-    benchmark->setRepeats(repeats);
-    benchmark->setDeviceType(deviceType);
-    benchmark->setLogger(&gLogger);
-    // Report current settings:
-    gLogger.logProgress(commandLine);
+    benchmark->setRepeats(FLAGS_repeat);
+    benchmark->setWriter(&gWriter);
 }
 
-static int process_input(const SkString& input,
+static int process_input(const char* input,
                          sk_tools::PictureBenchmark& benchmark) {
-    SkOSFile::Iter iter(input.c_str(), "skp");
+    SkString inputAsSkString(input);
+    SkOSFile::Iter iter(input, "skp");
     SkString inputFilename;
     int failures = 0;
     if (iter.next(&inputFilename)) {
         do {
-            SkString inputPath;
-            sk_tools::make_filepath(&inputPath, input, inputFilename);
+            SkString inputPath = SkOSPath::Join(input, inputFilename.c_str());
             if (!run_single_benchmark(inputPath, benchmark)) {
                 ++failures;
             }
         } while(iter.next(&inputFilename));
-    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
-        if (!run_single_benchmark(input, benchmark)) {
+    } else if (SkStrEndsWith(input, ".skp")) {
+        if (!run_single_benchmark(inputAsSkString, benchmark)) {
             ++failures;
         }
     } else {
         SkString warning;
-        warning.printf("Warning: skipping %s\n", input.c_str());
+        warning.printf("Warning: skipping %s\n", input);
         gLogger.logError(warning);
     }
     return failures;
@@ -730,19 +395,61 @@ static int process_input(const SkString& input,
 
 int tool_main(int argc, char** argv);
 int tool_main(int argc, char** argv) {
-#ifdef SK_ENABLE_INST_COUNT
+    SetupCrashHandler();
+    SkString usage;
+    usage.printf("Time drawing .skp files.\n"
+                 "\tPossible arguments for --filter: [%s]\n\t\t[%s]",
+                 filterTypesUsage().c_str(), filterFlagsUsage().c_str());
+    SkCommandLineFlags::SetUsage(usage.c_str());
+    SkCommandLineFlags::Parse(argc, argv);
+
+    if (FLAGS_repeat < 1) {
+        SkString error;
+        error.printf("--repeats must be >= 1. Was %i\n", FLAGS_repeat);
+        gLogger.logError(error);
+        exit(-1);
+    }
+
+    if (FLAGS_logFile.count() == 1) {
+        if (!gLogger.SetLogFile(FLAGS_logFile[0])) {
+            SkString str;
+            str.printf("Could not open %s for writing.\n", FLAGS_logFile[0]);
+            gLogger.logError(str);
+            // TODO(borenet): We're disabling this for now, due to
+            // write-protected Android devices.  The very short-term
+            // solution is to ignore the fact that we have no log file.
+            //exit(-1);
+        }
+    }
+
+    SkAutoTDelete<PictureJSONResultsWriter> jsonWriter;
+    if (FLAGS_jsonLog.count() == 1) {
+        SkASSERT(FLAGS_builderName.count() == 1 && FLAGS_gitHash.count() == 1);
+        jsonWriter.reset(SkNEW(PictureJSONResultsWriter(
+                        FLAGS_jsonLog[0],
+                        FLAGS_builderName[0],
+                        FLAGS_buildNumber,
+                        FLAGS_timestamp,
+                        FLAGS_gitHash[0],
+                        FLAGS_gitNumber)));
+        gWriter.add(jsonWriter.get());
+    }
+
+    gWriter.add(&gLogWriter);
+
+
+#if SK_ENABLE_INST_COUNT
     gPrintInstCount = true;
 #endif
     SkAutoGraphics ag;
 
-    SkTArray<SkString> inputs;
     sk_tools::PictureBenchmark benchmark;
 
-    parse_commandline(argc, argv, &inputs, &benchmark);
+    setup_benchmark(&benchmark);
 
     int failures = 0;
-    for (int i = 0; i < inputs.count(); ++i) {
-        failures += process_input(inputs[i], benchmark);
+    for (int i = 0; i < FLAGS_readPath.count(); ++i) {
+        failures += process_input(FLAGS_readPath[i], benchmark);
     }
 
     if (failures != 0) {
@@ -751,6 +458,13 @@ int tool_main(int argc, char** argv) {
         gLogger.logError(err);
         return 1;
     }
+#if SK_LAZY_CACHE_STATS
+    if (FLAGS_trackDeferredCaching) {
+        SkDebugf("Total cache hit rate: %f\n",
+                 (double) gTotalCacheHits / (gTotalCacheHits + gTotalCacheMisses));
+    }
+#endif
+    gWriter.end();
     return 0;
 }