Tesselate path once for tiled offscreen AA

Review URL: http://codereview.appspot.com/4661062/


git-svn-id: http://skia.googlecode.com/svn/trunk@1777 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 0528267..a8810f9 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -580,7 +580,7 @@
 
     void drawClipIntoStencil();
 
-    GrPathRenderer* getPathRenderer(const GrDrawTarget*, const GrPath&, GrPathFill);
+    GrPathRenderer* getPathRenderer(const GrPath&, GrPathFill);
 
     struct OffscreenRecord;
 
diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h
index d77aad6..6bc91b5 100644
--- a/gpu/include/GrPathRenderer.h
+++ b/gpu/include/GrPathRenderer.h
@@ -18,57 +18,46 @@
 #define GrPathRenderer_DEFINED
 
 #include "GrDrawTarget.h"
+#include "SkTemplates.h"
 
 class SkPath;
 struct GrPoint;
 
 /**
  *  Base class for drawing paths into a GrDrawTarget.
+ *  Paths may be drawn multiple times as when tiling for supersampling. The 
+ *  calls on GrPathRenderer to draw a path will look like this:
+ *  
+ *  pr->setPath(target, path, fill, translate); // sets the path to draw
+ *      pr->drawPath(...);  // draw the path
+ *      pr->drawPath(...);
+ *      ...
+ *  pr->clearPath();  // finished with the path
  */
 class GR_API GrPathRenderer : public GrRefCnt {
 public:
     GrPathRenderer(void);
-
     /**
      * Returns true if this path renderer is able to render the path.
      * Returning false allows the caller to fallback to another path renderer.
      *
-     * @param target    The target to draw into
      * @param path      The path to draw
      * @param fill      The fill rule to use
      *
      * @return  true if the path can be drawn by this object, false otherwise.
      */
-    virtual bool canDrawPath(const GrDrawTarget* target, const SkPath& path,
-                             GrPathFill fill) const = 0;
-
-    /**
-     * Draws a path into the draw target. The target will already have its draw
-     * state configured for the draw.
-     * @param target                the target to draw into.
-     * @param stages                indicates which stages the are already
-     *                              in use. All enabled stages expect positions
-     *                              as texture coordinates. The path renderer
-     *                              use the remaining stages for its path
-     *                              filling algorithm.
-     * @param path                  the path to draw.
-     * @param fill                  the fill rule to apply.
-     * @param translate             optional additional translation to apply to
-     *                              the path. NULL means (0,0).
-     */
-    virtual void drawPath(GrDrawTarget* target,
-                          GrDrawTarget::StageBitfield stages,
-                          const SkPath& path,
-                          GrPathFill fill,
-                          const GrPoint* translate) = 0;
+    virtual bool canDrawPath(const SkPath& path, GrPathFill fill) const = 0;
 
     /**
      * For complex clips Gr uses the stencil buffer. The path renderer must be
      * able to render paths into the stencil buffer. However, the path renderer
-     * itself may require the stencil buffer to resolve the path fill rule. This
-     * function queries whether the path render needs its own stencil
+     * itself may require the stencil buffer to resolve the path fill rule.
+     * This function queries whether the path render needs its own stencil
      * pass. If this returns false then drawPath() should not modify the
-     * the target's stencil settings but use those already set on target.
+     * the target's stencil settings but use those already set on target. The
+     * target is passed as a param in case the answer depends upon draw state.
+     * The view matrix and render target set on the draw target may change 
+     * before setPath/drawPath is called and so shouldn't be considered.
      *
      * @param target target that the path will be rendered to
      * @param path   the path that will be drawn
@@ -85,7 +74,59 @@
                                      GrPathFill fill) const { return false; }
 
     /**
-     * Draws a path to the stencil buffer. Assume the writable stencil bits
+     * @return true if the path renderer can perform anti-aliasing (aside from
+     * having FSAA enabled for a render target). Target is provided to
+     * communicate the draw state (blend mode, stage settings, etc).
+     */
+    virtual bool supportsAA(GrDrawTarget* target,
+                            const SkPath& path,
+                            GrPathFill fill) { return false; }
+
+    /**
+     * Sets the path to render and target to render into. All calls to drawPath
+     * and drawPathToStencil must occur between setPath and clearPath. The
+     * path cannot be modified externally between setPath and clearPath. The
+     * path may be drawn several times (e.g. tiled supersampler). The target's
+     * state may change between setPath and drawPath* calls. However, if the
+     * path renderer specified vertices/indices during setPath or drawPath*
+     * they will still be set at subsequent drawPath* calls until the next
+     * clearPath. The target's draw state may change between drawPath* calls
+     * so if the subclass does any caching of tesselation, etc. then it must
+     * validate that target parameters that guided the decisions still hold.
+     *
+     * @param target                the target to draw into.
+     * @param path                  the path to draw.
+     * @param fill                  the fill rule to apply.
+     * @param translate             optional additional translation to apply to
+     *                              the path. NULL means (0,0).
+     */
+    void setPath(GrDrawTarget* target,
+                 const SkPath* path,
+                 GrPathFill fill,
+                 const GrPoint* translate);
+
+    /**
+     * Notifies path renderer that path set in setPath is no longer in use.
+     */
+    void clearPath();
+
+    /**
+     * Draws the path into the draw target. If requiresStencilBuffer returned
+     * false then the target may be setup for stencil rendering (since the 
+     * path renderer didn't claim that it needs to use the stencil internally).
+     *
+     * Only called between setPath / clearPath.
+     *
+     * @param stages                bitfield that indicates which stages are
+     *                              in use. All enabled stages expect positions
+     *                              as texture coordinates. The path renderer
+     *                              use the remaining stages for its path
+     *                              filling algorithm.
+     */
+    virtual void drawPath(GrDrawTarget::StageBitfield stages) = 0;
+
+    /**
+     * Draws the path to the stencil buffer. Assume the writable stencil bits
      * are already initialized to zero. Fill will always be either
      * kWinding_PathFill or kEvenOdd_PathFill.
      *
@@ -95,27 +136,11 @@
      * The default implementation assumes the path filling algorithm doesn't
      * require a separate stencil pass and so crashes.
      *
-     *
-     * @param target                the target to draw into.
-     * @param path                  the path to draw.
-     * @param fill                  the fill rule to apply.
-     * @param translate             optional additional translation to apply to
-     *                              the path. NULL means (0,0).
+     * Only called between setPath / clearPath.
      */
-    virtual void drawPathToStencil(GrDrawTarget* target,
-                                   const SkPath& path,
-                                   GrPathFill fill,
-                                   const GrPoint* translate) {
+    virtual void drawPathToStencil() {
         GrCrash("Unexpected call to drawPathToStencil.");
     }
-    
-    /**
-     * @return true if the path renderer can perform anti-aliasing (aside from
-     * having FSAA enabled for a render target)
-     */
-    virtual bool supportsAA(GrDrawTarget* target,
-                            const SkPath& path,
-                            GrPathFill fill) { return false; }
 
     /**
      * This is called to install a custom path renderer in every GrContext at
@@ -134,8 +159,56 @@
         fCurveTolerance = SkScalarMul(fCurveTolerance, multiplier);
     }
 
+    /**
+     * Helper that sets a path and automatically remove it in destructor.
+     */
+    class AutoClearPath {
+    public:
+        AutoClearPath() {
+            fPathRenderer = NULL;
+        }
+        AutoClearPath(GrPathRenderer* pr,
+                       GrDrawTarget* target,
+                       const SkPath* path,
+                       GrPathFill fill,
+                       const GrPoint* translate) {
+            GrAssert(NULL != pr);
+            pr->setPath(target, path, fill, translate);
+            fPathRenderer = pr;
+        }
+        void set(GrPathRenderer* pr,
+                 GrDrawTarget* target,
+                 const SkPath* path,
+                 GrPathFill fill,
+                 const GrPoint* translate) {
+            if (NULL != fPathRenderer) {
+                fPathRenderer->clearPath();
+            }
+            GrAssert(NULL != pr);
+            pr->setPath(target, path, fill, translate);
+            fPathRenderer = pr;
+        }
+        ~AutoClearPath() {
+            if (NULL != fPathRenderer) {
+                fPathRenderer->clearPath();
+            }
+        }
+    private:
+        GrPathRenderer* fPathRenderer;
+    };
+
 protected:
+
+    // subclass can override these to be notified just after a path is set
+    // and just before the path is cleared.
+    virtual void pathWasSet() {}
+    virtual void pathWillClear() {}
+
     GrScalar fCurveTolerance;
+    const SkPath*               fPath;
+    GrDrawTarget*               fTarget;
+    GrPathFill                  fFill;
+    GrPoint                     fTranslate;
 
 private:
 
@@ -151,35 +224,37 @@
     GrDefaultPathRenderer(bool separateStencilSupport,
                           bool stencilWrapOpsSupport);
 
-    virtual bool canDrawPath(const GrDrawTarget* target,
-                             const SkPath& path,
+    virtual bool canDrawPath(const SkPath& path,
                              GrPathFill fill) const { return true; }
 
-    virtual void drawPath(GrDrawTarget* target,
-                          GrDrawTarget::StageBitfield stages,
-                          const SkPath& path,
-                          GrPathFill fill,
-                          const GrPoint* translate);
     virtual bool requiresStencilPass(const GrDrawTarget* target,
                                      const SkPath& path,
                                      GrPathFill fill) const;
-    virtual void drawPathToStencil(GrDrawTarget* target,
-                                   const SkPath& path,
-                                   GrPathFill fill,
-                                   const GrPoint* translate);
+
+    virtual void drawPath(GrDrawTarget::StageBitfield stages);
+    virtual void drawPathToStencil();
+
+protected:
+    virtual void pathWillClear();
+
 private:
 
-    void onDrawPath(GrDrawTarget* target,
-                    GrDrawTarget::StageBitfield stages,
-                    const SkPath& path,
-                    GrPathFill fill,
-                    const GrPoint* translate,
-                    bool stencilOnly);
+    void onDrawPath(GrDrawTarget::StageBitfield stages, bool stencilOnly);
+
+    void createGeom(GrScalar srcSpaceTolSqd,
+                    GrDrawTarget::StageBitfield stages);
 
     bool    fSeparateStencil;
     bool    fStencilWrapOps;
 
+    int                         fSubpathCount;
+    SkAutoSTMalloc<8, uint16_t> fSubpathVertCount;
+    GrScalar                    fPreviousSrcTol;
+    GrDrawTarget::StageBitfield fPreviousStages;
+
+
     typedef GrPathRenderer INHERITED;
 };
 
 #endif
+
diff --git a/gpu/include/GrScalar.h b/gpu/include/GrScalar.h
index 35cd61a..409e80b 100644
--- a/gpu/include/GrScalar.h
+++ b/gpu/include/GrScalar.h
@@ -39,7 +39,8 @@
 #define GrIntToScalar(a)    SkIntToScalar(a)
 #define GrScalarHalf(a)     SkScalarHalf(a)
 #define GrScalarAve(a,b)    SkScalarAve(a,b)
-#define GrMul(a,b)          SkScalarMul(a,b)
+#define GrMul(a,b)          SkScalarMul(a,b) // deprecated, prefer GrScalarMul
+#define GrScalarMul(a,b)    SkScalarMul(a,b)
 #define GrScalarDiv(a,b)    SkScalarDiv(a, b)
 #define GrScalarToFloat(a)  SkScalarToFloat(a)
 #define GrFloatToScalar(a)  SkScalarToFloat(a)
diff --git a/gpu/include/GrTesselatedPathRenderer.h b/gpu/include/GrTesselatedPathRenderer.h
index e37e66b..dac9a17 100644
--- a/gpu/include/GrTesselatedPathRenderer.h
+++ b/gpu/include/GrTesselatedPathRenderer.h
@@ -23,22 +23,14 @@
 public:
     GrTesselatedPathRenderer();
 
-    virtual void drawPath(GrDrawTarget* target,
-                          GrDrawTarget::StageBitfield stages,
-                          const GrPath& path,
-                          GrPathFill fill,
-                          const GrPoint* translate);
-    virtual bool canDrawPath(const GrDrawTarget* target,
-                             const GrPath& path,
+    virtual void drawPath(GrDrawTarget::StageBitfield stages);
+    virtual bool canDrawPath(const GrPath& path,
                              GrPathFill fill) const;
 
     virtual bool requiresStencilPass(const GrDrawTarget* target,
                                      const GrPath& path,
                                      GrPathFill fill) const { return false; }
-    virtual void drawPathToStencil(GrDrawTarget* target,
-                                   const GrPath& path,
-                                   GrPathFill fill,
-                                   const GrPoint* translate);
+    virtual void drawPathToStencil();
     virtual bool supportsAA(GrDrawTarget* target,
                             const GrPath& path,
                             GrPathFill fill);
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 4982704..eee73d9 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -715,7 +715,7 @@
                                  OffscreenRecord* record) {
     SK_TRACE_EVENT0("GrContext::doOffscreenAAPass2");
     GrAssert(NULL != record->fEntry0);
-    
+    GrDrawTarget::AutoGeometryPush agp(target);
     GrIRect tileRect;
     tileRect.fLeft = boundRect.fLeft + tileX * record->fTileSizeX;
     tileRect.fTop  = boundRect.fTop  + tileY * record->fTileSizeY,
@@ -1321,7 +1321,9 @@
                          GrPathFill fill, const GrPoint* translate) {
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-    GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
+    GrPathRenderer* pr = this->getPathRenderer(path, fill);
+    GrPathRenderer::AutoClearPath arp(pr, target, &path, fill, translate);
+    GrDrawTarget::StageBitfield stageMask = paint.getActiveStageMask();
 
     if (!pr->supportsAA(target, path, fill) &&
         this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
@@ -1357,13 +1359,12 @@
             for (int tx = 0; tx < record.fTileCountX; ++tx) {
                 for (int ty = 0; ty < record.fTileCountY; ++ty) {
                     this->setupOffscreenAAPass1(target, bound, tx, ty, &record);
-                    pr->drawPath(target, 0, path, fill, translate);
+                    pr->drawPath(0);
                     this->doOffscreenAAPass2(target, paint, bound, tx, ty, &record);
                 }
             }
             this->cleanupOffscreenAA(target, pr, &record);
             if (IsFillInverted(fill) && bound != clipIBounds) {
-                int stageMask = paint.getActiveStageMask();
                 GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
                 GrRect rect;
                 if (clipIBounds.fTop < bound.fTop) {
@@ -1389,11 +1390,8 @@
             }
             return;
         }
-    }
-
-    GrDrawTarget::StageBitfield enabledStages = paint.getActiveStageMask();
-
-    pr->drawPath(target, enabledStages, path, fill, translate);
+    } 
+    pr->drawPath(stageMask);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1690,14 +1688,13 @@
     return fGpu->getQuadIndexBuffer();
 }
 
-GrPathRenderer* GrContext::getPathRenderer(const GrDrawTarget* target,
-                                           const GrPath& path,
+GrPathRenderer* GrContext::getPathRenderer(const GrPath& path,
                                            GrPathFill fill) {
     if (NULL != fCustomPathRenderer &&
-        fCustomPathRenderer->canDrawPath(target, path, fill)) {
+        fCustomPathRenderer->canDrawPath(path, fill)) {
         return fCustomPathRenderer;
     } else {
-        GrAssert(fDefaultPathRenderer.canDrawPath(target, path, fill));
+        GrAssert(fDefaultPathRenderer.canDrawPath(path, fill));
         return &fDefaultPathRenderer;
     }
 }
diff --git a/gpu/src/GrGpu.cpp b/gpu/src/GrGpu.cpp
index 192d29b..62c96f5 100644
--- a/gpu/src/GrGpu.cpp
+++ b/gpu/src/GrGpu.cpp
@@ -24,9 +24,8 @@
 #include "GrPathRenderer.h"
 
 // probably makes no sense for this to be less than a page
-static const size_t VERTEX_POOL_VB_SIZE = 1 << 12;
-static const int VERTEX_POOL_VB_COUNT = 1;
-
+static const size_t VERTEX_POOL_VB_SIZE = 1 << 18;
+static const int VERTEX_POOL_VB_COUNT = 4;
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -454,6 +453,7 @@
             // with the existing clip.
             for (int c = firstElement; c < count; ++c) {
                 GrPathFill fill;
+                bool fillInverted;
                 // enabled at bottom of loop
                 this->disableState(kModifyStencilClip_StateBit);
 
@@ -465,16 +465,20 @@
 
                 GrPathRenderer* pr = NULL;
                 const GrPath* clipPath = NULL;
+                GrPathRenderer::AutoClearPath arp;
                 if (kRect_ClipType == clip.getElementType(c)) {
                     canRenderDirectToStencil = true;
                     fill = kEvenOdd_PathFill;
+                    fillInverted = false;
                 } else {
                     fill = clip.getPathFill(c);
+                    fillInverted = IsFillInverted(fill);
+                    fill = NonInvertedFill(fill);
                     clipPath = &clip.getPath(c);
-                    pr = this->getClipPathRenderer(*clipPath, NonInvertedFill(fill));
+                    pr = this->getClipPathRenderer(*clipPath, fill);
                     canRenderDirectToStencil =
-                        !pr->requiresStencilPass(this, *clipPath,
-                                                 NonInvertedFill(fill));
+                        !pr->requiresStencilPass(this, *clipPath, fill);
+                    arp.set(pr, this, clipPath, fill, NULL);
                 }
 
                 GrSetOp op = firstElement == c ? kReplace_SetOp : clip.getOp(c);
@@ -489,7 +493,7 @@
                     GrStencilSettings::GetClipPasses(op,
                                                      canRenderDirectToStencil,
                                                      clipBit,
-                                                     IsFillInverted(fill),
+                                                     fillInverted,
                                                      &passes, stencilSettings);
 
                 // draw the element to the client stencil bits if necessary
@@ -509,12 +513,9 @@
                     } else {
                         if (canRenderDirectToStencil) {
                             this->setStencil(gDrawToStencil);
-                            pr->drawPath(this, 0, *clipPath, NonInvertedFill(fill),
-                                         NULL);
+                            pr->drawPath(0);
                         } else {
-                            pr->drawPathToStencil(this, *clipPath,
-                                                  NonInvertedFill(fill),
-                                                  NULL);
+                            pr->drawPathToStencil();
                         }
                     }
                 }
@@ -530,8 +531,7 @@
                             this->drawSimpleRect(clip.getRect(c), NULL, 0);
                         } else {
                             SET_RANDOM_COLOR
-                            GrAssert(!IsFillInverted(fill));
-                            pr->drawPath(this, 0, *clipPath, fill, NULL);
+                            pr->drawPath(0);
                         }
                     } else {
                         SET_RANDOM_COLOR
@@ -558,7 +558,7 @@
 GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
                                            GrPathFill fill) {
     if (NULL != fClientPathRenderer &&
-        fClientPathRenderer->canDrawPath(this, path, fill)) {
+        fClientPathRenderer->canDrawPath(path, fill)) {
             return fClientPathRenderer;
     } else {
         if (NULL == fDefaultPathRenderer) {
@@ -566,7 +566,7 @@
                 new GrDefaultPathRenderer(this->supportsTwoSidedStencil(),
                                           this->supportsStencilWrapOps());
         }
-        GrAssert(fDefaultPathRenderer->canDrawPath(this, path, fill));
+        GrAssert(fDefaultPathRenderer->canDrawPath(path, fill));
         return fDefaultPathRenderer;
     }
 }
