Perform multi core rendering in bench_pictures.

Add a flag in SkGPipeWriter for threadsafe drawing.

Add a deferred pipe controller to SamplePipeControllers, which can
be called to play back in multiple threads.

Depends on http://codereview.appspot.com/6459105/

Review URL: https://codereview.appspot.com/6482068

git-svn-id: http://skia.googlecode.com/svn/trunk@5371 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 347812f..02be7cf 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -125,12 +125,14 @@
         '../src/pipe/utils/SamplePipeControllers.cpp',
      ],
      'include_dirs': [
-       '../src/pipe/utils',
+       '../src/pipe/utils/',
+       '../src/utils/',
      ],
      'dependencies': [
         'core.gyp:core',
         'images.gyp:images',
         'tools.gyp:picture_utils',
+        'utils.gyp:utils',
      ],
      'export_dependent_settings': [
        'images.gyp:images',
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index e4c8bc4..3570946 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -93,10 +93,10 @@
 template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
 public:
     SkAutoTDeleteArray(T array[]) : fArray(array) {}
-    ~SkAutoTDeleteArray() { delete[] fArray; }
+    ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); }
 
     T*      get() const { return fArray; }
-    void    free() { delete[] fArray; fArray = NULL; }
+    void    free() { SkDELETE_ARRAY(fArray); fArray = NULL; }
     T*      detach() { T* array = fArray; fArray = NULL; return array; }
 
 private:
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
index bbe504a..f1fc4b0 100644
--- a/include/pipe/SkGPipe.h
+++ b/include/pipe/SkGPipe.h
@@ -95,12 +95,19 @@
          *  (for example) we cannot put function pointers in the stream.
          */
         kCrossProcess_Flag              = 1 << 0,
+
         /**
          *  Only meaningful if kCrossProcess_Flag is set. Tells the writer that
          *  in spite of being cross process, it will have shared address space
-         *  with the reader, so the two can share large objects (like SkBitmaps)
+         *  with the reader, so the two can share large objects (like SkBitmaps).
          */
-        kSharedAddressSpace_Flag        = 1 << 1
+        kSharedAddressSpace_Flag        = 1 << 1,
+
+        /**
+         *  Tells the writer that there will be multiple threads reading the stream
+         *  simultaneously.
+         */
+        kSimultaneousReaders_Flag       = 1 << 2,
     };
 
     SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0,
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index ce83c83..803685a 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -426,17 +426,32 @@
 private:
     SkBitmapHeapEntry* fHeapEntry;
     const SkBitmap*    fBitmap;
+    SkBitmap           fBitmapStorage;
 };
 
 BitmapHolder::BitmapHolder(SkReader32* reader, uint32_t op32,
                            SkGPipeState* state) {
-    unsigned index = DrawOp_unpackData(op32);
-    if (shouldFlattenBitmaps(state->getFlags())) {
+    const unsigned flags = state->getFlags();
+    const unsigned index = DrawOp_unpackData(op32);
+    if (shouldFlattenBitmaps(flags)) {
         fHeapEntry = NULL;
         fBitmap = state->getBitmap(index);
     } else {
-        fHeapEntry = state->getSharedHeap()->getEntry(index);
-        fBitmap = fHeapEntry->getBitmap();
+        SkBitmapHeapEntry* entry = state->getSharedHeap()->getEntry(index);
+        if (SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag)) {
+            // Make a shallow copy for thread safety. Each thread will point to the same SkPixelRef,
+            // which is thread safe.
+            fBitmapStorage = *entry->getBitmap();
+            fBitmap = &fBitmapStorage;
+            // Release the ref on the bitmap now, since we made our own copy.
+            entry->releaseRef();
+            fHeapEntry = NULL;
+        } else {
+            SkASSERT(!shouldFlattenBitmaps(flags));
+            SkASSERT(!SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag));
+            fHeapEntry = entry;
+            fBitmap = fHeapEntry->getBitmap();
+        }
     }
 }
 
diff --git a/src/pipe/utils/SamplePipeControllers.cpp b/src/pipe/utils/SamplePipeControllers.cpp
index 740d1b8..c54bd13 100644
--- a/src/pipe/utils/SamplePipeControllers.cpp
+++ b/src/pipe/utils/SamplePipeControllers.cpp
@@ -76,3 +76,40 @@
     }
     this->INHERITED::notifyWritten(bytes);
 }
