4x4 SSAA with improvements in determination of when to apply. Still disabled at compile time.

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




git-svn-id: http://skia.googlecode.com/svn/trunk@1218 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 214fc05..faf535f 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -614,21 +614,23 @@
         kOffscreenStage = 1,
     };
 
+    bool doOffscreenAA(GrDrawTarget* target, 
+                       const GrPaint& paint,
+                       bool isLines) const;
+
     // sets up target to draw coverage to the supersampled render target
     bool setupOffscreenAAPass1(GrDrawTarget* target,
                                bool requireStencil,
+                               const GrIRect& boundRect,
                                OffscreenRecord* record);
 
     // sets up target to sample coverage of supersampled render target back
     // to the main render target using stage kOffscreenStage.
-    // caller should set view matrix to matrix used for this pass prior to 
-    // calling.
-    void setupOffscreenAAPass2(GrDrawTarget* target,
-                               const GrPaint& paint,
-                               OffscreenRecord* record);
+    void offscreenAAPass2(GrDrawTarget* target,
+                          const GrPaint& paint,
+                          const GrIRect& boundRect,
+                          OffscreenRecord* record);
     
-    // cleans up from supersample aa drawing
-    void endOffscreenAA(GrDrawTarget* target, OffscreenRecord* record);
 };
 
 /**
@@ -655,3 +657,4 @@
 #endif
 
 #include "GrContext_impl.h"
+
diff --git a/gpu/include/GrContext_impl.h b/gpu/include/GrContext_impl.h
index b0faa2c..0fa3b8d 100644
--- a/gpu/include/GrContext_impl.h
+++ b/gpu/include/GrContext_impl.h
@@ -18,10 +18,11 @@
 #define GrContext_impl_DEFINED
 
 struct GrContext::OffscreenRecord {
-    OffscreenRecord() { fEntry = NULL; }
-    ~OffscreenRecord() { GrAssert(NULL == fEntry); }
+    OffscreenRecord() { fEntry0 = NULL; fEntry1 = NULL; }
+    ~OffscreenRecord() { GrAssert(NULL == fEntry0 && NULL == fEntry1); }
 
-    GrTextureEntry*                fEntry;
+    GrTextureEntry*                fEntry0;
+    GrTextureEntry*                fEntry1;
     GrDrawTarget::SavedDrawState   fSavedState;
 };
 
@@ -52,16 +53,6 @@
         layout |= GrDrawTarget::kColor_VertexLayoutBit;
     }
 
-    bool doOffscreenAA = false;
-    OffscreenRecord record;
-    if (paint.fAntiAlias &&
-        !this->getRenderTarget()->isMultisampled() &&
-        !(GrIsPrimTypeLines(primitiveType) && fGpu->supportsAALines()) &&
-        this->setupOffscreenAAPass1(target, false, &record)) {
-        doOffscreenAA = true;
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(kOffscreenStage);
-    }
-
     int vertexCount = posSrc.count();
     int indexCount = (NULL != idxSrc) ? idxSrc->count() : 0;
 
@@ -94,51 +85,30 @@
         idxSrc->writeValue(i, indices + i);
     }
 
+    bool doAA = false;
+    OffscreenRecord record;
+    GrIRect bounds;
+
+    if (-1 == texOffsets[0] && -1 == colorOffset && 
+        this->doOffscreenAA(target, paint, GrIsPrimTypeLines(primitiveType))) {
+        GrRect b;
+        b.setBounds(geo.positions(), vertexCount);
+        target->getViewMatrix().mapRect(&b);
+        b.roundOut(&bounds);
+        if (this->setupOffscreenAAPass1(target, false, bounds, &record)) {
+            doAA = true;
+        }
+    }
+
     if (NULL == idxSrc) {
         target->drawNonIndexed(primitiveType, 0, vertexCount);
     } else {
         target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
     }
 
-    if (doOffscreenAA) {
-        // draw to the offscreen
-        if (NULL != indices) {
-            target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
-        } else {
-            target->drawNonIndexed(primitiveType, 0, vertexCount);
-        }
-        // When there are custom texture coordinates we can't just draw
-        // a quad to sample the offscreen. Instead we redraw the geometry to
-        // specify the texture coords. This isn't quite right either, primitives
-        // will only be eroded at the edges, not expanded into partial pixels.
-        bool useRect = 0 == (layout & GrDrawTarget::StageTexCoordVertexLayoutBit(0,0));
-        if (useRect) {
-            target->setViewMatrix(GrMatrix::I());
-        }
-        this->setupOffscreenAAPass2(target, paint, &record);
-        if (useRect) {
-            geo.set(NULL, 0, 0, 0);
-            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);
-            target->drawSimpleRect(dstRect, NULL, stages);
-        } else {
-            if (NULL != indices) {
-                target->drawIndexed (primitiveType, 0, 0, vertexCount, indexCount);
-            } else {
-                target->drawNonIndexed(primitiveType, 0, vertexCount);
-            }
-        }
-        this->endOffscreenAA(target, &record);
-    } else {
-        if (NULL != indices) {
-            target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
-        } else {
-            target->drawNonIndexed(primitiveType, 0, vertexCount);
-        }
+    if (doAA) {
+        geo.set(NULL, 0, 0, 0); // have to release geom before can draw again
+        this->offscreenAAPass2(target, paint, bounds, &record);
     }
 }
 
diff --git a/gpu/include/GrDrawTarget.h b/gpu/include/GrDrawTarget.h
index c971e71..6c92071 100644
--- a/gpu/include/GrDrawTarget.h
+++ b/gpu/include/GrDrawTarget.h
@@ -551,7 +551,7 @@
      *          of vertices to be filled by caller. The next draw will read
      *          these vertices.
      *
-     *          if indecCount is nonzero, *indices will be the array of indices
+     *          if indexCount is nonzero, *indices will be the array of indices
      *          to be filled by caller. The next indexed draw will read from
      *          these indices.
      *
diff --git a/gpu/include/GrGpu.h b/gpu/include/GrGpu.h
index 590712b..16bb6b6 100644
--- a/gpu/include/GrGpu.h
+++ b/gpu/include/GrGpu.h
@@ -229,6 +229,11 @@
      * value.
      */
     int minRenderTargetHeight() const  { return fMinRenderTargetHeight; }
