Add tiled rendering as an option to GM.

Use an SkGPipe to play back drawing into tiles.
This will help us to debug differences in drawing
while tiled.

Pass --tiledPipe to gm to use the tiled pipe.
Review URL: https://codereview.appspot.com/6295050

git-svn-id: http://skia.googlecode.com/svn/trunk@4199 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 08c7c52..34ded4a 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -20,8 +20,9 @@
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
 #include "SkPicture.h"
-#include "SkStream.h"
 #include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SamplePipeControllers.h"
 
 static bool gForceBWtext;
 
@@ -619,45 +620,6 @@
     }
 }
 
-class PipeController : public SkGPipeController {
-public:
-    PipeController(SkCanvas* target);
-    ~PipeController();
-    virtual void* requestBlock(size_t minRequest, size_t* actual);
-    virtual void notifyWritten(size_t bytes);
-private:
-    SkGPipeReader fReader;
-    void* fBlock;
-    size_t fBlockSize;
-    size_t fBytesWritten;
-    SkGPipeReader::Status fStatus;
-};
-
-PipeController::PipeController(SkCanvas* target)
-:fReader(target) {
-    fBlock = NULL;
-    fBlockSize = fBytesWritten = 0;
-}
-
-PipeController::~PipeController() {
-    sk_free(fBlock);
-}
-
-void* PipeController::requestBlock(size_t minRequest, size_t *actual) {
-    sk_free(fBlock);
-    fBlockSize = minRequest * 4;
-    fBlock = sk_malloc_throw(fBlockSize);
-    fBytesWritten = 0;
-    *actual = fBlockSize;
-    return fBlock;
-}
-
-void PipeController::notifyWritten(size_t bytes) {
-    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
-    SkASSERT(SkGPipeReader::kError_Status != fStatus);
-    fBytesWritten += bytes;
-}
-
 static ErrorBitfield test_pipe_playback(GM* gm,
                                         const ConfigData& gRec,
                                         const SkBitmap& comparisonBitmap,
@@ -680,6 +642,27 @@
                                "-pipe", bitmap, NULL, &comparisonBitmap);
 }
 
+static ErrorBitfield test_tiled_pipe_playback(GM* gm,
+                                        const ConfigData& gRec,
+                                        const SkBitmap& comparisonBitmap,
+                                        const char readPath [],
+                                        const char diffPath []) {
+    if (kRaster_Backend != gRec.fBackend) {
+        return ERROR_NONE;
+    }
+    SkBitmap bitmap;
+    SkISize size = gm->getISize();
+    setup_bitmap(gRec, size, &bitmap);
+    TiledPipeController pipeController(bitmap);
+    SkGPipeWriter writer;
+    SkCanvas* pipeCanvas = writer.startRecording(&pipeController,
+                                                 SkGPipeWriter::kCrossProcess_Flag);
+    invokeGM(gm, pipeCanvas);
+    writer.endRecording();
+    return handle_test_results(gm, gRec, NULL, NULL, diffPath,
+                               "-tiled pipe", bitmap, NULL, &comparisonBitmap);
+}
+
 static void write_picture_serialization(GM* gm, const ConfigData& rec,
                                         const char writePicturePath[]) {
     // only do this once, so we pick raster
@@ -701,6 +684,7 @@
     SkDebugf(
         "%s [-w writePath] [-r readPath] [-d diffPath] [-i resourcePath]\n"
         "    [--noreplay] [--pipe] [--serialize] [--forceBWtext] [--nopdf] \n"
+        "    [--tiledPipe] \n"
         "    [--nodeferred] [--match substring] [--notexturecache]\n"
         , argv0);
     SkDebugf("    writePath: directory to write rendered images in.\n");
@@ -711,6 +695,7 @@
     SkDebugf("    resourcePath: directory that stores image resources.\n");
     SkDebugf("    --noreplay: do not exercise SkPicture replay.\n");
     SkDebugf("    --pipe: Exercise SkGPipe replay.\n");
+    SkDebugf("    --tiledPipe: Exercise tiled SkGPipe replay.\n");
     SkDebugf(
 "    --serialize: exercise SkPicture serialization & deserialization.\n");
     SkDebugf("    --forceBWtext: disable text anti-aliasing.\n");
@@ -823,6 +808,7 @@
     bool doPDF = true;
     bool doReplay = true;
     bool doPipe = false;
+    bool doTiledPipe = false;
     bool doSerialize = false;
     bool doDeferred = true;
     bool disableTextureCache = false;
@@ -861,6 +847,8 @@
             gForceBWtext = true;
         } else if (strcmp(*argv, "--pipe") == 0) {
             doPipe = true;
+        } else if (strcmp(*argv, "--tiledPipe") == 0) {
+            doTiledPipe = true;
         } else if (strcmp(*argv, "--noreplay") == 0) {
             doReplay = false;
         } else if (strcmp(*argv, "--nopdf") == 0) {
@@ -1007,6 +995,13 @@
                                                  readPath, diffPath);
             }
 