+
+////////////////////////////////////////////////////////////////////////////////
+
+DeferredPipeController::DeferredPipeController(int numberOfReaders)
+: fAllocator(kMinBlockSize)
+, fNumberOfReaders(numberOfReaders) {
+    fBlock = NULL;
+    fBytesWritten = 0;
+}
+
+void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
+    if (fBlock) {
+        // Save the previous block for later
+        PipeBlock previousBloc(fBlock, fBytesWritten);
+        fBlockList.push(previousBloc);
+    }
+    int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
+    fBlock = fAllocator.allocThrow(blockSize);
+    fBytesWritten = 0;
+    *actual = blockSize;
+    return fBlock;
+}
+
+void DeferredPipeController::notifyWritten(size_t bytes) {
+    fBytesWritten += bytes;
+}
+
+void DeferredPipeController::playback(SkCanvas* target) {
+    SkGPipeReader reader(target);
+    for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
+        reader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fBytes);
+    }
+
+    if (fBlock) {
+        reader.playback(fBlock, fBytesWritten);
+    }
+}
diff --git a/src/pipe/utils/SamplePipeControllers.h b/src/pipe/utils/SamplePipeControllers.h
index 715693c..bbd8024 100644
--- a/src/pipe/utils/SamplePipeControllers.h
+++ b/src/pipe/utils/SamplePipeControllers.h
@@ -6,7 +6,9 @@
  */
 
 #include "SkBitmap.h"
+#include "SkChunkAlloc.h"
 #include "SkGPipe.h"
+#include "SkTDArray.h"
 
 class SkCanvas;
 class SkMatrix;
@@ -43,3 +45,41 @@
     SkBitmap fBitmaps[NumberOfTiles];
     typedef PipeController INHERITED;
 };
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Borrowed (and modified) from SkDeferredCanvas.cpp::DeferredPipeController.
+ * Allows playing back from multiple threads.
+ */
+class DeferredPipeController : public SkGPipeController {
+public:
+    DeferredPipeController(int numberOfReaders);
+    virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+    virtual int numberOfReaders() const SK_OVERRIDE { return fNumberOfReaders; }
+
+    /**
+     * Play the stored drawing commands to the specified canvas. If SkGPipeWriter::startRecording
+     * used the flag SkGPipeWriter::kSimultaneousReaders_Flag, this can be called from different
+     * threads simultaneously.
+     */
+    void playback(SkCanvas*);
+private:
+    enum {
+        kMinBlockSize = 4096
+    };
+    struct PipeBlock {
+        PipeBlock(void* block, size_t bytes) { fBlock = block, fBytes = bytes; }
+        // Stream of draw commands written by the SkGPipeWriter. Allocated by fAllocator, which will
+        // handle freeing it.
+        void* fBlock;
+        // Number of bytes that were written to fBlock.
+        size_t fBytes;
+    };
+    void* fBlock;
+    size_t fBytesWritten;
+    SkChunkAlloc fAllocator;
+    SkTDArray<PipeBlock> fBlockList;
+    int fNumberOfReaders;
+};
diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp
index d24bb8b..29152f8 100644
--- a/tools/PictureBenchmark.cpp
+++ b/tools/PictureBenchmark.cpp
@@ -194,13 +194,16 @@
     }
 
     SkString result;
+    if (fRenderer.isMultiThreaded()) {
+        result.printf("multithreaded using %s ", (fRenderer.isUsePipe() ? "pipe" : "picture"));
+    }
     if (fRenderer.getTileMinPowerOf2Width() > 0) {
-        result.printf("%i_pow2tiles_%iminx%i: msecs = %6.2f", fRenderer.numTiles(),
-                      fRenderer.getTileMinPowerOf2Width(), fRenderer.getTileHeight(),
-                      wall_time / fRepeats);
+        result.appendf("%i_pow2tiles_%iminx%i: msecs = %6.2f", fRenderer.numTiles(),
+                       fRenderer.getTileMinPowerOf2Width(), fRenderer.getTileHeight(),
+                       wall_time / fRepeats);
     } else {
-        result.printf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(),
-                      fRenderer.getTileWidth(), fRenderer.getTileHeight(), wall_time / fRepeats);
+        result.appendf("%i_tiles_%ix%i: msecs = %6.2f", fRenderer.numTiles(),
+                       fRenderer.getTileWidth(), fRenderer.getTileHeight(), wall_time / fRepeats);
     }
 #if SK_SUPPORT_GPU
     if (fRenderer.isUsingGpuDevice()) {
diff --git a/tools/PictureBenchmark.h b/tools/PictureBenchmark.h
index f020c8d..2a14ea2 100644
--- a/tools/PictureBenchmark.h
+++ b/tools/PictureBenchmark.h
@@ -125,6 +125,14 @@
         return fRenderer.getTileMinPowerOf2Width();
     }
 
