Put clear and discard into GrBatch.

Review URL: https://codereview.chromium.org/1293563003
diff --git a/src/core/SkStringUtils.cpp b/src/core/SkStringUtils.cpp
index 390de7f..35e5557 100644
--- a/src/core/SkStringUtils.cpp
+++ b/src/core/SkStringUtils.cpp
@@ -35,3 +35,29 @@
     }
 }
 
+SkString SkTabString(const SkString& string, int tabCnt) {
+    if (tabCnt <= 0) {
+        return string;
+    }
+    SkString tabs;
+    for (int i = 0; i < tabCnt; ++i) {
+        tabs.append("\t");
+    }
+    SkString result;
+    static const char newline[] = "\n";
+    const char* input = string.c_str();
+    int nextNL = SkStrFind(input, newline);
+    while (nextNL >= 0) {
+        if (nextNL > 0) {
+            result.append(tabs);
+        }
+        result.append(input, nextNL + 1);
+        input += nextNL + 1;
+        nextNL = SkStrFind(input, newline);
+    }
+    if (*input != '\0') {
+        result.append(tabs);
+    }
+    result.append(input);
+    return result;
+}
diff --git a/src/core/SkStringUtils.h b/src/core/SkStringUtils.h
index 2839ac2..fd158c3 100644
--- a/src/core/SkStringUtils.h
+++ b/src/core/SkStringUtils.h
@@ -35,4 +35,7 @@
     SkAppendScalar(str, value, kHex_SkScalarAsStringType);
 }
 
+/** Indents every non-empty line of the string by tabCnt tabs */
+SkString SkTabString(const SkString& string, int tabCnt);
+
 #endif
diff --git a/src/gpu/GrBufferedDrawTarget.cpp b/src/gpu/GrBufferedDrawTarget.cpp
index a706bf3..c59d035 100644
--- a/src/gpu/GrBufferedDrawTarget.cpp
+++ b/src/gpu/GrBufferedDrawTarget.cpp
@@ -26,7 +26,7 @@
     this->reset();
 }
 
-void GrBufferedDrawTarget::onDrawBatch(GrDrawBatch* batch) {
+void GrBufferedDrawTarget::onDrawBatch(GrBatch* batch) {
     fCommands->recordDrawBatch(batch, *this->caps());
 }
 
@@ -69,24 +69,12 @@
                                opts);
 }
 
-void GrBufferedDrawTarget::onClear(const SkIRect& rect, GrColor color,
-                                   GrRenderTarget* renderTarget) {
-    fCommands->recordClear(rect, color, renderTarget);
-}
-
 void GrBufferedDrawTarget::clearStencilClip(const SkIRect& rect,
                                             bool insideClip,
                                             GrRenderTarget* renderTarget) {
     fCommands->recordClearStencilClip(rect, insideClip, renderTarget);
 }
 
-void GrBufferedDrawTarget::discard(GrRenderTarget* renderTarget) {
-    if (!this->caps()->discardRenderTargetSupport()) {
-        return;
-    }
-    fCommands->recordDiscard(renderTarget);
-}
-
 void GrBufferedDrawTarget::onReset() {
     fCommands->reset();
     fPathIndexBuffer.rewind();
diff --git a/src/gpu/GrBufferedDrawTarget.h b/src/gpu/GrBufferedDrawTarget.h
index bb7fd89..e764512 100644
--- a/src/gpu/GrBufferedDrawTarget.h
+++ b/src/gpu/GrBufferedDrawTarget.h
@@ -34,8 +34,6 @@
                           bool insideClip,
                           GrRenderTarget* renderTarget) override;
 
