Per-draw super sampling. Disabled, path only, 2x2 only

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



git-svn-id: http://skia.googlecode.com/svn/trunk@1186 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index cbe9e12..495eff4 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -605,6 +605,26 @@
                                     GrPathIter* path,
                                     GrPathFill fill);
 
+    struct OffscreenRecord;
+    // we currently only expose stage 0 through the paint so use stage 1. We
+    // use stage 1 for the offscreen.
+    enum {
+        kOffscreenStage = 1,
+    };
+
+    // sets up target to draw coverage to the supersampled render target
+    bool setupOffscreenAAPass1(GrDrawTarget* target,
+                               bool requireStencil,
+                               OffscreenRecord* record);
+
+    // sets up target to sample coverage of supersampled render target back
+    // to the main render target using stage kOffscreenStage.
+    void setupOffscreenAAPass2(GrDrawTarget* target,
+                               const GrPaint& paint,
+                               OffscreenRecord* record);
+    
+    // cleans up from supersample aa drawing
+    void endOffscreenAA(GrDrawTarget* target, OffscreenRecord* record);
 };
 
 /**
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index 1be2601..e021a93 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -276,6 +276,18 @@
     void preConcatViewMatrix(const GrMatrix& m);
 
     /**
+     *  Multiplies the current view matrix by a matrix
+     *
+     *  After this call V' = m*V where V is the old view matrix,
+     *  m is the parameter to this function, and V' is the new view matrix.
+     *  (We consider positions to be column vectors so position vector p is
+     *  transformed by matrix X as p' = X*p.)
+     *
+     *  @param m the matrix used to modify the view matrix.
+     */
+    void postConcatViewMatrix(const GrMatrix& m);
+
+    /**
      * Retrieves the current view matrix
      * @return the current view matrix.
      */
