First pass at accelerating gpu-based AA clipping

http://codereview.appspot.com/6195051/



git-svn-id: http://skia.googlecode.com/svn/trunk@3853 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/bench/AAClipBench.cpp b/bench/AAClipBench.cpp
index 39088c1..ea4f6da 100644
--- a/bench/AAClipBench.cpp
+++ b/bench/AAClipBench.cpp
@@ -10,7 +10,84 @@
 #include "SkPath.h"
 #include "SkRegion.h"
 #include "SkString.h"
+#include "SkCanvas.h"
 
+
+////////////////////////////////////////////////////////////////////////////////
+// This bench tests out AA/BW clipping via canvas' clipPath and clipRect calls
+class AAClipBench : public SkBenchmark {
+    SkString fName;
+    SkPath   fClipPath;
+    SkRect   fClipRect;
+    SkRect   fDrawRect;
+    bool     fDoPath;
+    bool     fDoAA;
+
+    enum {
+        N = SkBENCHLOOP(200),
+    };
+
+public:
+    AAClipBench(void* param, bool doPath, bool doAA) 
+        : INHERITED(param) {
+        fDoPath = doPath;
+        fDoAA = doAA;
+
+        fName.printf("aaclip_%s_%s",
+                     doPath ? "path" : "rect",
+                     doAA ? "AA" : "BW");
+
+        fClipRect.set(SkFloatToScalar(10.5), SkFloatToScalar(10.5), 
+                      SkFloatToScalar(50.5), SkFloatToScalar(50.5));
+        fClipPath.addRoundRect(fClipRect, SkIntToScalar(10), SkIntToScalar(10));
+        fDrawRect.set(SkIntToScalar(0), SkIntToScalar(0),
+                      SkIntToScalar(100), SkIntToScalar(100));
+
+        SkASSERT(fClipPath.isConvex());
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        for (int i = 0; i < N; ++i) {
+            // jostle the clip regions each time to prevent caching
+            fClipRect.offset((i % 2) == 0 ? SkIntToScalar(10) : SkIntToScalar(-10), 0);
+            fClipPath.reset();
+            fClipPath.addRoundRect(fClipRect, 
+                                   SkIntToScalar(5), SkIntToScalar(5));
+            SkASSERT(fClipPath.isConvex());
+
+            canvas->save();
+#if 1
+            if (fDoPath) {
+                canvas->clipPath(fClipPath, SkRegion::kReplace_Op, fDoAA);
+            } else {
+                canvas->clipRect(fClipRect, SkRegion::kReplace_Op, fDoAA);
+            }
+
+            canvas->drawRect(fDrawRect, paint);
+#else
+            // this path tests out directly draw the clip primitive
+            // use it to comparing just drawing the clip vs. drawing using
+            // the clip
+            if (fDoPath) {
+                canvas->drawPath(fClipPath, paint);
+            } else {
+                canvas->drawRect(fClipRect, paint);
+            }
+#endif
+            canvas->restore();
+        }
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
 class AAClipBuilderBench : public SkBenchmark {
     SkString fName;
     SkPath   fPath;
@@ -56,6 +133,7 @@
     typedef SkBenchmark INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
 class AAClipRegionBench : public SkBenchmark {
 public:
     AAClipRegionBench(void* param) : INHERITED(param) {
@@ -88,7 +166,7 @@
     typedef SkBenchmark INHERITED;
 };
 
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
 static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, false)); }
 static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, true)); }
@@ -102,3 +180,14 @@
 
 static SkBenchmark* Fact01(void* p) { return SkNEW_ARGS(AAClipRegionBench, (p)); }
 static BenchRegistry gReg01(Fact01);
+
+static SkBenchmark* Fact000(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, false)); }
+static SkBenchmark* Fact001(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, true)); }
+static SkBenchmark* Fact002(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, false)); }
+static SkBenchmark* Fact003(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, true)); }
+
+static BenchRegistry gReg000(Fact000);
+static BenchRegistry gReg001(Fact001);
+static BenchRegistry gReg002(Fact002);
+static BenchRegistry gReg003(Fact003);
+
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
index f4b0721..1a892d8 100644
--- a/src/gpu/GrClipMaskManager.cpp
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -340,7 +340,6 @@
 
 void clear(GrGpu* gpu,
            GrTexture* target,
-           const GrRect& bounds,
            GrColor color) {
     GrDrawState* drawState = gpu->drawState();
     GrAssert(NULL != drawState);
@@ -350,6 +349,36 @@
     gpu->clear(NULL, color);
 }
 