-    void discard(GrRenderTarget*) override;
-
 protected:
     void appendIndicesAndTransforms(const void* indexValues, PathIndexType indexType, 
                                     const float* transformValues, PathTransformType transformType,
@@ -78,7 +76,7 @@
     void onFlush() override;
 
     // overrides from GrDrawTarget
-    void onDrawBatch(GrDrawBatch*) override;
+    void onDrawBatch(GrBatch*) override;
     void onStencilPath(const GrPipelineBuilder&,
                        const GrPathProcessor*,
                        const GrPath*,
@@ -97,9 +95,6 @@
                      int count,
                      const GrStencilSettings&,
                      const PipelineInfo&) override;
-    void onClear(const SkIRect& rect,
-                 GrColor color,
-                 GrRenderTarget* renderTarget) override;
     void onCopySurface(GrSurface* dst,
                        GrSurface* src,
                        const SkIRect& srcRect,
diff --git a/src/gpu/GrCommandBuilder.cpp b/src/gpu/GrCommandBuilder.cpp
index 76f830d..86c1668 100644
--- a/src/gpu/GrCommandBuilder.cpp
+++ b/src/gpu/GrCommandBuilder.cpp
@@ -18,20 +18,6 @@
     }
 }
 
-GrTargetCommands::Cmd* GrCommandBuilder::recordClear(const SkIRect& rect,
-                                                     GrColor color,
-                                                     GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-    SkASSERT(rect.fLeft <= rect.fRight && rect.fTop <= rect.fBottom);
-
-    Clear* clr = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), Clear, (renderTarget));
-    GrColorIsPMAssert(color);
-    clr->fColor = color;
-    clr->fRect = rect;
-    GrBATCH_INFO("Recording clear %d\n", clr->uniqueID());
-    return clr;
-}
-
 GrTargetCommands::Cmd* GrCommandBuilder::recordClearStencilClip(const SkIRect& rect,
                                                                 bool insideClip,
                                                                 GrRenderTarget* renderTarget) {
@@ -46,15 +32,6 @@
     return clr;
 }
 
-GrTargetCommands::Cmd* GrCommandBuilder::recordDiscard(GrRenderTarget* renderTarget) {
-    SkASSERT(renderTarget);
-
-    Clear* clr = GrNEW_APPEND_TO_RECORDER(*this->cmdBuffer(), Clear, (renderTarget));
-    clr->fColor = GrColor_ILLEGAL;
-    GrBATCH_INFO("Recording discard %d\n", clr->uniqueID());
-    return clr;
-}
-
 GrTargetCommands::Cmd* GrCommandBuilder::recordCopySurface(GrSurface* dst,
                                                            GrSurface* src,
                                                            const SkIRect& srcRect,
diff --git a/src/gpu/GrCommandBuilder.h b/src/gpu/GrCommandBuilder.h
index 004fc79..b50a6bb 100644
--- a/src/gpu/GrCommandBuilder.h
+++ b/src/gpu/GrCommandBuilder.h
@@ -29,8 +29,7 @@
     virtual Cmd* recordClearStencilClip(const SkIRect& rect,
                                         bool insideClip,
                                         GrRenderTarget* renderTarget);
-    virtual Cmd* recordDiscard(GrRenderTarget*);
-    virtual Cmd* recordDrawBatch(GrDrawBatch*, const GrCaps&) = 0;
+    virtual Cmd* recordDrawBatch(GrBatch*, const GrCaps&) = 0;
     virtual Cmd* recordStencilPath(const GrPipelineBuilder&,
                                    const GrPathProcessor*,
                                    const GrPath*,
@@ -51,9 +50,6 @@
                                  int,
                                  const GrStencilSettings&,
                                  const GrPipelineOptimizations&) = 0;
-    virtual Cmd* recordClear(const SkIRect& rect,
-                             GrColor,
-                             GrRenderTarget*);
     virtual Cmd* recordCopySurface(GrSurface* dst,
                                    GrSurface* src,
                                    const SkIRect& srcRect,
@@ -64,7 +60,6 @@
     typedef GrTargetCommands::StencilPath StencilPath;
     typedef GrTargetCommands::DrawPath DrawPath;
     typedef GrTargetCommands::DrawPaths DrawPaths;
-    typedef GrTargetCommands::Clear Clear;
     typedef GrTargetCommands::ClearStencilClip ClearStencilClip;
     typedef GrTargetCommands::CopySurface CopySurface;
 
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index 0184890..d2c258d 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -20,6 +20,8 @@
 #include "GrTexture.h"
 #include "GrVertexBuffer.h"
 
+#include "batches/GrClearBatch.h"
+#include "batches/GrDiscardBatch.h"
 #include "batches/GrDrawBatch.h"
 #include "batches/GrRectBatchFactory.h"
 
@@ -340,8 +342,18 @@
         pipelineBuilder.setRenderTarget(renderTarget);
 
         this->drawSimpleRect(pipelineBuilder, color, SkMatrix::I(), *rect);
-    } else {       
-        this->onClear(*rect, color, renderTarget);
+    } else {
+        GrBatch* batch = SkNEW_ARGS(GrClearBatch, (*rect, color, renderTarget));
+        this->onDrawBatch(batch);
+        batch->unref();
+    }
+}
+
+void GrDrawTarget::discard(GrRenderTarget* renderTarget) {
+    if (this->caps()->discardRenderTargetSupport()) {
+        GrBatch* batch = SkNEW_ARGS(GrDiscardBatch, (renderTarget));
+        this->onDrawBatch(batch);
+        batch->unref();
     }
 }
 
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index d552d39..1890883 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -30,6 +30,7 @@
 #include "SkTypes.h"
 #include "SkXfermode.h"
 