+    void setThreading(bool multi) {
+        fRenderer.setMultiThreaded(multi);
+    }
+
+    void setUsePipe(bool usePipe) {
+        fRenderer.setUsePipe(usePipe);
+    }
+
 private:
     TiledPictureRenderer fRenderer;
     typedef PictureBenchmark INHERITED;
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
index b7958e2..d5b72ab 100644
--- a/tools/PictureRenderer.cpp
+++ b/tools/PictureRenderer.cpp
@@ -6,22 +6,24 @@
  */
 
 #include "PictureRenderer.h"
+#include "picture_utils.h"
 #include "SamplePipeControllers.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
-#include "SkImageEncoder.h"
 #include "SkGPipe.h"
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#endif
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
 #include "SkMatrix.h"
 #include "SkPicture.h"
 #include "SkScalar.h"
 #include "SkString.h"
+#include "SkTemplates.h"
 #include "SkTDArray.h"
+#include "SkThreadUtils.h"
 #include "SkTypes.h"
-#include "picture_utils.h"
-
-#if SK_SUPPORT_GPU
-#include "SkGpuDevice.h"
-#endif
 
 namespace sk_tools {
 
@@ -156,7 +158,9 @@
 }
 
 TiledPictureRenderer::TiledPictureRenderer()
-    : fTileWidth(kDefaultTileWidth)
+    : fMultiThreaded(false)
+    , fUsePipe(false)
+    , fTileWidth(kDefaultTileWidth)
     , fTileHeight(kDefaultTileHeight)
     , fTileMinPowerOf2Width(0)
     , fTileHeightPercentage(0.0)
@@ -236,7 +240,7 @@
 // constraints are that every tile must have a pixel width that is a power of
 // two and also be of some minimal width (that is also a power of two).
 //
-// This is sovled by first taking our picture size and rounding it up to the
+// This is solved by first taking our picture size and rounding it up to the
 // multiple of the minimal width. The binary representation of this rounded
 // value gives us the tiles we need: a bit of value one means we need a tile of
 // that size.
