Add detection of when partial pixel coverage (for aa or otherwise) will cause incorrect blend

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



git-svn-id: http://skia.googlecode.com/svn/trunk@2323 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h
index 37ac1c4..eeb96cc 100644
--- a/gpu/include/GrContext.h
+++ b/gpu/include/GrContext.h
@@ -592,8 +592,7 @@
     struct OffscreenRecord;
 
     // determines whether offscreen AA should be applied
-    bool doOffscreenAA(GrDrawTarget* target, 
-                       const GrPaint& paint,
+    bool doOffscreenAA(GrDrawTarget* target,
                        bool isHairLines) const;
 
     // attempts to setup offscreen AA. All paint state must be transferred to
diff --git a/gpu/src/GrContext.cpp b/gpu/src/GrContext.cpp
index ee38d72..23ad9e1 100644
--- a/gpu/src/GrContext.cpp
+++ b/gpu/src/GrContext.cpp
@@ -29,6 +29,10 @@
 
 #define BATCH_RECT_TO_RECT (1 && !GR_STATIC_RECT_VB)
 
+// When we're using coverage AA but the blend is incompatible (given gpu
+// limitations) should we disable AA or draw wrong?
+#define DISABLE_COVERAGE_AA_FOR_BLEND 0
+
 static const size_t MAX_TEXTURE_CACHE_COUNT = 256;
 static const size_t MAX_TEXTURE_CACHE_BYTES = 16 * 1024 * 1024;
 
@@ -632,6 +636,12 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+namespace {
+inline bool disable_coverage_aa_for_blend(GrDrawTarget* target) {
+    return DISABLE_COVERAGE_AA_FOR_BLEND && !target->canApplyCoverage();
+}
+}
+
 struct GrContext::OffscreenRecord {
     enum Downsample {
         k4x4TwoPass_Downsample,
@@ -650,12 +660,11 @@
 };
 
 bool GrContext::doOffscreenAA(GrDrawTarget* target,
-                              const GrPaint& paint,
                               bool isHairLines) const {
 #if !GR_USE_OFFSCREEN_AA
     return false;
 #else
-    if (!paint.fAntiAlias) {
+    if (!target->isAntialiasState()) {
         return false;
     }
     // Line primitves are always rasterized as 1 pixel wide.
@@ -667,14 +676,7 @@
     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) {
+    if (disable_coverage_aa_for_blend(target)) {
         return false;
     }
     return true;
@@ -1139,7 +1141,6 @@
 }
 
 static bool apply_aa_to_rect(GrDrawTarget* target,
-                             const GrPaint& paint,
                              const GrRect& rect,
                              GrScalar width, 
                              const GrMatrix* matrix,
@@ -1150,7 +1151,11 @@
     // will be axis-aligned,the render target is not
     // multisampled, and the rect won't land on integer coords.
 
-    if (!paint.fAntiAlias) {
+    if (!target->isAntialiasState()) {
+        return false;
+    }
+
+    if (!target->canTweakAlphaForCoverage()) {
         return false;
     }
 
@@ -1198,7 +1203,7 @@
 
     GrRect devRect = rect;
     GrMatrix combinedMatrix;
-    bool doAA = apply_aa_to_rect(target, paint, rect, width, matrix,
+    bool doAA = apply_aa_to_rect(target, rect, width, matrix,
                                  &combinedMatrix, &devRect);
 
     if (doAA) {
@@ -1440,6 +1445,15 @@
                          GrPathFill fill, const GrPoint* translate) {
 
     GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+
+    // An Assumption here is that path renderer would use some form of tweaking
+    // the src color (either the input alpha or in the frag shader) to implement
+    // aa. If we have some future driver-mojo path AA that can do the right
+    // thing WRT to the blend then we'll need some query on the PR.
+    if (disable_coverage_aa_for_blend(target)) {
+        target->disableState(GrDrawTarget::kAntialias_StateBit);
+    }
+    
     GrPathRenderer* pr = this->getPathRenderer(target, path, fill);
     if (NULL == pr) {
         GrPrintf("Unable to find path renderer compatible with path.\n");
@@ -1450,7 +1464,7 @@
     GrDrawTarget::StageBitfield stageMask = paint.getActiveStageMask();
 
     if (!pr->supportsAA(target, path, fill) &&
-        this->doOffscreenAA(target, paint, kHairLine_PathFill == fill)) {
+        this->doOffscreenAA(target, kHairLine_PathFill == fill)) {
 
         bool needsStencil = pr->requiresStencilPass(target, path, fill);
 
@@ -1666,6 +1680,10 @@
     }
     target->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
     target->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
+
+    if (paint.getActiveMaskStageMask() && !target->canApplyCoverage()) {
+        GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
+    }
 }
 
 GrDrawTarget* GrContext::prepareToDraw(const GrPaint& paint,
diff --git a/gpu/src/GrDrawTarget.cpp b/gpu/src/GrDrawTarget.cpp
index bfcc07d..59661d8 100644
--- a/gpu/src/GrDrawTarget.cpp
+++ b/gpu/src/GrDrawTarget.cpp
@@ -744,6 +744,25 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+// Some blend modes allow folding a partial coverage value into the color's
+// alpha channel, while others will blend incorrectly.
+bool GrDrawTarget::CanTweakAlphaForCoverage(GrBlendCoeff dstCoeff) {
+    /**
+     * The fractional coverage is f
+     * The src and dst coeffs are Cs and Cd
+     * The dst and src colors are S and D
+     * We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D
+     * By tweaking the source color's alpha we're replacing S with S'=fS. It's
+     * obvious that that first term will always be ok. The second term can be
+     * rearranged as [1-(1-Cd)f]D. By substituing in the various possbilities
+     * for Cd we find that only 1, ISA, and ISC produce the correct depth
+     * coeffecient in terms of S' and D.
+     */
+    return kOne_BlendCoeff == dstCoeff ||
+           kISA_BlendCoeff == dstCoeff ||
+           kISC_BlendCoeff == dstCoeff;
+}
+
 bool GrDrawTarget::CanDisableBlend(GrVertexLayout layout, const DrState& state) {
     // If we compute a coverage value (using edge AA or a coverage stage) then
     // we can't force blending off.
@@ -808,9 +827,15 @@
     // there is a conflict between using smooth lines and our use of
     // premultiplied alpha. Smooth lines tweak the incoming alpha value
     // but not in a premul-alpha way. So we only use them when our alpha
-    // is 0xff.
+    // is 0xff and tweaking the color for partial coverage is OK
     return (kAntialias_StateBit & state.fFlagBits) &&
-           CanDisableBlend(layout, state);
+           CanDisableBlend(layout, state) &&
+           CanTweakAlphaForCoverage(state.fDstBlend);
+}
+
+bool GrDrawTarget::canApplyCoverage() const {
+    return this->getCaps().fDualSourceBlendingSupport ||
+           CanTweakAlphaForCoverage(fCurrDrawState.fDstBlend);
 }
 
 bool GrDrawTarget::canDisableBlend() const {
diff --git a/gpu/src/GrDrawTarget.h b/gpu/src/GrDrawTarget.h
index a57ff81..f1c98e8 100644
--- a/gpu/src/GrDrawTarget.h
+++ b/gpu/src/GrDrawTarget.h
@@ -595,6 +595,28 @@
      */
     bool canDisableBlend() const;
 
+    /**
+     * Color alpha and coverage are two inputs to the drawing pipeline. For some
+     * blend modes it is safe to fold the coverage into constant or per-vertex
+     * color alpha value. For other blend modes they must be handled separately.
+     * Depending on features available in the underlying 3D API this may or may
+     * not be possible.
+     *
+     * This function looks at the current blend on the draw target and the draw
+     * target's capabilities to determine whether coverage can be handled
+     * correctly.
+     */
+    bool canApplyCoverage() const;
+
+    /**
+     * Determines whether incorporating partial pixel coverage into the constant
+     * color specified by setColor or per-vertex colors will give the right
+     * blending result.
+     */
+    bool canTweakAlphaForCoverage() const {
+        return CanTweakAlphaForCoverage(fCurrDrawState.fDstBlend);
+    }
+
      /**
       * Determines the interpretation per-vertex edge data when the
       * kEdge_VertexLayoutBit is set (see below). When per-vertex edges are not
@@ -1227,6 +1249,10 @@
 
 protected:
 
+    // Determines whether it is correct to apply partial pixel coverage
+    // by multiplying the src color by the fractional coverage.
+    static bool CanTweakAlphaForCoverage(GrBlendCoeff dstCoeff);
+    
     // determines whether HW blending can be disabled or not
     static bool CanDisableBlend(GrVertexLayout layout, const DrState& state);