+class GrBatch;
 class GrClip;
 class GrCaps;
 class GrPath;
@@ -155,10 +156,8 @@
                bool canIgnoreRect,
                GrRenderTarget* renderTarget);
 
-    /**
-     * Discards the contents render target.
-     **/
-    virtual void discard(GrRenderTarget*) = 0;
+    /** Discards the contents render target. */
+    void discard(GrRenderTarget*);
 
     /**
      * Called at start and end of gpu trace marking
@@ -242,7 +241,7 @@
 
     virtual void onFlush() = 0;
 
-    virtual void onDrawBatch(GrDrawBatch*) = 0;
+    virtual void onDrawBatch(GrBatch*) = 0;
     virtual void onStencilPath(const GrPipelineBuilder&,
                                const GrPathProcessor*,
                                const GrPath*,
@@ -262,8 +261,6 @@
                              const GrStencilSettings&,
                              const PipelineInfo&) = 0;
 
-    virtual void onClear(const SkIRect& rect, GrColor color, GrRenderTarget* renderTarget) = 0;
-
     /** The subclass's copy surface implementation. It should assume that any clipping has already
         been performed on the rect and point and that the GrGpu supports the copy. */
     virtual void onCopySurface(GrSurface* dst,
diff --git a/src/gpu/GrImmediateDrawTarget.cpp b/src/gpu/GrImmediateDrawTarget.cpp
index bac9e60..55af985 100644
--- a/src/gpu/GrImmediateDrawTarget.cpp
+++ b/src/gpu/GrImmediateDrawTarget.cpp
@@ -25,7 +25,7 @@
     this->reset();
 }
 