+    
+    /**
+     * Reports whether full scene anti-aliasing is supported.
+     */
+    bool supportsFullsceneAA() const { return fFSAASupport; }
 
     /**
      * Returns true if NPOT textures can be created
@@ -391,6 +396,7 @@
     bool fTwoSidedStencilSupport;
     bool fStencilWrapOpsSupport;
     bool fAALineSupport;
+    bool fFSAASupport;
 
     // set by subclass to true if index and vertex buffers can be locked, false
     // otherwise.
diff --git a/gpu/include/GrPathRenderer.h b/gpu/include/GrPathRenderer.h
index 03092b6..21cab6b 100644
--- a/gpu/include/GrPathRenderer.h
+++ b/gpu/include/GrPathRenderer.h
@@ -112,7 +112,9 @@
      * @return true if the path renderer can perform anti-aliasing (aside from
      * having FSAA enabled for a render target)
      */
-    virtual bool supportsAA() { return false; }
+    virtual bool supportsAA(GrDrawTarget* target,
+                            GrPathIter* path,
+                            GrPathFill fill) { return false; }
 
     /**
      * This is called to install a custom path renderer in every GrContext at
diff --git a/gpu/include/GrRect.h b/gpu/include/GrRect.h
index 8a63cdc..7d03396 100644
--- a/gpu/include/GrRect.h
+++ b/gpu/include/GrRect.h
@@ -63,6 +63,11 @@
         fRight = fBottom = GR_Int32Max;
     }
 
+    void setLargestInverted() {
+        fLeft = fTop = GR_Int32Max;
+        fRight = fBottom = GR_Int32Min;
+    }
+
     bool quickReject(int l, int t, int r, int b) const {
         return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
     }
@@ -95,6 +100,35 @@
         }
     }
 
+    bool intersectWith(int left, int top,
+                       int right, int bottom) {
+        if (fRight < left ||
+            fLeft > right ||
+            fBottom < top ||
+            fTop > bottom) {
+            this->setEmpty();
+            return false;
+        } else {
+            fLeft = GrMax(fLeft, left);
+            fRight = GrMin(fRight, right);
+            fTop = GrMax(fTop, top);
+            fBottom = GrMin(fBottom, bottom);
+            return true;
+        }
+    }
+
+    /**
+     * Enlarge the rectangle to include rect.
+     */
+    void growToInclude(const GrIRect& rect) {
+        GrAssert(!rect.isEmpty());
+        fLeft  = GrMin(rect.fLeft, fLeft);
+        fRight = GrMax(rect.fRight, fRight);
+
+        fTop    = GrMin(rect.fTop, fTop);
+        fBottom = GrMax(rect.fBottom, fBottom);
+    }
+
     friend bool operator==(const GrIRect& a, const GrIRect& b) {
         return 0 == memcmp(&a, &b, sizeof(a));
     }
@@ -118,6 +152,11 @@
                fTop    <= r.fTop &&
                fBottom >= r.fBottom;
     }