@@ -275,9 +279,93 @@
     fTiles.reset();
 }
 
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Draw using Pipe
+
+struct TileData {
+    TileData(SkCanvas* canvas, DeferredPipeController* controller);
+    SkCanvas* fCanvas;
+    DeferredPipeController* fController;
+    SkThread fThread;
+};
+
+static void DrawTile(void* data) {
+    SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
+    TileData* tileData = static_cast<TileData*>(data);
+    tileData->fController->playback(tileData->fCanvas);
+}
+
+TileData::TileData(SkCanvas* canvas, DeferredPipeController* controller)
+: fCanvas(canvas)
+, fController(controller)
+, fThread(&DrawTile, static_cast<void*>(this)) {}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Draw using Picture
+
+struct CloneData {
+    CloneData(SkCanvas* target, SkPicture* original);
+    SkCanvas* fCanvas;
+    SkPicture* fClone;
+    SkThread fThread;
+};
+
+static void DrawClonedTile(void* data) {
+    SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
+    CloneData* cloneData = static_cast<CloneData*>(data);
+    cloneData->fCanvas->drawPicture(*cloneData->fClone);
+}
+
+CloneData::CloneData(SkCanvas* target, SkPicture* clone)
+: fCanvas(target)
+, fClone(clone)
+, fThread(&DrawClonedTile, static_cast<void*>(this)) {}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
 void TiledPictureRenderer::drawTiles() {
-    for (int i = 0; i < fTiles.count(); ++i) {
-        fTiles[i]->drawPicture(*(fPicture));
+    if (fMultiThreaded) {
+        if (fUsePipe) {
+            // First, draw into a pipe controller
+            SkGPipeWriter writer;
+            DeferredPipeController controller(fTiles.count());
+            SkCanvas* pipeCanvas = writer.startRecording(&controller,
+                                                         SkGPipeWriter::kSimultaneousReaders_Flag);
+            pipeCanvas->drawPicture(*(fPicture));
+            writer.endRecording();
+
+            // Create and start the threads.
+            TileData* tileData[fTiles.count()];
+            for (int i = 0; i < fTiles.count(); i++) {
+                tileData[i] = SkNEW_ARGS(TileData, (fTiles[i], &controller));
+                if (!tileData[i]->fThread.start()) {
+                    SkDebugf("could not start thread %i\n", i);
+                }
+            }
+            for (int i = 0; i < fTiles.count(); i++) {
+                tileData[i]->fThread.join();
+                SkDELETE(tileData[i]);
+            }
+        } else {
+            SkPicture* clones = SkNEW_ARRAY(SkPicture, fTiles.count());
+            SkAutoTDeleteArray<SkPicture> autodelete(clones);
+            fPicture->clone(clones, fTiles.count());
+            CloneData* cloneData[fTiles.count()];
+            for (int i = 0; i < fTiles.count(); i++) {
+                cloneData[i] = SkNEW_ARGS(CloneData, (fTiles[i], &clones[i]));
+                if (!cloneData[i]->fThread.start()) {
+                    SkDebugf("Could not start picture thread %i\n", i);
+                }
+            }
+            for (int i = 0; i < fTiles.count(); i++) {
+                cloneData[i]->fThread.join();
+                SkDELETE(cloneData[i]);
+            }
+        }
+    } else {
+        for (int i = 0; i < fTiles.count(); ++i) {
+            fTiles[i]->drawPicture(*(fPicture));
+        }
     }
 }
 
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
index 25e2ba4..c901903 100644
--- a/tools/PictureRenderer.h
+++ b/tools/PictureRenderer.h
@@ -163,12 +163,30 @@
         return fTiles.count();
     }
 
+    void setMultiThreaded(bool multi) {
+        fMultiThreaded = multi;
+    }
+
+    bool isMultiThreaded() const {
+        return fMultiThreaded;
+    }
+
+    void setUsePipe(bool usePipe) {
+        fUsePipe = usePipe;
+    }
+
+    bool isUsePipe() const {
+        return fUsePipe;
+    }
+
     ~TiledPictureRenderer();
 
 protected:
     virtual void finishDraw();
 
 private:
+    bool fMultiThreaded;
+    bool fUsePipe;
     int fTileWidth;
     int fTileHeight;
     double fTileWidthPercentage;
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
index 1579f69..084a971 100644
--- a/tools/bench_pictures_main.cpp
+++ b/tools/bench_pictures_main.cpp
@@ -23,8 +23,9 @@
 "Usage: \n"
 "     %s <inputDir>...\n"
 "     [--repeat] \n"
-"     [--mode pipe | pow2tile minWidth height[%] | record | simple\n"
-"         | tile width[%] height[%] | unflatten]\n"
+"     [--mode pow2tile minWidth height[] (multi) | record | simple\n"
+"             | tile width[] height[] (multi) | unflatten]\n"
+"     [--pipe]\n"
 "     [--device bitmap"
 #if SK_SUPPORT_GPU
 " | gpu"
