Refactoring SkDeferredCanvas to use SkGPipe.
Keeping the refactor hidden behind a config macro for now.

TEST=covered by existing skia gm tests
BUG=https://code.google.com/p/chromium/issues/detail?id=133432
Review URL: https://codereview.appspot.com/6405054

git-svn-id: http://skia.googlecode.com/svn/trunk@4656 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 9b11e59..bdfcc6c 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -6,11 +6,12 @@
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/effects',
+        '../include/pipe',
         '../include/utils',
         '../include/utils/mac',
         '../include/utils/unix',
         '../include/utils/win',
-        '../include/effects',
         '../include/xml',
       ],
       'sources': [
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
index 1fa999b..878b693 100644
--- a/include/pipe/SkGPipe.h
+++ b/include/pipe/SkGPipe.h
@@ -98,6 +98,14 @@
     // should be no more drawing calls made into the recording canvas.
     void endRecording();
 
+    /**
+     *  Tells the writer to commit all recorded draw commands to the
+     *  controller immediately.
+     *  @param detachCurrentBlock Set to true to request that the next draw
+     *      command be recorded in a new block.
+     */
+    void flushRecording(bool detachCurrentBlock);
+
 private:
     class SkGPipeCanvas* fCanvas;
     SkGPipeController*   fController;
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index 8964e9e..25aed36 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -8,16 +8,26 @@
 #ifndef SkDeferredCanvas_DEFINED
 #define SkDeferredCanvas_DEFINED
 
+#ifndef SK_DEFERRED_CANVAS_USES_GPIPE
+#define SK_DEFERRED_CANVAS_USES_GPIPE 0
+#endif
+
 #include "SkCanvas.h"
 #include "SkDevice.h"
-#include "SkPicture.h"
 #include "SkPixelRef.h"
 
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+#include "SkGPipe.h"
+#include "SkChunkAlloc.h"
+#else
+#include "SkPicture.h"
+#endif
+
 /** \class SkDeferredCanvas
-    Subclass of SkCanvas that encapsulates an SkPicture for deferred drawing.
-    The main difference between this class and SkPictureRecord (the canvas
-    provided by SkPicture) is that this is a full drop-in replacement for
-    SkCanvas, while SkPictureRecord only supports draw operations.
+    Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
+    drawing. The main difference between this class and SkPictureRecord (the
+    canvas provided by SkPicture) is that this is a full drop-in replacement
+    for SkCanvas, while SkPictureRecord only supports draw operations.
     SkDeferredCanvas will transparently trigger the flushing of deferred
     draw operations when an attempt is made to access the pixel data.
 */
@@ -149,6 +159,35 @@
         typedef SkRefCnt INHERITED;
     };
 
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+protected:
+    class DeferredPipeController : public SkGPipeController {
+    public:
+        DeferredPipeController();
+        void setPlaybackCanvas(SkCanvas*);
+        virtual ~DeferredPipeController();
+        virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+        virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+        void playback();
+        void reset();
+        bool hasRecorded() {return fAllocator.blockCount() != 0;}
+    private:
+        enum {
+            kMinBlockSize = 4096
+        };
+        struct PipeBlock {
+            PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; }
+            void* fBlock;
+            size_t fSize;
+        };
+        void* fBlock;
+        size_t fBytesWritten;
+        SkChunkAlloc fAllocator;
+        SkTDArray<PipeBlock> fBlockList;
+        SkGPipeReader fReader;
+    };
+#endif
+
 public:
     class DeferredDevice : public SkDevice {
     public:
@@ -277,7 +316,15 @@
     private:
         virtual void flush();
 
+        void endRecording();
+        void beginRecording();
+
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+        SkGPipeWriter  fPipeWriter;
+        DeferredPipeController fPipeController;
+#else
         SkPicture fPicture;
+#endif
         SkDevice* fImmediateDevice;
         SkCanvas* fImmediateCanvas;
         SkCanvas* fRecordingCanvas;
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index 9007163..fc40006 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -350,6 +350,8 @@
         }
     }
 
+    void flushRecording(bool detachCurrentBlock);
+
     // overrides from SkCanvas
     virtual int save(SaveFlags) SK_OVERRIDE;
     virtual int saveLayer(const SkRect* bounds, const SkPaint*,
@@ -1077,6 +1079,14 @@
     }
 }
 