+
+    static const GrIRect& EmptyIRect() {
+        static const GrIRect gEmpty(0,0,0,0);
+        return gEmpty;
+    }
 };
 
 struct GrIRect16 {
@@ -367,17 +406,36 @@
      * Sets this rect to the intersection with a clip rect. If there is no
      * intersection then this rect will be made empty.
      */
-    void intersectWith(const GrRect& clipRect) {
+    bool intersectWith(const GrRect& clipRect) {
         if (fRight < clipRect.fLeft ||
             fLeft > clipRect.fRight ||
             fBottom < clipRect.fTop ||
             fTop > clipRect.fBottom) {
             this->setEmpty();
+            return false;
         } else {
             fLeft = GrMax(fLeft, clipRect.fLeft);
             fRight = GrMin(fRight, clipRect.fRight);
             fTop = GrMax(fTop, clipRect.fTop);
             fBottom = GrMin(fBottom, clipRect.fBottom);
+            return true;
+        }
+    }
+
+    bool intersectWith(GrScalar left, GrScalar top,
+                       GrScalar right, GrScalar bottom) {
+        if (fRight < left ||
+            fLeft > right ||
+            fBottom < top ||
+            fTop > bottom) {
+            this->setEmpty();
+            return false;
+        } else {
+            fLeft = GrMax(fLeft, left);
+            fRight = GrMin(fRight, right);
+            fTop = GrMax(fTop, top);
+            fBottom = GrMin(fBottom, bottom);
+            return true;
         }
     }
 
diff --git a/gpu/include/GrTexture.h b/gpu/include/GrTexture.h
index 6d4f4d7..0e2f369 100644
--- a/gpu/include/GrTexture.h
+++ b/gpu/include/GrTexture.h
@@ -65,21 +65,31 @@
      * when the render target has been modified outside of Gr. Only meaningful
      * for Gr-created RT/Textures and Platform RT/Textures created with the
      * kGrCanResolve flag.
+     * @param rect  a rect bounding the area needing resolve. NULL indicates
+     *              the whole RT needs resolving.
      */
-    void flagAsNeedingResolve() {
-        fNeedsResolve = kCanResolve_ResolveType == getResolveType();
-    }
+    void flagAsNeedingResolve(const GrIRect* rect = NULL);
+
+    /**
+     * Call to override the region that needs to be resolved.
+     */
+    void overrideResolveRect(const GrIRect rect);
 
     /**
      * Call to indicate that GrRenderTarget was externally resolved. This may
      * allow Gr to skip a redundant resolve step.
      */
-    void flagAsResolved() { fNeedsResolve = false; }
+    void flagAsResolved() { fResolveRect.setLargestInverted(); }
 
     /**
      * @return true if the GrRenderTarget requires MSAA resolving
      */
-    bool needsResolve() { return fNeedsResolve; }
+    bool needsResolve() const { return !fResolveRect.isEmpty(); }
+
+    /**
+     * Returns a rect bounding the region needing resolving.
+     */
+    const GrIRect& getResolveRect() const { return fResolveRect; }
 
     /**
      * Reads a rectangle of pixels from the render target.
@@ -119,8 +129,9 @@
         , fHeight(height)
         , fStencilBits(stencilBits)
         , fIsMultisampled(isMultisampled)
-        , fNeedsResolve(false)
-    {}
+    {
+        fResolveRect.setLargestInverted();
+    }
 
     friend class GrTexture;
     // When a texture unrefs an owned rendertarget this func
@@ -133,14 +144,14 @@
         fTexture = NULL;
     }
 
+private:
     GrTexture* fTexture; // not ref'ed
     int        fWidth;
     int        fHeight;
     int        fStencilBits;
     bool       fIsMultisampled;
-    bool       fNeedsResolve;
+    GrIRect    fResolveRect;
 
-private:
     // GrGpu keeps a cached clip in the render target to avoid redundantly
     // rendering the clip into the same stencil buffer.
     friend class GrGpu;
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index d3904e0..2263172 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -26,7 +26,7 @@
 #include "GrBufferAllocPool.h"
 #include "GrPathRenderer.h"
 
-#define ENABLE_SSAA 0
+#define ENABLE_OFFSCREEN_AA 0
 
 #define DEFER_TEXT_RENDERING 1
 
@@ -414,20 +414,49 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+bool GrContext::doOffscreenAA(GrDrawTarget* target, 
+                              const GrPaint& paint,
+                              bool isLines) const {
+#if !ENABLE_OFFSCREEN_AA
+    return false;
+#else
+    if (!paint.fAntiAlias) {
+        return false;
+    }
+    if (isLines && fGpu->supportsAALines()) {
+        return false;
+    }
+    if (target->getRenderTarget()->isMultisampled()) {
+        return false;
+    }
+    // we have to be sure that the blend equation is expressible
+    // as simple src / dst coeffecients when the source 
+    // is already modulated by the coverage fraction.
+    // We could use dual-source blending to get the correct per-pixel
+    // dst coeffecient for the remaining cases.
+    if (kISC_BlendCoeff != paint.fDstBlendCoeff &&
+        kOne_BlendCoeff != paint.fDstBlendCoeff &&
+        kISA_BlendCoeff != paint.fDstBlendCoeff) {
+        return false;
+    }
+    return true;
+#endif
+}
+
 bool GrContext::setupOffscreenAAPass1(GrDrawTarget* target,
                                       bool requireStencil,
+                                      const GrIRect& boundRect,
                                       OffscreenRecord* record) {
-#if !ENABLE_SSAA
-    return false;
-#endif
+    GrAssert(ENABLE_OFFSCREEN_AA);
 
-    GrAssert(NULL == record->fEntry);
+    GrAssert(NULL == record->fEntry0);
+    GrAssert(NULL == record->fEntry1);
 
-    int width  = this->getRenderTarget()->width();
-    int height = this->getRenderTarget()->height();
+    int boundW = boundRect.width();
+    int boundH = boundRect.height();
+    int size  = GrMax(64, (int)GrNextPow2(GrMax(boundW, boundH)));
 
     GrTextureDesc desc;
-    desc.fAALevel = kNone_GrAALevel;
     if (requireStencil) {
         desc.fFlags = kRenderTarget_GrTextureFlagBit;
     } else {
@@ -435,63 +464,133 @@
                       kNoStencil_GrTextureFlagBit;
     }
 
-    desc.fWidth = 2 * width;
-    desc.fHeight = 2 * height;
     desc.fFormat = kRGBA_8888_GrPixelConfig;
 
-    record->fEntry = this->lockKeylessTexture(desc);
-    if (NULL == record->fEntry) {
+    int scale;
+    // Using MSAA seems to be slower for some yet unknown reason.
+    if (false && fGpu->supportsFullsceneAA()) {
+        scale = GR_Scalar1;
+        desc.fAALevel = kMed_GrAALevel;
+    } else {
+        scale = 4;
+        desc.fAALevel = kNone_GrAALevel;
+    }
+    
+    desc.fWidth = scale * size;
+    desc.fHeight = scale * size;
+
+    record->fEntry0 = this->lockKeylessTexture(desc);
+
+    if (NULL == record->fEntry0) {
         return false;
     }
-    GrRenderTarget* offscreen = record->fEntry->texture()->asRenderTarget();
-    GrAssert(NULL != offscreen);
+
+    if (scale > 1) {
+        desc.fWidth /= 2;
+        desc.fHeight /= 2;
+        record->fEntry1 = this->lockKeylessTexture(desc);
+        if (NULL == record->fEntry1) {
+            this->unlockTexture(record->fEntry0);
+            record->fEntry0 = NULL;
+            return false;
+        }
+    }
+
+    GrRenderTarget* offRT0 = record->fEntry0->texture()->asRenderTarget();
+    GrAssert(NULL != offRT0);
 
     target->saveCurrentDrawState(&record->fSavedState);
 
     GrPaint tempPaint;
     tempPaint.reset();
     SetPaint(tempPaint, target);
-    target->setRenderTarget(offscreen);
+    target->setRenderTarget(offRT0);
 
+    GrMatrix transM;
+    transM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+    target->postConcatViewMatrix(transM);
     GrMatrix scaleM;
-    scaleM.setScale(2 * GR_Scalar1, 2 * GR_Scalar1);
+    scaleM.setScale(scale * GR_Scalar1, scale * GR_Scalar1);
     target->postConcatViewMatrix(scaleM);
 
     // clip gets applied in second pass
     target->disableState(GrDrawTarget::kClip_StateBit);
 
-    target->clear(NULL, 0x0);
+    GrIRect clear(0, 0, scale * boundW, scale * boundH);
+    target->clear(&clear, 0x0);
+
     return true;
 }
 
-void GrContext::setupOffscreenAAPass2(GrDrawTarget* target,
-                                      const GrPaint& paint,
-                                      OffscreenRecord* record) {
+void GrContext::offscreenAAPass2(GrDrawTarget* target,
+                                 const GrPaint& paint,
+                                 const GrIRect& boundRect,
+                                 OffscreenRecord* record) {
 
-    GrAssert(NULL != record->fEntry);
-    GrTexture* offscreen = record->fEntry->texture();
-    GrAssert(NULL != offscreen);
+    GrAssert(NULL != record->fEntry0);
 
-    target->restoreDrawState(record->fSavedState);
-
-    target->setTexture(kOffscreenStage, offscreen);
+    bool downsample =  NULL != record->fEntry1;
+    
     GrMatrix sampleM;
-    sampleM.setScale(GR_Scalar1 / target->getRenderTarget()->width(),
-                     GR_Scalar1 / target->getRenderTarget()->height());
-    sampleM.preConcat(target->getViewMatrix());
-
-    // use bilinear filtering to get downsample
     GrSamplerState sampler(GrSamplerState::kClamp_WrapMode, 
-                           GrSamplerState::kClamp_WrapMode,
-                           sampleM, true);
+                           GrSamplerState::kClamp_WrapMode, true);
+
+    GrTexture* src = record->fEntry0->texture();
+    int scale;
+
+    if (downsample) {
+        scale = 2;
+        GrRenderTarget* dst = record->fEntry1->texture()->asRenderTarget();
+        
+        // Do 2x2 downsample from first to second
+        target->setTexture(kOffscreenStage, src);
+        target->setRenderTarget(dst);
+        target->setViewMatrix(GrMatrix::I());
+        sampleM.setScale(scale * GR_Scalar1 / src->width(),
+                         scale * GR_Scalar1 / src->height());
+        sampler.setMatrix(sampleM);
+        target->setSamplerState(kOffscreenStage, sampler);
+        GrRect rect(0, 0,
+                    scale * boundRect.width(),
+                    scale * boundRect.height());
+        target->drawSimpleRect(rect, NULL, 1 << kOffscreenStage);
+        
+        src = record->fEntry1->texture();
+    } else {
+        scale = 1;
+        GrIRect rect(0, 0, boundRect.width(), boundRect.height());
+        src->asRenderTarget()->overrideResolveRect(rect);
+    }
+
+    // setup for draw back to main RT
+    target->restoreDrawState(record->fSavedState);
+    if (NULL != paint.getTexture()) {
+        GrMatrix invVM;
+        if (target->getViewInverse(&invVM)) {
+            target->preConcatSamplerMatrix(0, invVM);
+        }
+    }
+    target->setViewMatrix(GrMatrix::I());
+
+    target->setTexture(kOffscreenStage, src);
+    sampleM.setScale(scale * GR_Scalar1 / src->width(),
+                     scale * GR_Scalar1 / src->height());
+    sampler.setMatrix(sampleM);
+    sampleM.setTranslate(-boundRect.fLeft, -boundRect.fTop);
+    sampler.preConcatMatrix(sampleM);
     target->setSamplerState(kOffscreenStage, sampler);
-}
 
-void GrContext::endOffscreenAA(GrDrawTarget* target, OffscreenRecord* record) {
-    this->unlockTexture(record->fEntry);
-    record->fEntry = NULL;
+    GrRect dstRect(boundRect);
+    int stages = (1 << kOffscreenStage) | (NULL == paint.getTexture() ? 0 : 1);
+    target->drawSimpleRect(dstRect, NULL, stages);
 
-    target->restoreDrawState(record->fSavedState);    
+    this->unlockTexture(record->fEntry0);
+    record->fEntry0 = NULL;
+    if (downsample) {
+        this->unlockTexture(record->fEntry1);
+        record->fEntry1 = NULL;
+    }
+    target->restoreDrawState(record->fSavedState);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -838,7 +937,7 @@
                                             fGpu->getUnitSquareVertexBuffer());
             GrDrawTarget::AutoViewMatrixRestore avmr(target);
             GrMatrix m;
-            m.setAll(rect.width(), 0,             rect.fLeft,
+            m.setAll(rect.width(),    0,             rect.fLeft,
                         0,            rect.height(), rect.fTop,
                         0,            0,             GrMatrix::I()[8]);
 
@@ -944,15 +1043,9 @@
         vertexSize += sizeof(GrColor);
     }
 
-    bool doOffscreenAA = false;
+    bool doAA = false;
     OffscreenRecord record;
-    if (paint.fAntiAlias &&
-        !this->getRenderTarget()->isMultisampled() &&
-        !(GrIsPrimTypeLines(primitiveType) && fGpu->supportsAALines()) &&
-        this->setupOffscreenAAPass1(target, false, &record)) {
-        doOffscreenAA = true;
-        layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(kOffscreenStage);
-    }
+    GrIRect bounds;
 
     if (sizeof(GrPoint) != vertexSize) {
         if (!geo.set(target, layout, vertexCount, 0)) {
@@ -978,6 +1071,17 @@
             curVertex = (void*)((intptr_t)curVertex + vsize);
         }
     } else {
+        // we don't do offscreen AA when we have per-vertex tex coords or colors
+        if (this->doOffscreenAA(target, paint, GrIsPrimTypeLines(primitiveType))) {
+            GrRect b;
+            b.setBounds(positions, vertexCount);
+            target->getViewMatrix().mapRect(&b);
+            b.roundOut(&bounds);
+            
+            if (this->setupOffscreenAAPass1(target, false, bounds, &record)) {
+                doAA = true;
+            }
+        }
         target->setVertexSourceToArray(layout, positions, vertexCount);
     }
 
@@ -985,45 +1089,14 @@
         target->setIndexSourceToArray(indices, indexCount);
     }
 
-    if (doOffscreenAA) {
-        // draw to the offscreen
-        if (NULL != indices) {
-            target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
-        } else {
-            target->drawNonIndexed(primitiveType, 0, vertexCount);
-        }
-        // When there are custom texture coordinates we can't just draw
-        // a quad to sample the offscreen. Instead we redraw the geometry to
-        // specify the texture coords. This isn't quite right either, primitives
-        // will only be eroded at the edges, not expanded into partial pixels.
-        bool useRect = 0 == (layout & GrDrawTarget::StageTexCoordVertexLayoutBit(0,0));
-        if (useRect) {
-            target->setViewMatrix(GrMatrix::I());
-        }
-        this->setupOffscreenAAPass2(target, paint, &record);
-        if (useRect) {
-            geo.set(NULL, 0, 0, 0);
-            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);
-            target->drawSimpleRect(dstRect, NULL, stages);
-        } else {
-            if (NULL != indices) {
-                target->drawIndexed (primitiveType, 0, 0, vertexCount, indexCount);
-            } else {
-                target->drawNonIndexed(primitiveType, 0, vertexCount);
-            }
-        }
-        this->endOffscreenAA(target, &record);
+    if (NULL != indices) {
+        target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
     } else {
-        if (NULL != indices) {
-            target->drawIndexed(primitiveType, 0, 0, vertexCount, indexCount);
-        } else {
-            target->drawNonIndexed(primitiveType, 0, vertexCount);
-        }
+        target->drawNonIndexed(primitiveType, 0, vertexCount);
+    }
+
+    if (doAA) {
+        this->offscreenAAPass2(target, paint, bounds, &record);
     }
 }
 
@@ -1038,28 +1111,39 @@
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
     GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
 
-    if (paint.fAntiAlias &&
-        !this->getRenderTarget()->isMultisampled() &&
-        !pr->supportsAA()) {
+    if (!IsFillInverted(fill) && // will be relaxed soon
+        !pr->supportsAA(target, path, fill) &&
+        this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
 
-         OffscreenRecord record;
-         bool needsStencil = pr->requiresStencilPass(target, path, fill);
-         if (this->setupOffscreenAAPass1(target, needsStencil, &record)) {
-             pr->drawPath(target, 0, path, fill, translate);
-             
-             target->setViewMatrix(GrMatrix::I());
-             this->setupOffscreenAAPass2(target, paint, &record);
+        OffscreenRecord record;
+        bool needsStencil = pr->requiresStencilPass(target, path, fill);
 
-             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);
+        // compute bounds as intersection of rt size, clip, and path
+        GrIRect bound(0, 0, 
+                      target->getRenderTarget()->width(), 
+                      target->getRenderTarget()->height());
+        if (target->getClip().hasConservativeBounds()) {
+            GrIRect clipIBounds;
+            target->getClip().getConservativeBounds().roundOut(&clipIBounds);
+            if (!bound.intersectWith(clipIBounds)) {
+                return;
+            }
+        }
+        GrRect pathBounds;
+        if (path->getConservativeBounds(&pathBounds)) {
+            GrIRect pathIBounds;
+            target->getViewMatrix().mapRect(&pathBounds, pathBounds);
+            pathBounds.roundOut(&pathIBounds);
+            if (!bound.intersectWith(pathIBounds)) {
+                return;
+            }
+        }
 
-             this->endOffscreenAA(target, &record);
-             return;
-         }
+        if (this->setupOffscreenAAPass1(target, needsStencil, bound, &record)) {
+            pr->drawPath(target, 0, path, fill, translate);
+            this->offscreenAAPass2(target, paint, bound, &record);
+            return;
+        }
     } 
     GrDrawTarget::StageBitfield enabledStages = 0;
     if (NULL != paint.getTexture()) {
@@ -1068,6 +1152,7 @@
 
     pr->drawPath(target, enabledStages, path, fill, translate);
 }
+
 void GrContext::drawPath(const GrPaint& paint,
                          const GrPath& path,
                          GrPathFill fill,
diff --git a/gpu/src/GrGLTexture.cpp b/gpu/src/GrGLTexture.cpp
index 3c6504d..d3ccfa6 100644
--- a/gpu/src/GrGLTexture.cpp
+++ b/gpu/src/GrGLTexture.cpp
@@ -33,7 +33,6 @@
     fTexFBOID               = ids.fTexFBOID;
     fStencilRenderbufferID  = ids.fStencilRenderbufferID;
     fMSColorRenderbufferID  = ids.fMSColorRenderbufferID;
-    fNeedsResolve           = false;
     fViewport               = viewport;
     fOwnIDs                 = ids.fOwnIDs;
     fTexIDObj               = texID;
diff --git a/gpu/src/GrGpuGL.cpp b/gpu/src/GrGpuGL.cpp
index 352ad3f..721d084 100644
--- a/gpu/src/GrGpuGL.cpp
+++ b/gpu/src/GrGpuGL.cpp
@@ -281,6 +281,7 @@
             GrPrintf("\tMax Samples: %d\n", maxSamples);
         }
     }
+    fFSAASupport = fAASamples[kHigh_GrAALevel] > 0;
 
     if (GR_GL_SUPPORT_DESKTOP) {
         fHasStencilWrap = (major >= 2 || (major == 1 && minor >= 4)) ||
@@ -1183,7 +1184,7 @@
             return;
         }
     }
-    this->flushRenderTarget();
+    this->flushRenderTarget(rect);
     this->flushScissor(rect);
     GR_GL(ColorMask(GR_GL_TRUE,GR_GL_TRUE,GR_GL_TRUE,GR_GL_TRUE));
     fHWDrawState.fFlagBits &= ~kNoColorWrites_StateBit;
@@ -1198,7 +1199,9 @@
     if (NULL == fCurrDrawState.fRenderTarget) {
         return;
     }
-    flushRenderTarget();
+    
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
+
     if (fHWBounds.fScissorEnabled) {
         GR_GL(Disable(GR_GL_SCISSOR_TEST));
         fHWBounds.fScissorEnabled = false;
@@ -1223,7 +1226,7 @@
     // zero the client's clip bits. So we just clear the whole thing.
     static const GrGLint clipStencilMask  = ~0;
 #endif
-    flushRenderTarget();
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
     flushScissor(&rect);
     GR_GL(StencilMask(clipStencilMask));
     GR_GL(ClearStencil(0));
@@ -1232,7 +1235,7 @@
 }
 
 void GrGpuGL::onForceRenderTargetFlush() {
-    flushRenderTarget();
+    this->flushRenderTarget(&GrIRect::EmptyIRect());
 }
 
 bool GrGpuGL::onReadPixels(GrRenderTarget* target,
@@ -1252,10 +1255,10 @@
         case GrGLRenderTarget::kAutoResolves_ResolveType:
             autoTargetRestore.save(&fCurrDrawState.fRenderTarget);
             fCurrDrawState.fRenderTarget = target;
-            flushRenderTarget();
+            this->flushRenderTarget(&GrIRect::EmptyIRect());
             break;
         case GrGLRenderTarget::kCanResolve_ResolveType:
-            resolveRenderTarget(tgt);
+            this->resolveRenderTarget(tgt);
             // we don't track the state of the READ FBO ID.
             GR_GL(BindFramebuffer(GR_GL_READ_FRAMEBUFFER, tgt->textureFBOID()));
             break;
@@ -1293,17 +1296,16 @@
     return true;
 }
 
-void GrGpuGL::flushRenderTarget() {
+void GrGpuGL::flushRenderTarget(const GrIRect* bound) {
 
     GrAssert(NULL != fCurrDrawState.fRenderTarget);
 
+    GrGLRenderTarget* rt = (GrGLRenderTarget*)fCurrDrawState.fRenderTarget;
     if (fHWDrawState.fRenderTarget != fCurrDrawState.fRenderTarget) {
-        GrGLRenderTarget* rt = (GrGLRenderTarget*)fCurrDrawState.fRenderTarget;
         GR_GL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
     #if GR_COLLECT_STATS
         ++fStats.fRenderTargetChngCnt;
     #endif
-        rt->flagAsNeedingResolve();
     #if GR_DEBUG
         GrGLenum status = GR_GL(CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
         if (status != GR_GL_FRAMEBUFFER_COMPLETE) {
@@ -1318,6 +1320,9 @@
             fHWBounds.fViewportRect = vp;
         }
     }
+    if (NULL == bound || !bound->isEmpty()) {
+        rt->flagAsNeedingResolve(bound);
+    }
 }
 
 GrGLenum gPrimitiveType2GLMode[] = {
@@ -1427,12 +1432,16 @@
         // the bound DRAW FBO ID.
         fHWDrawState.fRenderTarget = NULL;
         const GrGLIRect& vp = rt->getViewport();
+        const GrIRect dirtyRect = rt->getResolveRect();
+        GrGLIRect r;
+        r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop, 
+                        dirtyRect.width(), dirtyRect.height());
 
         if (kAppleES_MSFBO == fMSFBOType) {
             // Apple's extension uses the scissor as the blit bounds.
             GR_GL(Enable(GR_GL_SCISSOR_TEST));
-            GR_GL(Scissor(vp.fLeft, vp.fBottom,
-                          vp.fWidth, vp.fHeight));
+            GR_GL(Scissor(r.fLeft, r.fBottom,
+                          r.fWidth, r.fHeight));
             GR_GL(ResolveMultisampleFramebuffer());
             fHWBounds.fScissorRect.invalidate();
             fHWBounds.fScissorEnabled = true;
@@ -1442,10 +1451,10 @@
                 GrAssert(kDesktopEXT_MSFBO == fMSFBOType);
                 flushScissor(NULL);
             }
-            int right = vp.fLeft + vp.fWidth;
-            int top = vp.fBottom + vp.fHeight;
-            GR_GL(BlitFramebuffer(vp.fLeft, vp.fBottom, right, top,
-                                  vp.fLeft, vp.fBottom, right, top,
+            int right = r.fLeft + r.fWidth;
+            int top = r.fBottom + r.fHeight;
+            GR_GL(BlitFramebuffer(r.fLeft, r.fBottom, right, top,
+                                  r.fLeft, r.fBottom, right, top,
                                   GR_GL_COLOR_BUFFER_BIT, GR_GL_NEAREST));
         }
         rt->flagAsResolved();
@@ -1781,9 +1790,16 @@
         }
     }
 
-    flushRenderTarget();
-    flushAAState(type);
-    flushBlend(type);
+    GrIRect* rect = NULL;
+    GrIRect clipBounds;
+    if ((fCurrDrawState.fFlagBits & kClip_StateBit) &&
+        fClip.hasConservativeBounds()) {
+        fClip.getConservativeBounds().roundOut(&clipBounds);
+        rect = &clipBounds;
+    }
+    this->flushRenderTarget(rect);
+    this->flushAAState(type);
+    this->flushBlend(type);
     
     if ((fCurrDrawState.fFlagBits & kDither_StateBit) !=
         (fHWDrawState.fFlagBits & kDither_StateBit)) {
diff --git a/gpu/src/GrGpuGL.h b/gpu/src/GrGpuGL.h
index bfa7351..da955cf 100644
--- a/gpu/src/GrGpuGL.h
+++ b/gpu/src/GrGpuGL.h
@@ -155,7 +155,9 @@
 
     bool useSmoothLines();
 
-    void flushRenderTarget();
+    // bound is region that may be modified and therefore has to be resolved.
+    // NULL means whole target. Can be an empty rect.
+    void flushRenderTarget(const GrIRect* bound);
     void flushStencil();
     void flushAAState(GrPrimitiveType type);
     void flushBlend(GrPrimitiveType type);
diff --git a/gpu/src/GrTexture.cpp b/gpu/src/GrTexture.cpp
index 1ea02a7..e9ec4e6 100644
--- a/gpu/src/GrTexture.cpp
+++ b/gpu/src/GrTexture.cpp
@@ -28,6 +28,28 @@
                                            config, buffer);
 }
 
+void GrRenderTarget::flagAsNeedingResolve(const GrIRect* rect) {
+    if (kCanResolve_ResolveType == getResolveType()) {
+        if (NULL != rect) {
+            fResolveRect.growToInclude(*rect);
+            fResolveRect.intersectWith(0, 0, this->width(), this->height());
+        } else {
+            fResolveRect.setLTRB(0, 0, this->width(), this->height());
+        }
+    }
+}
+
+void GrRenderTarget::overrideResolveRect(const GrIRect rect) {
+    fResolveRect = rect;
+    if (fResolveRect.isEmpty()) {
+        fResolveRect.setLargestInverted();
+    } else {
+        if (!fResolveRect.intersectWith(0, 0, this->width(), this->height())) {
+            fResolveRect.setLargestInverted();
+        }
+    }
+}
+
 bool GrTexture::readPixels(int left, int top, int width, int height,
                            GrPixelConfig config, void* buffer) {
     // go through context so that all necessary flushing occurs
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
index f260e1a..4557cc4 100644
--- a/samplecode/SampleVertices.cpp
+++ b/samplecode/SampleVertices.cpp
@@ -21,11 +21,20 @@
 
 static SkShader* make_shader0(SkIPoint* size) {
     SkBitmap    bm;
+    size->set(2, 2);
+    bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY);
+    SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80);
+    SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff);
+    bm.allocPixels();
+    bm.eraseColor(color0);
+    bm.lockPixels();
+    uint32_t* pixels = (uint32_t*) bm.getPixels();
+    pixels[0] = pixels[2] = color0;
+    pixels[1] = pixels[3] = color1;
+    bm.unlockPixels();
 
-    SkImageDecoder::DecodeFile("/skimages/logo.png", &bm);
-    size->set(bm.width(), bm.height());
-    return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
-                                        SkShader::kClamp_TileMode);
+    return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                            SkShader::kRepeat_TileMode);
 }
 
 static SkShader* make_shader1(const SkIPoint& size) {