@@ -36,32 +37,37 @@
 "     inputDir:  A list of directories and files to use as input. Files are\n"
 "                expected to have the .skp extension.\n\n");
     SkDebugf(
-"     --mode pipe | pow2tile minWidht height[%] | record | simple\n"
-"        | tile width[%] height[%] | unflatten: Run in the corresponding mode.\n"
-"                                               Default is simple.\n");
+"     --mode pow2tile minWidht height[] (multi) | record | simple\n"
+"            | tile width[] height[] (multi) | unflatten:\n"
+"            Run in the corresponding mode.\n"
+"            Default is simple.\n");
     SkDebugf(
-"                     pipe, Benchmark SkGPipe rendering.\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");
+"                     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"
+"                                                 Append \"multi\" for multithreaded\n"
+"                                                 drawing.\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");
+"                     tile width[] height[], Benchmark simple rendering using\n"
+"                                            tiles with the given dimensions.\n"
+"                                            Append \"multi\" for multithreaded\n"
+"                                            drawing.\n");
     SkDebugf(
 "                     unflatten, Benchmark picture unflattening.\n");
     SkDebugf("\n");
     SkDebugf(
+"     --pipe: Benchmark SkGPipe rendering. Compatible with tiled, multithreaded rendering.\n");
+    SkDebugf(
 "     --device bitmap"
 #if SK_SUPPORT_GPU
 " | gpu"
@@ -112,6 +118,13 @@
     sk_tools::PictureRenderer::SkDeviceTypes deviceType =
         sk_tools::PictureRenderer::kBitmap_DeviceType;
 
+    bool usePipe = false;
+    bool multiThreaded = false;
+    bool useTiles = false;
+    const char* widthString = NULL;
+    const char* heightString = NULL;
+    bool isPowerOf2Mode = false;
+    const char* mode = NULL;
     for (++argv; argv < stop; ++argv) {
         if (0 == strcmp(*argv, "--repeat")) {
             ++argv;
@@ -128,6 +141,8 @@
                 usage(argv0);
                 exit(-1);
             }
+        } else if (0 == strcmp(*argv, "--pipe")) {
+            usePipe = true;
         } else if (0 == strcmp(*argv, "--mode")) {
             SkDELETE(benchmark);
 
@@ -138,82 +153,40 @@
                 exit(-1);
             }
 
-            if (0 == strcmp(*argv, "pipe")) {
-                benchmark = SkNEW(sk_tools::PipePictureBenchmark);
-            } else if (0 == strcmp(*argv, "record")) {
+            if (0 == strcmp(*argv, "record")) {
                 benchmark = SkNEW(sk_tools::RecordPictureBenchmark);
             } else if (0 == strcmp(*argv, "simple")) {
                 benchmark = SkNEW(sk_tools::SimplePictureBenchmark);
             } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))) {
-                char* mode = *argv;
-                bool isPowerOf2Mode = false;
+                useTiles = true;
+                mode = *argv;
 
                 if (0 == strcmp(*argv, "pow2tile")) {
                     isPowerOf2Mode = true;
                 }
 
-                sk_tools::TiledPictureBenchmark* tileBenchmark =
-                    SkNEW(sk_tools::TiledPictureBenchmark);
                 ++argv;
                 if (argv >= stop) {
-                    SkDELETE(tileBenchmark);
                     SkDebugf("Missing width for --mode %s\n", mode);
                     usage(argv0);
                     exit(-1);
                 }
 
-                if (isPowerOf2Mode) {
-                    int minWidth = atoi(*argv);
-
-                    if (!SkIsPow2(minWidth) || minWidth <= 0) {
-                        SkDELETE(tileBenchmark);
-                        SkDebugf("--mode %s must be given a width"
-                                 " value that is a power of two\n", mode);
-                        exit(-1);
-                    }
-
-                    tileBenchmark->setTileMinPowerOf2Width(minWidth);
-                } else if (sk_tools::is_percentage(*argv)) {
-                    tileBenchmark->setTileWidthPercentage(atof(*argv));
-                    if (!(tileBenchmark->getTileWidthPercentage() > 0)) {
-                        SkDELETE(tileBenchmark);
-                        SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
-                        exit(-1);
-                    }
-                } else {
-                    tileBenchmark->setTileWidth(atoi(*argv));
-                    if (!(tileBenchmark->getTileWidth() > 0)) {
-                        SkDELETE(tileBenchmark);
-                        SkDebugf("--mode %s must be given a width > 0\n", mode);
-                        exit(-1);
-                    }
-                }
-
+                widthString = *argv;
                 ++argv;
                 if (argv >= stop) {
-                    SkDELETE(tileBenchmark);
-                    SkDebugf("Missing height for --mode %s\n", mode);
+                    SkDebugf("Missing height for --mode tile\n");
                     usage(argv0);
                     exit(-1);
                 }
+                heightString = *argv;
 
-                if (sk_tools::is_percentage(*argv)) {
-                    tileBenchmark->setTileHeightPercentage(atof(*argv));
-                    if (!(tileBenchmark->getTileHeightPercentage() > 0)) {
-                        SkDELETE(tileBenchmark);
-                        SkDebugf("--mode %s must be given a height percentage > 0\n", mode);
-                        exit(-1);
-                    }
+                ++argv;
+                if (argv < stop && 0 == strcmp(*argv, "multi")) {
+                    multiThreaded = true;
                 } else {
-                    tileBenchmark->setTileHeight(atoi(*argv));
-                    if (!(tileBenchmark->getTileHeight() > 0)) {
-                        SkDELETE(tileBenchmark);
-                        SkDebugf("--mode %s must be given a height > 0\n", mode);
-                        exit(-1);
-                    }
+                    --argv;
                 }
-
-                benchmark = tileBenchmark;
             } else if (0 == strcmp(*argv, "unflatten")) {
                 benchmark = SkNEW(sk_tools::UnflattenPictureBenchmark);
             } else {
@@ -252,6 +225,55 @@
         }
     }
 