diff --git a/gpu/src/GrPathRenderer.cpp b/gpu/src/GrPathRenderer.cpp
index 1e3c645..b103880 100644
--- a/gpu/src/GrPathRenderer.cpp
+++ b/gpu/src/GrPathRenderer.cpp
@@ -11,15 +11,49 @@
 #include SK_USER_TRACE_INCLUDE_FILE
 
 GrPathRenderer::GrPathRenderer()
-    : fCurveTolerance (GR_Scalar1) {
-
+    : fCurveTolerance (GR_Scalar1)
+    , fPath(NULL)
+    , fTarget(NULL) {
 }
-   
+
+
+void GrPathRenderer::setPath(GrDrawTarget* target,
+                             const SkPath* path,
+                             GrPathFill fill,
+                             const GrPoint* translate) {
+    GrAssert(NULL == fPath);
+    GrAssert(NULL == fTarget);
+    GrAssert(NULL != target);
+
+    fTarget = target;
+    fPath = path;
+    fFill = fill;
+    if (NULL != translate) {
+        fTranslate = *translate;
+    } else {
+        fTranslate.fX = fTranslate.fY = 0;
+    }
+    this->pathWasSet();
+}
+
+void GrPathRenderer::clearPath() {
+    this->pathWillClear();
+    fTarget->resetVertexSource();
+    fTarget = NULL;
+    fPath = NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 GrDefaultPathRenderer::GrDefaultPathRenderer(bool separateStencilSupport,
                                              bool stencilWrapOpsSupport)
     : fSeparateStencil(separateStencilSupport)
-    , fStencilWrapOps(stencilWrapOpsSupport) {
-
+    , fStencilWrapOps(stencilWrapOpsSupport)
+    , fSubpathCount(0)
+    , fSubpathVertCount(0)
+    , fPreviousSrcTol(-GR_Scalar1)
+    , fPreviousStages(-1) {
+    fTarget = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -189,21 +223,105 @@
     return !single_pass_path(*target, path, fill);
 }
 
-void GrDefaultPathRenderer::onDrawPath(GrDrawTarget* target,
-                                       GrDrawTarget::StageBitfield stages,
-                                       const GrPath& path,
-                                       GrPathFill fill,
-                                       const GrPoint* translate,
+void GrDefaultPathRenderer::pathWillClear() {
+    fSubpathVertCount.realloc(0);
+    fTarget->resetVertexSource();
+    fPreviousSrcTol = -GR_Scalar1;
+    fPreviousStages = -1;
+}
+
+void GrDefaultPathRenderer::createGeom(GrScalar srcSpaceTol, 
+                                       GrDrawTarget::StageBitfield stages) {
+    {
+    SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
+
+    fPreviousSrcTol = srcSpaceTol;
+    fPreviousStages = stages;
+
+    GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
+    int maxPts = GrPathUtils::worstCasePointCount(*fPath, &fSubpathCount,
+                                                  srcSpaceTol);
+
+    GrVertexLayout layout = 0;
+    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
+        if ((1 << s) & stages) {
+            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
+        }
+    }
+
+    // add 4 to hold the bounding rect
+    GrPoint* base;
+    fTarget->reserveVertexSpace(layout, maxPts + 4, (void**)&base);
+
+    GrPoint* vert = base;
+    GrPoint* subpathBase = base;
+
+    fSubpathVertCount.realloc(fSubpathCount);
+
+    GrPoint pts[4];
+
+    bool first = true;
+    int subpath = 0;
+
+    SkPath::Iter iter(*fPath, false);
+
+    for (;;) {
+        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
+        switch (cmd) {
+            case kMove_PathCmd:
+                if (!first) {
+                    fSubpathVertCount[subpath] = vert-subpathBase;
+                    subpathBase = vert;
+                    ++subpath;
+                }
+                *vert = pts[0];
+                vert++;
+                break;
+            case kLine_PathCmd:
+                *vert = pts[1];
+                vert++;
+                break;
+            case kQuadratic_PathCmd: {
+                GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
+                                                     srcSpaceTolSqd, &vert,
+                                                     GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
+                break;
+            }
+            case kCubic_PathCmd: {
+                GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
+                                                 srcSpaceTolSqd, &vert,
+                                                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
+                break;
+            }
+            case kClose_PathCmd:
+                break;
+            case kEnd_PathCmd:
+                fSubpathVertCount[subpath] = vert-subpathBase;
+                ++subpath; // this could be only in debug
+                goto FINISHED;
+        }
+        first = false;
+    }
+FINISHED:
+    GrAssert(subpath == fSubpathCount);
+    GrAssert((vert - base) <= maxPts);
+
+    if (fTranslate.fX || fTranslate.fY) {
+        int count = vert - base;
+        for (int i = 0; i < count; i++) {
+            base[i].offset(fTranslate.fX, fTranslate.fY);
+        }
+    }
+    }
+}
+
+void GrDefaultPathRenderer::onDrawPath(GrDrawTarget::StageBitfield stages,
                                        bool stencilOnly) {
+
     SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath",
                     "points", SkStringPrintf("%i", path.countPoints()).c_str());
 
-    GrDrawTarget::AutoStateRestore asr(target);
-    bool colorWritesWereDisabled = target->isColorWriteDisabled();
-    // face culling doesn't make sense here
-    GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
-
-    GrMatrix viewM = target->getViewMatrix();
+    GrMatrix viewM = fTarget->getViewMatrix();
     // In order to tesselate the path we get a bound on how much the matrix can
     // stretch when mapping to screen coordinates.
     GrScalar stretch = viewM.getMaxStretch();
@@ -216,28 +334,25 @@
     } else {
         tol = GrScalarDiv(tol, stretch);
     }
-    GrScalar tolSqd = GrMul(tol, tol);
-
-    int subpathCnt;
-    int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
-
-    GrVertexLayout layout = 0;
-    for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
-        if ((1 << s) & stages) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
+    // FIXME: It's really dumb that we recreate the verts for a new vertex
+    // layout. We only do that because the GrDrawTarget API doesn't allow
+    // us to change the vertex layout after reserveVertexSpace(). We won't
+    // actually change the vertex data when the layout changes since all the
+    // stages reference the positions (rather than having separate tex coords)
+    // and we don't ever have per-vert colors. In practice our call sites
+    // won't change the stages in use inside a setPath / removePath pair. But
+    // it is a silly limitation of the GrDrawTarget design that should be fixed.
+    if (tol != fPreviousSrcTol ||
+        stages != fPreviousStages) {
+        this->createGeom(tol, stages);
     }
 
-    // add 4 to hold the bounding rect
-    GrDrawTarget::AutoReleaseGeometry arg(target, layout, maxPts + 4, 0);
+    GrAssert(NULL != fTarget);
+    GrDrawTarget::AutoStateRestore asr(fTarget);
+    bool colorWritesWereDisabled = fTarget->isColorWriteDisabled();
+    // face culling doesn't make sense here
+    GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
 
-    GrPoint* base = (GrPoint*) arg.vertices();
-    GrPoint* vert = base;
-    GrPoint* subpathBase = base;
-
-    SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
-
-    // TODO: use primitve restart if available rather than multiple draws
     GrPrimitiveType             type;
     int                         passCount = 0;
     const GrStencilSettings*    passes[3];
@@ -245,7 +360,7 @@
     bool                        reverse = false;
     bool                        lastPassIsBounds;
 
-    if (kHairLine_PathFill == fill) {
+    if (kHairLine_PathFill == fFill) {
         type = kLineStrip_PrimitiveType;
         passCount = 1;
         if (stencilOnly) {
@@ -257,7 +372,7 @@
         drawFace[0] = GrDrawTarget::kBoth_DrawFace;
     } else {
         type = kTriangleFan_PrimitiveType;
-        if (single_pass_path(*target, path, fill)) {
+        if (single_pass_path(*fTarget, *fPath, fFill)) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
@@ -267,7 +382,7 @@
             drawFace[0] = GrDrawTarget::kBoth_DrawFace;
             lastPassIsBounds = false;
         } else {
-            switch (fill) {
+            switch (fFill) {
                 case kInverseEvenOdd_PathFill:
                     reverse = true;
                     // fallthrough
@@ -327,140 +442,62 @@
                     }
                     break;
                 default:
-                    GrAssert(!"Unknown path fill!");
+                    GrAssert(!"Unknown path fFill!");
                     return;
             }
         }
     }
 
-    GrPoint pts[4];
-
-    bool first = true;
-    int subpath = 0;
-
-    SkPath::Iter iter(path, false);
-
-    {
-    SK_TRACE_EVENT0("GrDefaultPathRenderer::onDrawPath::assembleVerts");
-    for (;;) {
-
-        GrPathCmd cmd = (GrPathCmd)iter.next(pts);
-        switch (cmd) {
-            case kMove_PathCmd:
-                if (!first) {
-                    subpathVertCount[subpath] = vert-subpathBase;
-                    subpathBase = vert;
-                    ++subpath;
-                }
-                *vert = pts[0];
-                vert++;
-                break;
-            case kLine_PathCmd:
-                *vert = pts[1];
-                vert++;
-                break;
-            case kQuadratic_PathCmd: {
-                GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
-                                                     tolSqd, &vert,
-                                                     GrPathUtils::quadraticPointCount(pts, tol));
-                break;
-            }
-            case kCubic_PathCmd: {
-                GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
-                                                 tolSqd, &vert,
-                                                 GrPathUtils::cubicPointCount(pts, tol));
-                break;
-            }
-            case kClose_PathCmd:
-                break;
-            case kEnd_PathCmd:
-                subpathVertCount[subpath] = vert-subpathBase;
-                ++subpath; // this could be only in debug
-                goto FINISHED;
-        }
-        first = false;
-    }
-    }
-FINISHED:
-    GrAssert(subpath == subpathCnt);
-    GrAssert((vert - base) <= maxPts);
-
-    if (translate) {
-        int count = vert - base;
-        for (int i = 0; i < count; i++) {
-            base[i].offset(translate->fX, translate->fY);
-        }
-    }
-
-    // if we're stenciling we will follow with a pass that draws
-    // a bounding rect to set the color. We're stenciling when
-    // passCount > 1.
-    const int& boundVertexStart = maxPts;
-    GrPoint* boundsVerts = base + boundVertexStart;
-    if (lastPassIsBounds) {
-        GrRect bounds;
-        if (reverse) {
-            GrAssert(NULL != target->getRenderTarget());
-            // draw over the whole world.
-            bounds.setLTRB(0, 0,
-                           GrIntToScalar(target->getRenderTarget()->width()),
-                           GrIntToScalar(target->getRenderTarget()->height()));
-            GrMatrix vmi;
-            if (target->getViewInverse(&vmi)) {
-                vmi.mapRect(&bounds);
-            }
-        } else {
-            bounds.setBounds((GrPoint*)base, vert - base);
-        }
-        boundsVerts[0].setRectFan(bounds.fLeft, bounds.fTop, bounds.fRight,
-                                  bounds.fBottom);
-    }
-
     {
     SK_TRACE_EVENT1("GrDefaultPathRenderer::onDrawPath::renderPasses",
                     "verts", SkStringPrintf("%i", vert - base).c_str());
     for (int p = 0; p < passCount; ++p) {
-        target->setDrawFace(drawFace[p]);
+        fTarget->setDrawFace(drawFace[p]);
         if (NULL != passes[p]) {
-            target->setStencil(*passes[p]);
+            fTarget->setStencil(*passes[p]);
         }
 
         if (lastPassIsBounds && (p == passCount-1)) {
             if (!colorWritesWereDisabled) {
-                target->disableState(GrDrawTarget::kNoColorWrites_StateBit);
+                fTarget->disableState(GrDrawTarget::kNoColorWrites_StateBit);
             }
-            target->drawNonIndexed(kTriangleFan_PrimitiveType,
-                                   boundVertexStart, 4);
-
+            GrRect bounds;
+            if (reverse) {
+                GrAssert(NULL != fTarget->getRenderTarget());
+                // draw over the whole world.
+                bounds.setLTRB(0, 0,
+                               GrIntToScalar(fTarget->getRenderTarget()->width()),
+                               GrIntToScalar(fTarget->getRenderTarget()->height()));
+                GrMatrix vmi;
+                if (fTarget->getViewInverse(&vmi)) {
+                    vmi.mapRect(&bounds);
+                }
+            } else {
+                bounds = fPath->getBounds();
+            }
+            GrDrawTarget::AutoGeometryPush agp(fTarget);
+            fTarget->drawSimpleRect(bounds, NULL, stages);
         } else {
             if (passCount > 1) {
-                target->enableState(GrDrawTarget::kNoColorWrites_StateBit);
+                fTarget->enableState(GrDrawTarget::kNoColorWrites_StateBit);
             }
             int baseVertex = 0;
-            for (int sp = 0; sp < subpathCnt; ++sp) {
-                target->drawNonIndexed(type,
-                                      baseVertex,
-                                      subpathVertCount[sp]);
-                baseVertex += subpathVertCount[sp];
+            for (int sp = 0; sp < fSubpathCount; ++sp) {
+                fTarget->drawNonIndexed(type, baseVertex, 
+                                        fSubpathVertCount[sp]);
+                baseVertex += fSubpathVertCount[sp];
             }
         }
     }
     }
 }
 
-void GrDefaultPathRenderer::drawPath(GrDrawTarget* target,
-                                     GrDrawTarget::StageBitfield stages,
-                                     const GrPath& path,
-                                     GrPathFill fill,
-                                     const GrPoint* translate) {
-    this->onDrawPath(target, stages, path, fill, translate, false);
+void GrDefaultPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
+    this->onDrawPath(stages, false);
 }
 
-void GrDefaultPathRenderer::drawPathToStencil(GrDrawTarget* target,
-                                              const GrPath& path,
-                                              GrPathFill fill,
-                                              const GrPoint* translate) {
-    GrAssert(kInverseEvenOdd_PathFill != fill);
-    GrAssert(kInverseWinding_PathFill != fill);
-    this->onDrawPath(target, 0, path, fill, translate, true);
+void GrDefaultPathRenderer::drawPathToStencil() {
+    GrAssert(kInverseEvenOdd_PathFill != fFill);
+    GrAssert(kInverseWinding_PathFill != fFill);
+    this->onDrawPath(0, true);
 }
diff --git a/gpu/src/GrTesselatedPathRenderer.cpp b/gpu/src/GrTesselatedPathRenderer.cpp
index 5ddba99..c85040a 100644
--- a/gpu/src/GrTesselatedPathRenderer.cpp
+++ b/gpu/src/GrTesselatedPathRenderer.cpp
@@ -33,7 +33,7 @@
 // limit the allowable vertex range to approximately half of the representable
 // IEEE exponent in order to avoid overflow when doing multiplies between
 // vertex components,
-const float kMaxVertexValue = 1e18;
+const float kMaxVertexValue = 1e18f;
 
 static inline GrDrawTarget::Edge computeEdge(const GrPoint& p,
                                              const GrPoint& q,
@@ -352,16 +352,12 @@
     return edges->count();
 }
 
-void GrTesselatedPathRenderer::drawPath(GrDrawTarget* target,
-                                        GrDrawTarget::StageBitfield stages,
-                                        const GrPath& path,
-                                        GrPathFill fill,
-                                        const GrPoint* translate) {
-    GrDrawTarget::AutoStateRestore asr(target);
+void GrTesselatedPathRenderer::drawPath(GrDrawTarget::StageBitfield stages) {
+    GrDrawTarget::AutoStateRestore asr(fTarget);
     // face culling doesn't make sense here
-    GrAssert(GrDrawTarget::kBoth_DrawFace == target->getDrawFace());
+    GrAssert(GrDrawTarget::kBoth_DrawFace == fTarget->getDrawFace());
 
-    GrMatrix viewM = target->getViewMatrix();
+    GrMatrix viewM = fTarget->getViewMatrix();
     // In order to tesselate the path we get a bound on how much the matrix can
     // stretch when mapping to screen coordinates.
     GrScalar stretch = viewM.getMaxStretch();
@@ -377,7 +373,7 @@
     GrScalar tolSqd = GrMul(tol, tol);
 
     int subpathCnt;
-    int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
+    int maxPts = GrPathUtils::worstCasePointCount(*fPath, &subpathCnt, tol);
 
     GrVertexLayout layout = 0;
     for (int s = 0; s < GrDrawTarget::kNumStages; ++s) {
@@ -386,7 +382,7 @@
         }
     }
 
-    bool inverted = IsFillInverted(fill);
+    bool inverted = IsFillInverted(fFill);
     if (inverted) {
         maxPts += 4;
         subpathCnt++;
@@ -402,7 +398,7 @@
     SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
 
     GrPoint pts[4];
-    SkPath::Iter iter(path, false);
+    SkPath::Iter iter(*fPath, false);
 
     bool first = true;
     int subpath = 0;
@@ -444,20 +440,20 @@
         first = false;
     }
 FINISHED:
-    if (translate) {
+    if (0 != fTranslate.fX || 0 != fTranslate.fY) {
         for (int i = 0; i < vert - base; i++) {
-            base[i].offset(translate->fX, translate->fY);
+            base[i].offset(fTranslate.fX, fTranslate.fY);
         }
     }
 
     if (inverted) {
         GrRect bounds;
-        GrAssert(NULL != target->getRenderTarget());
+        GrAssert(NULL != fTarget->getRenderTarget());
         bounds.setLTRB(0, 0,
-                       GrIntToScalar(target->getRenderTarget()->width()),
-                       GrIntToScalar(target->getRenderTarget()->height()));
+                       GrIntToScalar(fTarget->getRenderTarget()->width()),
+                       GrIntToScalar(fTarget->getRenderTarget()->height()));
         GrMatrix vmi;
-        if (target->getViewInverse(&vmi)) {
+        if (fTarget->getViewInverse(&vmi)) {
             vmi.mapRect(&bounds);
         }
         *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
@@ -476,22 +472,22 @@
         return;
     }
 
-    if (subpathCnt == 1 && !inverted && path.isConvex()) {
-        if (target->isAntialiasState()) {
+    if (subpathCnt == 1 && !inverted && fPath->isConvex()) {
+        if (fTarget->isAntialiasState()) {
             GrEdgeArray edges;
-            GrMatrix inverse, matrix = target->getViewMatrix();
-            target->getViewInverse(&inverse);
+            GrMatrix inverse, matrix = fTarget->getViewMatrix();
+            fTarget->getViewInverse(&inverse);
 
             count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
-            size_t maxEdges = target->getMaxEdges();
+            size_t maxEdges = fTarget->getMaxEdges();
             if (count == 0) {
                 return;
             }
             if (count <= maxEdges) {
                 // All edges fit; upload all edges and draw all verts as a fan
-                target->setVertexSourceToArray(layout, base, count);
-                target->setEdgeAAData(&edges[0], count);
-                target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+                fTarget->setVertexSourceToArray(layout, base, count);
+                fTarget->setEdgeAAData(&edges[0], count);
+                fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
             } else {
                 // Upload "maxEdges" edges and verts at a time, and draw as
                 // separate fans
@@ -499,26 +495,26 @@
                     edges[i] = edges[0];
                     base[i] = base[0];
                     int size = GR_CT_MIN(count - i, maxEdges);
-                    target->setVertexSourceToArray(layout, &base[i], size);
-                    target->setEdgeAAData(&edges[i], size);
-                    target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
+                    fTarget->setVertexSourceToArray(layout, &base[i], size);
+                    fTarget->setEdgeAAData(&edges[i], size);
+                    fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
                 }
             }
-            target->setEdgeAAData(NULL, 0);
+            fTarget->setEdgeAAData(NULL, 0);
         } else {
-            target->setVertexSourceToArray(layout, base, count);
-            target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
+            fTarget->setVertexSourceToArray(layout, base, count);
+            fTarget->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
         }
         return;
     }
 
-    if (target->isAntialiasState()) {
+    if (fTarget->isAntialiasState()) {
         // Run the tesselator once to get the boundaries.
-        GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
+        GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fFill));
         btess.addVertices(base, subpathVertCount, subpathCnt);
 
-        GrMatrix inverse, matrix = target->getViewMatrix();
-        if (!target->getViewInverse(&inverse)) {
+        GrMatrix inverse, matrix = fTarget->getViewMatrix();
+        if (!fTarget->getViewInverse(&inverse)) {
             return;
         }
 
@@ -553,7 +549,7 @@
         }
 
         // Draw the resulting polys and upload their edge data.
-        target->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
+        fTarget->enableState(GrDrawTarget::kEdgeAAConcave_StateBit);
         const GrPointArray& vertices = ptess.vertices();
         const GrIndexArray& indices = ptess.indices();
         const GrDrawTarget::Edge* edges = ptess.edges();
@@ -586,23 +582,23 @@
                 tri_edges[t++] = edge4;
                 tri_edges[t++] = edge5;
             }
-            target->setEdgeAAData(&tri_edges[0], t);
-            target->setVertexSourceToArray(layout, &tri_verts[0], 3);
-            target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
+            fTarget->setEdgeAAData(&tri_edges[0], t);
+            fTarget->setVertexSourceToArray(layout, &tri_verts[0], 3);
+            fTarget->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
         }
-        target->setEdgeAAData(NULL, 0);
-        target->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
+        fTarget->setEdgeAAData(NULL, 0);
+        fTarget->disableState(GrDrawTarget::kEdgeAAConcave_StateBit);
         return;
     }
 
-    GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
+    GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fFill));
     ptess.addVertices(base, subpathVertCount, subpathCnt);
     const GrPointArray& vertices = ptess.vertices();
     const GrIndexArray& indices = ptess.indices();
     if (indices.count() > 0) {
-        target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
-        target->setIndexSourceToArray(indices.begin(), indices.count());
-        target->drawIndexed(kTriangles_PrimitiveType,
+        fTarget->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
+        fTarget->setIndexSourceToArray(indices.begin(), indices.count());
+        fTarget->drawIndexed(kTriangles_PrimitiveType,
                             0,
                             0,
                             vertices.count(),
@@ -610,16 +606,12 @@
     }
 }
 
-bool GrTesselatedPathRenderer::canDrawPath(const GrDrawTarget* target,
-                                           const SkPath& path,
+bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
                                            GrPathFill fill) const {
     return kHairLine_PathFill != fill;
 }
 
-void GrTesselatedPathRenderer::drawPathToStencil(GrDrawTarget* target,
-                                                 const SkPath& path,
-                                                 GrPathFill fill,
-                                                 const GrPoint* translate) {
+void GrTesselatedPathRenderer::drawPathToStencil() {
     GrAlwaysAssert(!"multipass stencil should not be needed");
 }