+            if ((ERROR_NONE == testErrors) && doTiledPipe &&
+                !(gmFlags & GM::kSkipPipe_Flag)) {
+                testErrors |= test_tiled_pipe_playback(gm, gRec[i],
+                                                 forwardRenderedBitmap,
+                                                 readPath, diffPath);
+            }
+
             if ((ERROR_NONE == testErrors) && doSerialize  &&
                 !(gmFlags & GM::kSkipPicture_Flag)) {
                 testErrors |= test_picture_serialization(gm, gRec[i],
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 980c199..c3f359c 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -17,6 +17,8 @@
         '../gm/gm.cpp',
         '../gm/gmmain.cpp',
         '../gm/system_preferences_default.cpp',
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
       ],
       'dependencies': [
         'core.gyp:core',
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
index 29b058e..e0b52e0 100644
--- a/include/pipe/SkGPipe.h
+++ b/include/pipe/SkGPipe.h
@@ -23,6 +23,7 @@
 
 class SkGPipeReader {
 public:
+    SkGPipeReader();
     SkGPipeReader(SkCanvas* target);
     ~SkGPipeReader();
 
@@ -33,6 +34,7 @@
         kReadAtom_Status//!< finished reading an atom
     };
 
+    void setCanvas(SkCanvas*);
     // data must be 4-byte aligned
     // length must be a multiple of 4
     Status playback(const void* data, size_t length, size_t* bytesRead = NULL,
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index 976abbf..ddc0cc4 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -545,12 +545,21 @@
 
 #include "SkGPipe.h"
 
-SkGPipeReader::SkGPipeReader(SkCanvas* target) {
-    SkSafeRef(target);
-    fCanvas = target;
+SkGPipeReader::SkGPipeReader() {
+    fCanvas = NULL;
     fState = NULL;
 }
 
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+    fCanvas = NULL;
+    this->setCanvas(target);
+    fState = NULL;
+}
+
+void SkGPipeReader::setCanvas(SkCanvas *target) {
+    SkRefCnt_SafeAssign(fCanvas, target);
+}
+
 SkGPipeReader::~SkGPipeReader() {
     SkSafeUnref(fCanvas);
     delete fState;
diff --git a/src/pipe/utils/SamplePipeControllers.cpp b/src/pipe/utils/SamplePipeControllers.cpp
new file mode 100644
index 0000000..487a2b8
--- /dev/null
+++ b/src/pipe/utils/SamplePipeControllers.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SamplePipeControllers.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGPipe.h"
+
+PipeController::PipeController(SkCanvas* target)
+:fReader(target) {
+    fBlock = NULL;
+    fBlockSize = fBytesWritten = 0;
+}
+
+PipeController::~PipeController() {
+    sk_free(fBlock);
+}
+
+void* PipeController::requestBlock(size_t minRequest, size_t *actual) {
+    sk_free(fBlock);
+    fBlockSize = minRequest * 4;
+    fBlock = sk_malloc_throw(fBlockSize);
+    fBytesWritten = 0;
+    *actual = fBlockSize;
+    return fBlock;
+}
+
+void PipeController::notifyWritten(size_t bytes) {
+    fStatus = fReader.playback(this->getData(), bytes);
+    SkASSERT(SkGPipeReader::kError_Status != fStatus);
+    fBytesWritten += bytes;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TiledPipeController::TiledPipeController(const SkBitmap& bitmap)
+: INHERITED(NULL) {
+    int32_t top = 0;
+    int32_t bottom;
+    int32_t height = bitmap.height() / NumberOfTiles;
+    SkIRect rect;
+    for (int i = 0; i < NumberOfTiles; i++) {
+        bottom = i + 1 == NumberOfTiles ? bitmap.height() : top + height;
+        rect.setLTRB(0, top, bitmap.width(), bottom);
+        top = bottom;
+
+        bool extracted = bitmap.extractSubset(&fBitmaps[i], rect);
+        SkASSERT(extracted);
+        SkDevice* device = new SkDevice(fBitmaps[i]);
+        SkCanvas* canvas = new SkCanvas(device);
+        device->unref();
+        canvas->translate(SkIntToScalar(-rect.left()),
+                          SkIntToScalar(-rect.top()));
+        if (0 == i) {
+            fReader.setCanvas(canvas);
+        } else {
+            fReaders[i - 1].setCanvas(canvas);
+        }
+        canvas->unref();
+    }
+}
+
+void TiledPipeController::notifyWritten(size_t bytes) {
+    for (int i = 0; i < NumberOfTiles - 1; i++) {
+        fReaders[i].playback(this->getData(), bytes);
+    }
+    this->INHERITED::notifyWritten(bytes);
+}
diff --git a/src/pipe/utils/SamplePipeControllers.h b/src/pipe/utils/SamplePipeControllers.h
new file mode 100644
index 0000000..ace6274
--- /dev/null
+++ b/src/pipe/utils/SamplePipeControllers.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmap.h"
+#include "SkGPipe.h"
+
+class SkCanvas;
+
+class PipeController : public SkGPipeController {
+public:
+    PipeController(SkCanvas* target);
+    virtual ~PipeController();
+    virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+protected:
+    const void* getData() { return (const char*) fBlock + fBytesWritten; }
+    SkGPipeReader fReader;
+private:
+    void* fBlock;
+    size_t fBlockSize;
+    size_t fBytesWritten;
+    SkGPipeReader::Status fStatus;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class TiledPipeController : public PipeController {
+public:
+    TiledPipeController(const SkBitmap&);
+    virtual ~TiledPipeController() {};
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+private:
+    enum {
+        NumberOfTiles = 10
+    };
+    SkGPipeReader fReaders[NumberOfTiles - 1];
+    SkBitmap fBitmaps[NumberOfTiles];
+    typedef PipeController INHERITED;
+};