+void SkGPipeCanvas::flushRecording(bool detachCurrentBlock) {
+    doNotify();
+    if (detachCurrentBlock) {
+        // force a new block to be requested for the next recorded command
+        fBlockSize = 0; 
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 template <typename T> uint32_t castToU32(T value) {
@@ -1219,3 +1229,7 @@
     }
 }
 
+void SkGPipeWriter::flushRecording(bool detachCurrentBlock){
+    fCanvas->flushRecording(detachCurrentBlock);
+}
+
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index 9afd580..568f9a7 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -438,6 +438,66 @@
     return drawingCanvas();
 }
 
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+
+// SkDeferredCanvas::DeferredPipeController
+//-------------------------------------------
+
+SkDeferredCanvas::DeferredPipeController::DeferredPipeController() :
+    fAllocator(kMinBlockSize) {
+    fBlock = NULL;
+    fBytesWritten = 0;
+}
+
+SkDeferredCanvas::DeferredPipeController::~DeferredPipeController() {
+    fAllocator.reset();
+}
+
+void SkDeferredCanvas::DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
+    fReader.setCanvas(canvas);
+}
+
+void* SkDeferredCanvas::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 SkDeferredCanvas::DeferredPipeController::notifyWritten(size_t bytes) {
+    fBytesWritten += bytes;
+}
+
+void SkDeferredCanvas::DeferredPipeController::playback() {
+    
+    for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
+        fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize);
+    }
+    fBlockList.reset();
+
+    if (fBlock) {
+        fReader.playback(fBlock,fBytesWritten);
+        fBlock = NULL;
+    }
+
+    // Release all allocated blocks
+    fAllocator.reset();
+}
+
+void SkDeferredCanvas::DeferredPipeController::reset() {
+    fBlockList.reset();
+    fBlock = NULL;
+    fAllocator.reset();
+}
+
+#endif // SK_DEFERRED_CANVAS_USES_GPIPE
+
 // SkDeferredCanvas::DeferredDevice
 //------------------------------------
 
@@ -451,15 +511,37 @@
     SkSafeRef(fDeviceContext);
     fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
     fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
-    fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
-        fImmediateDevice->height(),
-        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    fPipeController.setPlaybackCanvas(fImmediateCanvas);
+#endif
+    beginRecording();
 }
 
 SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
     SkSafeUnref(fImmediateCanvas);
     SkSafeUnref(fDeviceContext);
 }
+
+
+void SkDeferredCanvas::DeferredDevice::endRecording() {
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    fPipeWriter.endRecording();
+    fPipeController.reset();
+#else
+    fPicture.endRecording();
+#endif
+    fRecordingCanvas = NULL;
+}
+
+void SkDeferredCanvas::DeferredDevice::beginRecording() {
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0);
+#else
+    fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
+        fImmediateDevice->height(),
+        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
+#endif
+}
     
 void SkDeferredCanvas::DeferredDevice::setDeviceContext(
     DeviceContext* deviceContext) {
@@ -483,10 +565,8 @@
 
             // beginRecording creates a new recording canvas and discards the
             // old one, hence purging deferred draw ops.
-            fRecordingCanvas = fPicture.beginRecording(
-                fImmediateDevice->width(),
-                fImmediateDevice->height(),
-                SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
+            this->endRecording();
+            this->beginRecording();
 
             // Restore pre-purge state
             if (!clipRegion.isEmpty()) {
@@ -510,16 +590,26 @@
 }
 
 void SkDeferredCanvas::DeferredDevice::flushPending() {
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    if (!fPipeController.hasRecorded()) {
+        return;
+    }
+#else
     if (!fPicture.hasRecorded()) {
         return;
     }
+#endif
     if (fDeviceContext) {
         fDeviceContext->prepareForDraw();
     }
+
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    fPipeWriter.flushRecording(true);
+    fPipeController.playback();
+#else
     fPicture.draw(fImmediateCanvas);
-    fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(), 
-        fImmediateDevice->height(),
-        SkPicture::kFlattenMutableNonTexturePixelRefs_RecordingFlag);
+    this->beginRecording();
+#endif
 }
 
 void SkDeferredCanvas::DeferredDevice::flush() {
@@ -528,9 +618,16 @@
 }
 
 void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
+#if SK_DEFERRED_CANVAS_USES_GPIPE
+    if (bitmap.isImmutable()) {
+        // FIXME: Make SkGPipe flatten software-backed non-immutable bitmaps 
+        return;
+    }
+#else
     if (bitmap.isImmutable() || fPicture.willFlattenPixelsOnRecord(bitmap)) {
         return; // safe to defer.
     }
+#endif
 
     // For now, drawing a writable bitmap triggers a flush
     // TODO: implement read-only semantics and auto buffer duplication on write