+    if (useTiles) {
+        sk_tools::TiledPictureBenchmark* tileBenchmark = SkNEW(sk_tools::TiledPictureBenchmark);
+        if (isPowerOf2Mode) {
+            int minWidth = atoi(widthString);
+            if (!SkIsPow2(minWidth) || minWidth < 0) {
+                SkDELETE(tileBenchmark);
+                SkDebugf("--mode %s must be given a width"
+                         " value that is a power of two\n", mode);
+                exit(-1);
+            }
+            tileBenchmark->setTileMinPowerOf2Width(minWidth);
+        } else if (sk_tools::is_percentage(widthString)) {
+            tileBenchmark->setTileWidthPercentage(atof(widthString));
+            if (!(tileBenchmark->getTileWidthPercentage() > 0)) {
+                SkDELETE(tileBenchmark);
+                SkDebugf("--mode tile must be given a width percentage > 0\n");
+                exit(-1);
+            }
+        } else {
+            tileBenchmark->setTileWidth(atoi(widthString));
+            if (!(tileBenchmark->getTileWidth() > 0)) {
+                SkDELETE(tileBenchmark);
+                SkDebugf("--mode tile must be given a width > 0\n");
+                exit(-1);
+            }
+        }
+
+        if (sk_tools::is_percentage(heightString)) {
+            tileBenchmark->setTileHeightPercentage(atof(heightString));
+            if (!(tileBenchmark->getTileHeightPercentage() > 0)) {
+                SkDELETE(tileBenchmark);
+                SkDebugf("--mode tile must be given a height percentage > 0\n");
+                exit(-1);
+            }
+        } else {
+            tileBenchmark->setTileHeight(atoi(heightString));
+            if (!(tileBenchmark->getTileHeight() > 0)) {
+                SkDELETE(tileBenchmark);
+                SkDebugf("--mode tile must be given a height > 0\n");
+                exit(-1);
+            }
+        }
+        tileBenchmark->setThreading(multiThreaded);
+        tileBenchmark->setUsePipe(usePipe);
+        benchmark = tileBenchmark;
+    } else if (usePipe) {
+        SkDELETE(benchmark);
+        benchmark = SkNEW(sk_tools::PipePictureBenchmark);
+    }
     if (inputs->count() < 1) {
         SkDELETE(benchmark);
         usage(argv0);
diff --git a/tools/picture_utils.cpp b/tools/picture_utils.cpp
index d5caf0c..fb5f37c 100644
--- a/tools/picture_utils.cpp
+++ b/tools/picture_utils.cpp
@@ -80,7 +80,7 @@
         basename->set(path.c_str(), end + 1);
     }
 
-    bool is_percentage(char* const string) {
+    bool is_percentage(const char* const string) {
         SkString skString(string);
         return skString.endsWith("%");
     }
diff --git a/tools/picture_utils.h b/tools/picture_utils.h
index 18352f2..bbde7f2 100644
--- a/tools/picture_utils.h
+++ b/tools/picture_utils.h
@@ -34,7 +34,7 @@
     void get_basename(SkString* basename, const SkString& path);
 
     // Returns true if the string ends with %
-    bool is_percentage(char* const string);
+    bool is_percentage(const char* const string);
 
     // Prints to STDOUT so that test results can be easily seperated from the
     // error stream. Note, that this still prints to the same stream as SkDebugf