From d46e2423a71d38b8a057cec2356741e35b03334d Mon Sep 17 00:00:00 2001 From: "bsalomon@google.com" Date: Fri, 23 Sep 2011 17:40:07 +0000 Subject: [PATCH] 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 --- gpu/include/GrContext.h | 3 +-- gpu/src/GrContext.cpp | 46 ++++++++++++++++++++++++++++------------ gpu/src/GrDrawTarget.cpp | 29 +++++++++++++++++++++++-- gpu/src/GrDrawTarget.h | 26 +++++++++++++++++++++++ 4 files changed, 86 insertions(+), 18 deletions(-) diff --git a/gpu/include/GrContext.h b/gpu/include/GrContext.h index 37ac1c4999..eeb96ccc32 100644 --- a/gpu/include/GrContext.h +++ b/gpu/include/GrContext.h @@ -592,8 +592,7 @@ private: 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 ee38d725bc..23ad9e1fce 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 @@ void GrContext::drawPaint(const GrPaint& paint) { //////////////////////////////////////////////////////////////////////////////// +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 @@ struct GrContext::OffscreenRecord { }; 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 @@ bool GrContext::doOffscreenAA(GrDrawTarget* target, 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 isIRect(const GrRect& r) { } static bool apply_aa_to_rect(GrDrawTarget* target, - const GrPaint& paint, const GrRect& rect, GrScalar width, const GrMatrix* matrix, @@ -1150,7 +1151,11 @@ static bool apply_aa_to_rect(GrDrawTarget* target, // 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 @@ void GrContext::drawRect(const GrPaint& paint, 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 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path, 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 @@ void GrContext::drawPath(const GrPaint& paint, const GrPath& path, 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 @@ void GrContext::SetPaint(const GrPaint& paint, GrDrawTarget* target) { } 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 bfcc07de80..59661d8a59 100644 --- a/gpu/src/GrDrawTarget.cpp +++ b/gpu/src/GrDrawTarget.cpp @@ -744,6 +744,25 @@ void GrDrawTarget::drawNonIndexed(GrPrimitiveType type, //////////////////////////////////////////////////////////////////////////////// +// 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 @@ bool GrDrawTarget::CanUseHWAALines(GrVertexLayout layout, const DrState& state) // 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 a57ff81c0a..f1c98e88cb 100644 --- a/gpu/src/GrDrawTarget.h +++ b/gpu/src/GrDrawTarget.h @@ -595,6 +595,28 @@ public: */ 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 @@ public: 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);