@@ -728,9 +740,17 @@
 
     class AutoStateRestore : ::GrNoncopyable {
     public:
+        AutoStateRestore();
         AutoStateRestore(GrDrawTarget* target);
         ~AutoStateRestore();
 
+        /**
+         * if this object is already saving state for param target then
+         * this does nothing. Otherise, it restores previously saved state on
+         * previous target (if any) and saves current state on param target.
+         */
+        void set(GrDrawTarget* target);
+
     private:
         GrDrawTarget*       fDrawTarget;
         SavedDrawState      fDrawState;
diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h
index 91d9086..03092b6 100644
--- a/gpu/include/GrPathRenderer.h
+++ b/gpu/include/GrPathRenderer.h
@@ -109,6 +109,12 @@
     }
     
     /**
+     * @return true if the path renderer can perform anti-aliasing (aside from
+     * having FSAA enabled for a render target)
+     */
+    virtual bool supportsAA() { return false; }
+
+    /**
      * This is called to install a custom path renderer in every GrContext at
      * create time. The default implementation in GrCreatePathRenderer_none.cpp
      * returns NULL. Link against another implementation to install your own.
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index 52db9c3..8acb3dc 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -26,6 +26,8 @@
 #include "GrBufferAllocPool.h"
 #include "GrPathRenderer.h"
 
+#define ENABLE_SSAA 0
+
 #define DEFER_TEXT_RENDERING 1
 
 #define BATCH_RECT_TO_RECT (1 && !GR_STATIC_RECT_VB)
@@ -105,7 +107,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
 enum {
     kNPOTBit    = 0x1,
     kFilterBit  = 0x2,
@@ -415,6 +416,97 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+struct GrContext::OffscreenRecord {
+    OffscreenRecord() { fEntry = NULL; }
+    ~OffscreenRecord() { GrAssert(NULL == fEntry); }
+
+    GrTextureEntry*                fEntry;
+    GrDrawTarget::SavedDrawState   fSavedState;
+};
+
+bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
+                                      bool requireStencil,
+                                      OffscreenRecord* record) {
+#if !ENABLE_SSAA
+    return false;
+#endif
+
+    GrAssert(NULL == record->fEntry);
+
+    int width  = this->getRenderTarget()->width();
+    int height = this->getRenderTarget()->height();
+
+    GrTextureDesc desc;
+    desc.fAALevel = kNone_GrAALevel;
+    if (requireStencil) {
+        desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    } else {
+        desc.fFlags = kRenderTarget_GrTextureFlagBit | 
+                      kNoStencil_GrTextureFlagBit;
+    }
+
+    desc.fWidth = 2 * width;
+    desc.fHeight = 2 * height;
+    desc.fFormat = kRGBA_8888_GrPixelConfig;
+
+    record->fEntry = this->lockKeylessTexture(desc);
+    if (NULL == record->fEntry) {
+        return false;
+    }
+    GrRenderTarget* offscreen = record->fEntry->texture()->asRenderTarget();
+    GrAssert(NULL != offscreen);
+
+    target->saveCurrentDrawState(&record->fSavedState);
+
+    GrPaint tempPaint;
+    tempPaint.reset();
+    SetPaint(tempPaint, target);
+    target->setRenderTarget(offscreen);
+
+    GrMatrix scaleM;
+    scaleM.setScale(2 * GR_Scalar1, 2 * GR_Scalar1);
+    target->postConcatViewMatrix(scaleM);
+
+    // clip gets applied in second pass
+    target->disableState(GrDrawTarget::kClip_StateBit);
+
+    target->clear(0x0);
+    return true;
+}
+
+void GrContext::setupOffscreenAAPass2(GrDrawTarget* target,
+                                      const GrPaint& paint,
+                                      OffscreenRecord* record) {
+
+    GrAssert(NULL != record->fEntry);
+    GrTexture* offscreen = record->fEntry->texture();
+    GrAssert(NULL != offscreen);
+
+    target->restoreDrawState(record->fSavedState);
+
+    target->setViewMatrix(GrMatrix::I());
+    target->setTexture(kOffscreenStage, offscreen);
+    GrMatrix scaleM;
+
+    scaleM.setScale(GR_Scalar1 / target->getRenderTarget()->width(),
+                    GR_Scalar1 / target->getRenderTarget()->height());
+
+    // use bilinear filtering to get downsample
+    GrSamplerState sampler(GrSamplerState::kClamp_WrapMode, 
+                           GrSamplerState::kClamp_WrapMode,
+                           scaleM, true);
+    target->setSamplerState(kOffscreenStage, sampler);
+}
+
+void GrContext::endOffscreenAA(GrDrawTarget* target, OffscreenRecord* record) {
+    this->unlockTexture(record->fEntry);
+    record->fEntry = NULL;
+
+    target->restoreDrawState(record->fSavedState);    
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 /*  create a triangle strip that strokes the specified triangle. There are 8
  unique vertices, but we repreat the last 2 to close up. Alternatively we
  could use an indices array, and then only send 8 verts, but not sure that
@@ -899,24 +991,45 @@
 }
 
 
-////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
 void GrContext::drawPath(const GrPaint& paint,
                          GrPathIter* path,
                          GrPathFill fill,
                          const GrPoint* translate) {
 
-
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
 
+    if (paint.fAntiAlias &&
+        !this->getRenderTarget()->isMultisampled() &&
+        !pr->supportsAA()) {
+
+         OffscreenRecord record;
+         bool needsStencil = pr->requiresStencilPass(target, path, fill);
+         if (this->setupOffscreenAAPass1(target, needsStencil, &record)) {
+             pr->drawPath(target, 0, path, fill, translate);
+             
+             this->setupOffscreenAAPass2(target, paint, &record);
+             
+             int stages = (NULL != paint.getTexture()) ? 0x1 : 0x0;
+             stages |= (1 << kOffscreenStage);
+             GrRect dstRect(0, 0, 
+                            target->getRenderTarget()->width(),
+                            target->getRenderTarget()->height());
+                            target->drawSimpleRect(dstRect, NULL, stages);
+
+             this->endOffscreenAA(target, &record);
+             return;
+         }
+    } 
     GrDrawTarget::StageBitfield enabledStages = 0;
     if (NULL != paint.getTexture()) {
         enabledStages |= 1;
     }
-    GrPathRenderer* pr = getPathRenderer(target, path, fill);
+
     pr->drawPath(target, enabledStages, path, fill, translate);
 }
-
 void GrContext::drawPath(const GrPaint& paint,
                          const GrPath& path,
                          GrPathFill fill,
@@ -1205,3 +1318,4 @@
         return &fDefaultPathRenderer;
     }
 }
+
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index 7c8e1c8..c33f15c 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -335,6 +335,10 @@
     fCurrDrawState.fViewMatrix.preConcat(matrix);
 }
 
+void GrDrawTarget::postConcatViewMatrix(const GrMatrix& matrix) {
+    fCurrDrawState.fViewMatrix.postConcat(matrix);
+}
+
 const GrMatrix& GrDrawTarget::getViewMatrix() const {
     return fCurrDrawState.fViewMatrix;
 }
@@ -581,6 +585,9 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+GrDrawTarget::AutoStateRestore::AutoStateRestore() {
+    fDrawTarget = NULL;
+}
 
 GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target) {
     fDrawTarget = target;
@@ -595,3 +602,14 @@
     }
 }
 
+void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target) {
+    if (target != fDrawTarget) {
+        if (NULL != fDrawTarget) {
+            fDrawTarget->restoreDrawState(fDrawState);
+        }
+        if (NULL != target) {
+            fDrawTarget->saveCurrentDrawState(&fDrawState);
+        }
+        fDrawTarget = target;
+    }
+}