-void GrImmediateDrawTarget::onDrawBatch(GrDrawBatch* batch) {
+void GrImmediateDrawTarget::onDrawBatch(GrBatch* batch) {
 
 #if 0
     // TODO: encapsulate the specialization of GrVertexBatch in GrVertexBatch so that we can
@@ -40,11 +40,6 @@
 #endif
 }
 
-void GrImmediateDrawTarget::onClear(const SkIRect& rect, GrColor color,
-                                    GrRenderTarget* renderTarget) {
-    this->getGpu()->clear(rect, color, renderTarget);
-}
-
 void GrImmediateDrawTarget::onCopySurface(GrSurface* dst,
                                           GrSurface* src,
                                           const SkIRect& srcRect,
@@ -58,14 +53,6 @@
     this->getGpu()->clearStencilClip(rect, insideClip, renderTarget);
 }
 
-void GrImmediateDrawTarget::discard(GrRenderTarget* renderTarget) {
-    if (!this->caps()->discardRenderTargetSupport()) {
-        return;
-    }
-
-    this->getGpu()->discard(renderTarget);
-}
-
 void GrImmediateDrawTarget::onReset() {}
 
 void GrImmediateDrawTarget::onFlush() {
diff --git a/src/gpu/GrImmediateDrawTarget.h b/src/gpu/GrImmediateDrawTarget.h
index cb2c243..10b7e24 100644
--- a/src/gpu/GrImmediateDrawTarget.h
+++ b/src/gpu/GrImmediateDrawTarget.h
@@ -30,14 +30,12 @@
                           bool insideClip,
                           GrRenderTarget* renderTarget) override;
 
-    void discard(GrRenderTarget*) override;
-
 private:
     void onReset() override;
     void onFlush() override;
 
     // overrides from GrDrawTarget
-    void onDrawBatch(GrDrawBatch*) override;
+    void onDrawBatch(GrBatch*) override;
     void onStencilPath(const GrPipelineBuilder&,
                        const GrPathProcessor*,
                        const GrPath*,
@@ -62,9 +60,6 @@
                      const PipelineInfo&) override {
         SkFAIL("Only batch implemented\n");
     }
-    void onClear(const SkIRect& rect,
-                 GrColor color,
-                 GrRenderTarget* renderTarget) override;
     void onCopySurface(GrSurface* dst,
                        GrSurface* src,
                        const SkIRect& srcRect,
diff --git a/src/gpu/GrInOrderCommandBuilder.cpp b/src/gpu/GrInOrderCommandBuilder.cpp
index 80989d1..078adad 100644
--- a/src/gpu/GrInOrderCommandBuilder.cpp
+++ b/src/gpu/GrInOrderCommandBuilder.cpp
@@ -25,7 +25,7 @@
     return isWinding;
 }
 
-GrTargetCommands::Cmd* GrInOrderCommandBuilder::recordDrawBatch(GrDrawBatch* batch,
+GrTargetCommands::Cmd* GrInOrderCommandBuilder::recordDrawBatch(GrBatch* batch,
                                                                 const GrCaps& caps) {
     GrBATCH_INFO("In-Recording (%s, %u)\n", batch->name(), batch->uniqueID());
     if (!this->cmdBuffer()->empty() &&
diff --git a/src/gpu/GrInOrderCommandBuilder.h b/src/gpu/GrInOrderCommandBuilder.h
index 13a8212..4d03120 100644
--- a/src/gpu/GrInOrderCommandBuilder.h
+++ b/src/gpu/GrInOrderCommandBuilder.h
@@ -17,7 +17,7 @@
 
     GrInOrderCommandBuilder() : INHERITED() { }
 
-    Cmd* recordDrawBatch(GrDrawBatch*, const GrCaps&) override;
+    Cmd* recordDrawBatch(GrBatch*, const GrCaps&) override;
     Cmd* recordStencilPath(const GrPipelineBuilder&,
                            const GrPathProcessor*,
                            const GrPath*,
diff --git a/src/gpu/GrReorderCommandBuilder.cpp b/src/gpu/GrReorderCommandBuilder.cpp
index d70e25a..af34998 100644
--- a/src/gpu/GrReorderCommandBuilder.cpp
+++ b/src/gpu/GrReorderCommandBuilder.cpp
@@ -6,6 +6,7 @@
  */
 
 #include "GrReorderCommandBuilder.h"
+#include "SkStringUtils.h"
 
 template <class Left, class Right>
 static bool intersect(const Left& a, const Right& b) {
@@ -15,7 +16,7 @@
            a.fTop < b.fBottom && b.fTop < a.fBottom;
 }
 
-GrTargetCommands::Cmd* GrReorderCommandBuilder::recordDrawBatch(GrDrawBatch* batch,
+GrTargetCommands::Cmd* GrReorderCommandBuilder::recordDrawBatch(GrBatch* batch,
                                                                 const GrCaps& caps) {
     // Check if there is a Batch Draw we can batch with by linearly searching back until we either
     // 1) check every draw
@@ -25,26 +26,13 @@
     static const int kMaxLookback = 10;
     int i = 0;
 
-    GrRenderTarget* rt = batch->pipeline()->getRenderTarget();
-
     GrBATCH_INFO("Re-Recording (%s, B%u)\n"
-                 "\tRenderTarget %p\n"
                  "\tBounds (%f, %f, %f, %f)\n",
                  batch->name(),
-                 batch->uniqueID(), rt,
+                 batch->uniqueID(),
                  batch->bounds().fLeft, batch->bounds().fRight,
                  batch->bounds().fTop, batch->bounds().fBottom);
-    if (GR_BATCH_SPEW) {
-        SkDebugf("\tColorStages:\n");
-        for (int i = 0; i < batch->pipeline()->numColorFragmentStages(); i++) {
-            SkDebugf("\t\t%s\n", batch->pipeline()->getColorStage(i).processor()->name());
-        }
-        SkDebugf("\tCoverageStages:\n");
-        for (int i = 0; i < batch->pipeline()->numCoverageFragmentStages(); i++) {
-            SkDebugf("\t\t%s\n", batch->pipeline()->getCoverageStage(i).processor()->name());
-        }
-        SkDebugf("\tXP: %s\n", batch->pipeline()->getXferProcessor()->name());
-    }
+    GrBATCH_INFO(SkTabString(batch->dumpInfo(), 1).c_str());
     GrBATCH_INFO("\tOutcome:\n");
     if (!this->cmdBuffer()->empty()) {
         GrTargetCommands::CmdBuffer::ReverseIter reverseIter(*this->cmdBuffer());
@@ -53,7 +41,7 @@
             if (Cmd::kDrawBatch_CmdType == reverseIter->type()) {
                 DrawBatch* previous = static_cast<DrawBatch*>(reverseIter.get());
 
-                if (previous->batch()->pipeline()->getRenderTarget() != rt) {
+                if (previous->batch()->renderTargetUniqueID() != batch->renderTargetUniqueID()) {
                     GrBATCH_INFO("\t\tBreaking because of (%s, B%u) Rendertarget\n",
                                  previous->batch()->name(), previous->batch()->uniqueID());
                     break;
@@ -70,24 +58,9 @@
                                  previous->batch()->name(), previous->batch()->uniqueID());
                     break;
                 }
-            } else if (Cmd::kClear_CmdType == reverseIter->type()) {
-                Clear* previous = static_cast<Clear*>(reverseIter.get());
-
-                // We cannot continue to search backwards if the render target changes
-                if (previous->renderTarget() != rt) {
-                    GrBATCH_INFO("\t\tBreaking because of Clear's Rendertarget change\n");
-                    break;
-                }
-
-                // We set the color to illegal if we are doing a discard.
-                if (previous->fColor == GrColor_ILLEGAL ||
-                    intersect(batch->bounds(), previous->fRect)) {
-                    GrBATCH_INFO("\t\tBreaking because of Clear intersection\n");
-                    break;
-                }
             } else {
                 GrBATCH_INFO("\t\tBreaking because of other %08x\n", reverseIter->type());
-                // TODO temporary until we can navigate the other types of commands
+                // TODO temporary until we only have batches.
                 break;
             }
         } while (reverseIter.previous() && ++i < kMaxLookback);
diff --git a/src/gpu/GrReorderCommandBuilder.h b/src/gpu/GrReorderCommandBuilder.h
index af4a28c..1dd33a0 100644
--- a/src/gpu/GrReorderCommandBuilder.h
+++ b/src/gpu/GrReorderCommandBuilder.h
@@ -17,7 +17,7 @@
 
     GrReorderCommandBuilder() : INHERITED() {}
 
-    Cmd* recordDrawBatch(GrDrawBatch*, const GrCaps&) override;
+    Cmd* recordDrawBatch(GrBatch*, const GrCaps&) override;
     Cmd* recordStencilPath(const GrPipelineBuilder&,
                            const GrPathProcessor*,
                            const GrPath*,
diff --git a/src/gpu/GrTargetCommands.cpp b/src/gpu/GrTargetCommands.cpp
index c2006d7..7a75661 100644
--- a/src/gpu/GrTargetCommands.cpp
+++ b/src/gpu/GrTargetCommands.cpp
@@ -30,11 +30,7 @@
     while (genIter.next()) {
         if (Cmd::kDrawBatch_CmdType == genIter->type()) {
             DrawBatch* db = reinterpret_cast<DrawBatch*>(genIter.get());
-            // TODO: encapsulate the specialization of GrVertexBatch in GrVertexBatch so that we can
-            // remove this cast. Currently all GrDrawBatches are in fact GrVertexBatch.
-            GrVertexBatch* vertexBatch = static_cast<GrVertexBatch*>(db->batch());
-
-            vertexBatch->prepareDraws(&flushState);
+            db->batch()->prepare(&flushState);
         }
     }
 
@@ -77,19 +73,7 @@
 }
 
 void GrTargetCommands::DrawBatch::execute(GrBatchFlushState* state) {
-    // TODO: encapsulate the specialization of GrVertexBatch in GrVertexBatch so that we can
-    // remove this cast. Currently all GrDrawBatches are in fact GrVertexBatch.
-    GrVertexBatch* vertexBatch = static_cast<GrVertexBatch*>(fBatch.get());
-    vertexBatch->issueDraws(state);
-}
-
-
-void GrTargetCommands::Clear::execute(GrBatchFlushState* state) {
-    if (GrColor_ILLEGAL == fColor) {
-        state->gpu()->discard(this->renderTarget());
-    } else {
-        state->gpu()->clear(fRect, fColor, this->renderTarget());
-    }
+    fBatch->draw(state);
 }
 
 void GrTargetCommands::ClearStencilClip::execute(GrBatchFlushState* state) {
diff --git a/src/gpu/GrTargetCommands.h b/src/gpu/GrTargetCommands.h
index 3c08d2d..cf3054d 100644
--- a/src/gpu/GrTargetCommands.h
+++ b/src/gpu/GrTargetCommands.h
@@ -15,7 +15,8 @@
 #include "GrRenderTarget.h"
 #include "GrTRecorder.h"
 
-#include "batches/GrDrawBatch.h"
+#include "batches/GrBatch.h"
+
 #include "SkRect.h"
 
 class GrResourceProvider;
@@ -30,12 +31,11 @@
     public:
         enum CmdType {
             kStencilPath_CmdType       = 1,
-            kClear_CmdType             = 2,
-            kClearStencil_CmdType      = 3,
-            kCopySurface_CmdType       = 4,
-            kDrawPath_CmdType          = 5,
-            kDrawPaths_CmdType         = 6,
-            kDrawBatch_CmdType         = 7,
+            kClearStencil_CmdType      = 2,
+            kCopySurface_CmdType       = 3,
+            kDrawPath_CmdType          = 4,
+            kDrawPaths_CmdType         = 5,
+            kDrawBatch_CmdType         = 6,
         };
 
         Cmd(CmdType type)
@@ -177,21 +177,6 @@
         GrPendingIOResource<const GrPathRange, kRead_GrIOType> fPathRange;
     };
 
-    // This is also used to record a discard by setting the color to GrColor_ILLEGAL
-    struct Clear : public Cmd {
-        Clear(GrRenderTarget* rt) : Cmd(kClear_CmdType), fRenderTarget(rt) {}
-
-        GrRenderTarget* renderTarget() const { return fRenderTarget.get(); }
-
-        void execute(GrBatchFlushState*) override;
-
-        SkIRect fRect;
-        GrColor fColor;
-
-    private:
-        GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
-    };
-
     // This command is ONLY used by the clip mask manager to clear the stencil clip bits
     struct ClearStencilClip : public Cmd {
         ClearStencilClip(GrRenderTarget* rt) : Cmd(kClearStencil_CmdType), fRenderTarget(rt) {}
@@ -228,17 +213,17 @@
     };
 
     struct DrawBatch : public Cmd {
-        DrawBatch(GrDrawBatch* batch)
+        DrawBatch(GrBatch* batch)
             : Cmd(kDrawBatch_CmdType)
             , fBatch(SkRef(batch)){
             SkASSERT(!batch->isUsed());
         }
 
-        GrDrawBatch* batch() { return fBatch; }
+        GrBatch* batch() { return fBatch; }
         void execute(GrBatchFlushState*) override;
 
     private:
-        SkAutoTUnref<GrDrawBatch>   fBatch;
+        SkAutoTUnref<GrBatch>   fBatch;
     };
 
     static const int kCmdBufferInitialSizeInBytes = 8 * 1024;
diff --git a/src/gpu/batches/GrBatch.h b/src/gpu/batches/GrBatch.h
index 7bb6af1..b6eec1f 100644
--- a/src/gpu/batches/GrBatch.h
+++ b/src/gpu/batches/GrBatch.h
@@ -12,8 +12,10 @@
 #include "GrNonAtomicRef.h"
 
 #include "SkRect.h"
+#include "SkString.h"
 
 class GrCaps;
+class GrBatchFlushState;
 
 /**
  * GrBatch is the base class for all Ganesh deferred geometry generators.  To facilitate
@@ -79,6 +81,20 @@
 #endif
     SkDEBUGCODE(bool isUsed() const { return fUsed; })
 
+    /** Called prior to drawing. The batch should perform any resource creation necessary to
+        to quickly issue its draw when draw is called. */
+    void prepare(GrBatchFlushState* state) { this->onPrepare(state); }
+
+    /** Issues the batches commands to GrGpu. */
+    void draw(GrBatchFlushState* state) { this->onDraw(state); }
+
+    /** Used to block batching across render target changes. Remove this once we store
+        GrBatches for different RTs in different targets. */
+    virtual uint32_t renderTargetUniqueID() const = 0;
+
+    /** Used for spewing information about batches when debugging. */
+    virtual SkString dumpInfo() const = 0;
+
 protected:
     template <typename PROC_SUBCLASS> void initClassID() {
          static uint32_t kClassID = GenID(&gCurrBatchClassID);
@@ -98,6 +114,9 @@
 private:
     virtual bool onCombineIfPossible(GrBatch*, const GrCaps& caps) = 0;
 
+    virtual void onPrepare(GrBatchFlushState*) = 0;
+    virtual void onDraw(GrBatchFlushState*) = 0;
+
     static uint32_t GenID(int32_t* idCounter) {
         // fCurrProcessorClassID has been initialized to kIllegalProcessorClassID. The
         // atomic inc returns the old value not the incremented value. So we add
diff --git a/src/gpu/batches/GrClearBatch.h b/src/gpu/batches/GrClearBatch.h
new file mode 100644
index 0000000..b36a53f
--- /dev/null
+++ b/src/gpu/batches/GrClearBatch.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrClearBatch_DEFINED
+#define GrClearBatch_DEFINED
+
+#include "GrBatch.h"
+#include "GrBatchFlushState.h"
+#include "GrGpu.h"
+#include "GrRenderTarget.h"
+
+class GrClearBatch final : public GrBatch {
+public:
+    GrClearBatch(const SkIRect& rect,  GrColor color, GrRenderTarget* rt)
+        : fRect(rect)
+        , fColor(color)
+        , fRenderTarget(rt) {
+        this->initClassID<GrClearBatch>();
+        fBounds = SkRect::Make(rect);
+    }
+
+    const char* name() const override { return "Clear"; }
+
+    uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+
+    SkString dumpInfo() const {
+        SkString string;
+        string.printf("Color: 0x%08x, Rect [L: %d, T: %d, R: %d, B: %d], RT: 0x%p",
+                      fColor, fRect.fLeft, fRect.fTop, fRect.fRight, fRect.fBottom,
+                      fRenderTarget.get());
+        return string;
+    }
+
+private:
+    bool onCombineIfPossible(GrBatch* t, const GrCaps& caps) override {
+        // We could combine clears. TBD how much complexity to put here.
+        return false;
+    }
+
+    void onPrepare(GrBatchFlushState*) override {}
+
+    void onDraw(GrBatchFlushState* state) override {
+        state->gpu()->clear(fRect, fColor, fRenderTarget.get());
+    }
+
+    SkIRect                                                 fRect;
+    GrColor                                                 fColor;
+    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType>    fRenderTarget;
+};
+
+#endif
diff --git a/src/gpu/batches/GrDiscardBatch.h b/src/gpu/batches/GrDiscardBatch.h
new file mode 100644
index 0000000..9d9570f
--- /dev/null
+++ b/src/gpu/batches/GrDiscardBatch.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDiscardBatch_DEFINED
+#define GrDiscardBatch_DEFINED
+
+#include "GrBatch.h"
+#include "GrBatchFlushState.h"
+#include "GrGpu.h"
+#include "GrRenderTarget.h"
+
+class GrDiscardBatch final : public GrBatch {
+public:
+    GrDiscardBatch(GrRenderTarget* rt)
+        : fRenderTarget(rt) {
+        this->initClassID<GrDiscardBatch>();
+        fBounds = SkRect::MakeWH(SkIntToScalar(rt->width()), SkIntToScalar(rt->height()));
+    }
+
+    const char* name() const override { return "Discard"; }
+
+    uint32_t renderTargetUniqueID() const override { return fRenderTarget.get()->getUniqueID(); }
+
+    SkString dumpInfo() const {
+        SkString string;
+        string.printf("RT: 0x%p", fRenderTarget.get());
+        return string;
+    }
+
+private:
+    bool onCombineIfPossible(GrBatch* that, const GrCaps& caps) override {
+        return fRenderTarget == that->cast<GrDiscardBatch>()->fRenderTarget;
+    }
+
+    void onPrepare(GrBatchFlushState*) override {}
+
+    void onDraw(GrBatchFlushState* state) override {
+        state->gpu()->discard(fRenderTarget.get());
+    }
+
+    GrPendingIOResource<GrRenderTarget, kWrite_GrIOType> fRenderTarget;
+};
+
+#endif
diff --git a/src/gpu/batches/GrDrawBatch.h b/src/gpu/batches/GrDrawBatch.h
index bbebe5b..ed33816 100644
--- a/src/gpu/batches/GrDrawBatch.h
+++ b/src/gpu/batches/GrDrawBatch.h
@@ -56,6 +56,25 @@
     // TODO no GrPrimitiveProcessors yet read fragment position
     bool willReadFragmentPosition() const { return false; }
 
+    uint32_t renderTargetUniqueID() const final {
+        SkASSERT(fPipelineInstalled);
+        return this->pipeline()->getRenderTarget()->getUniqueID();
+    }
+
+    SkString dumpInfo() const override {
+        SkString string;
+        string.append("ColorStages:\n");
+        for (int i = 0; i < this->pipeline()->numColorFragmentStages(); i++) {
+            string.appendf("\t\t%s\n", this->pipeline()->getColorStage(i).processor()->name());
+        }
+        string.append("CoverageStages:\n");
+        for (int i = 0; i < this->pipeline()->numCoverageFragmentStages(); i++) {
+            string.appendf("\t%s\n", this->pipeline()->getCoverageStage(i).processor()->name());
+        }
+        string.appendf("XP: %s\n", this->pipeline()->getXferProcessor()->name());
+        return string;
+    }
+
 private:
     /**
      * initBatchTracker is a hook for the some additional overrides / optimization possibilities
diff --git a/src/gpu/batches/GrVertexBatch.cpp b/src/gpu/batches/GrVertexBatch.cpp
index 6081e26..d61b511 100644
--- a/src/gpu/batches/GrVertexBatch.cpp
+++ b/src/gpu/batches/GrVertexBatch.cpp
@@ -11,7 +11,7 @@
 
 GrVertexBatch::GrVertexBatch() : fDrawArrays(1) {}
 
-void GrVertexBatch::prepareDraws(GrBatchFlushState* state) {
+void GrVertexBatch::onPrepare(GrBatchFlushState* state) {
     Target target(state, this);
     this->onPrepareDraws(&target);
 }
@@ -59,7 +59,7 @@
                                  quadIndexBuffer, kVerticesPerQuad, kIndicesPerQuad, quadsToDraw);
 }
 
-void GrVertexBatch::issueDraws(GrBatchFlushState* state) {
+void GrVertexBatch::onDraw(GrBatchFlushState* state) {
     int uploadCnt = fInlineUploads.count();
     int currUpload = 0;
 
diff --git a/src/gpu/batches/GrVertexBatch.h b/src/gpu/batches/GrVertexBatch.h
index b868962..0ff5ac1 100644
--- a/src/gpu/batches/GrVertexBatch.h
+++ b/src/gpu/batches/GrVertexBatch.h
@@ -26,9 +26,6 @@
 
     GrVertexBatch();
 
-    void prepareDraws(GrBatchFlushState* state);
-    void issueDraws(GrBatchFlushState* state);
-
 protected:
     /** Helper for rendering instances using an instanced index index buffer. This class creates the
         space for the vertices and flushes the draws to the batch target. */
@@ -65,6 +62,9 @@
     };
 
 private:
+    void onPrepare(GrBatchFlushState* state) final;
+    void onDraw(GrBatchFlushState* state) final;
+
     virtual void onPrepareDraws(Target*) = 0;
 
     // A set of contiguous draws with no inline uploads between them that all use the same