+// get a texture to act as a temporary buffer for AA clip boolean operations
+// TODO: given the expense of createTexture we may want to just cache this too
+void needTemp(GrGpu *gpu, const GrTextureDesc& desc, GrTexture** temp) {
+    if (NULL != *temp) {
+        // we've already allocated the temp texture
+        return;
+    }
+
+     *temp = gpu->createTexture(desc, NULL, 0);
+}
+
+}
+
+void GrClipMaskManager::getAccum(GrGpu* gpu,
+                                 const GrTextureDesc& desc,
+                                 GrTexture** accum) {
+    GrAssert(NULL == *accum);
+
+    // since we are getting an accumulator we know our cache is shot. See
+    // if we can reuse the texture stored in the cache
+    if (fAACache.getLastMaskWidth() >= desc.fWidth &&
+        fAACache.getLastMaskHeight() >= desc.fHeight) {
+        // we can just reuse the existing texture
+        *accum = fAACache.detachLastMask();
+        fAACache.reset();
+    } else {
+        *accum = gpu->createTexture(desc, NULL, 0);
+    }
+
+    GrAssert(1 == (*accum)->getRefCnt());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -403,7 +432,7 @@
     GrAssert(SkScalarIsInt(bounds.width()));
     GrAssert(SkScalarIsInt(bounds.height()));
 
-    GrTextureDesc desc = {
+    const GrTextureDesc desc = {
         kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit,
         SkScalarCeilToInt(bounds.width()),
         SkScalarCeilToInt(bounds.height()),
@@ -414,12 +443,12 @@
     GrRect newRTBounds;
     newRTBounds.setLTRB(0, 0, bounds.width(), bounds.height());
 
-    GrTexture* accum = gpu->createTexture(desc, NULL, 0);
-    GrTexture* temp = gpu->createTexture(desc, NULL, 0);
-    if (NULL == accum || NULL == temp) {
+    GrTexture* accum = NULL, *temp = NULL;
+    
+    getAccum(gpu, desc, &accum);
+    if (NULL == accum) {
         fClipMaskInAlpha = false;
         SkSafeUnref(accum);
-        SkSafeUnref(temp);
         return false;
     }
 
@@ -437,7 +466,7 @@
 
         m.setTranslate(-bounds.fLeft, -bounds.fTop);
 
-        drawState->preConcatViewMatrix(m);
+        drawState->setViewMatrix(m);
     }
 
     bool clearToInside;
@@ -447,7 +476,7 @@
                                               &clearToInside,
                                               &startOp);
 
-    clear(gpu, accum, newRTBounds, clearToInside ? 0xffffffff : 0x00000000);
+    clear(gpu, accum, clearToInside ? 0xffffffff : 0x00000000);
 
     // walk through each clip element and perform its set op
     for (int c = start; c < count; ++c) {
@@ -460,7 +489,7 @@
             // replace ops and alter GrClip to allow them through
 
             // clear the accumulator and draw the new object directly into it
-            clear(gpu, accum, newRTBounds, 0x00000000);
+            clear(gpu, accum, 0x00000000);
 
             setUpBooleanBlendCoeffs(drawState, op);
             this->drawClipShape(gpu, accum, clipIn, c);
@@ -474,8 +503,15 @@
                 continue;
             }
 
+            needTemp(gpu, desc, &temp);
+            if (NULL == temp) {
+                fClipMaskInAlpha = false;
+                SkSafeUnref(accum);
+                return false;
+            }
+
             // clear the temp target & draw into it
-            clear(gpu, temp, newRTBounds, 0x00000000);
+            clear(gpu, temp, 0x00000000);
 
             setUpBooleanBlendCoeffs(drawState, SkRegion::kReplace_Op);
             this->drawClipShape(gpu, temp, clipIn, c);
@@ -517,6 +553,7 @@
     *resultBounds = bounds;
     SkSafeUnref(accum);     // fAACache still has a ref to accum
     SkSafeUnref(temp);
+
     return true;
 }
 
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
index 516ec95..f827948 100644
--- a/src/gpu/GrClipMaskManager.h
+++ b/src/gpu/GrClipMaskManager.h
@@ -14,6 +14,7 @@
 #include "GrNoncopyable.h"
 #include "GrClip.h"
 #include "SkRefCnt.h"
+#include "GrTexture.h"
 
 class GrGpu;
 class GrPathRenderer;
@@ -41,9 +42,15 @@
  */
 class GrClipMaskCache : public GrNoncopyable {
 public:
-    GrClipMaskCache() 
-    : fLastWidth(-1)
-    , fLastHeight(-1) {
+    GrClipMaskCache() {
+        reset();
+    }
+
+    void reset () {
+        fLastWidth = -1;
+        fLastHeight = -1;
+        fLastClip.setEmpty();
+        fLastMask.reset(NULL);
         fLastBound.MakeEmpty();
     }
 
@@ -68,10 +75,42 @@
         fLastBound = bound;
     }
 
-    GrTexture*  getLastMask() {
+    int getLastWidth() const {
+        return fLastWidth;
+    }
+
+    int getLastHeight() const {
+        return fLastHeight;
+    }
+
+    const GrClip& getLastClip() const {
+        return fLastClip;
+    }
+
+    GrTexture* getLastMask() {
         return fLastMask.get();
     }
 
+    GrTexture* detachLastMask() {
+        return fLastMask.detach();
+    }
+
+    int getLastMaskWidth() const {
+        if (NULL == fLastMask.get()) {
+            return -1;
+        }
+
+        return fLastMask.get()->width();
+    }
+
+    int getLastMaskHeight() const {
+        if (NULL == fLastMask.get()) {
+            return -1;
+        }
+
+        return fLastMask.get()->height();
+    }
+
     const GrRect& getLastBound() const {
         return fLastBound;
     }
@@ -150,6 +189,10 @@
                      const GrRect& rect,
                      GrTexture* texture);
 
+    void getAccum(GrGpu* gpu,
+                  const GrTextureDesc& desc,
+                  GrTexture** accum);
+
     // determines the path renderer used to draw a clip path element.
     GrPathRenderer* getClipPathRenderer(GrGpu* gpu,
                                         const SkPath& path,