diff options
Diffstat (limited to 'src/gpu')
-rw-r--r-- | src/gpu/GrDrawState.cpp | 218 | ||||
-rw-r--r-- | src/gpu/GrDrawState.h | 131 | ||||
-rw-r--r-- | src/gpu/GrOptDrawState.cpp | 147 | ||||
-rw-r--r-- | src/gpu/GrOptDrawState.h | 67 | ||||
-rw-r--r-- | src/gpu/GrRODrawState.cpp | 183 | ||||
-rw-r--r-- | src/gpu/GrRODrawState.h | 60 |
6 files changed, 552 insertions, 254 deletions
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp index ffec6627ca..642ec2669f 100644 --- a/src/gpu/GrDrawState.cpp +++ b/src/gpu/GrDrawState.cpp @@ -6,8 +6,22 @@ */ #include "GrDrawState.h" -#include "GrPaint.h" + #include "GrDrawTargetCaps.h" +#include "GrOptDrawState.h" +#include "GrPaint.h" + +//////////////////////////////////////////////////////////////////////////////s + +GrOptDrawState* GrDrawState::createOptState() const { + if (NULL == fCachedOptState) { + fCachedOptState = SkNEW_ARGS(GrOptDrawState, (*this)); + } else { + SkASSERT(GrOptDrawState(*this) == *fCachedOptState); + } + fCachedOptState->ref(); + return fCachedOptState; +} //////////////////////////////////////////////////////////////////////////////s @@ -50,7 +64,8 @@ GrDrawState::CombinedState GrDrawState::CombineIfPossible( //////////////////////////////////////////////////////////////////////////////s -GrDrawState::GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix) { +GrDrawState::GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix) + : fCachedOptState(NULL) { SkDEBUGCODE(fBlockEffectRemovalCnt = 0;) *this = state; if (!preConcatMatrix.isIdentity()) { @@ -63,7 +78,7 @@ GrDrawState::GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatr for (int i = 0; i < this->numCoverageStages(); ++i) { fCoverageStages[i].localCoordChange(preConcatMatrix); } - this->invalidateBlendOptFlags(); + this->invalidateOptState(); } } @@ -97,6 +112,8 @@ GrDrawState& GrDrawState::operator=(const GrDrawState& that) { fHints = that.fHints; + SkRefCnt_SafeAssign(fCachedOptState, that.fCachedOptState); + memcpy(fFixedFunctionVertexAttribIndices, that.fFixedFunctionVertexAttribIndices, sizeof(fFixedFunctionVertexAttribIndices)); @@ -131,7 +148,7 @@ void GrDrawState::onReset(const SkMatrix* initialViewMatrix) { fHints = 0; - this->invalidateBlendOptFlags(); + this->invalidateOptState(); } bool GrDrawState::setIdentityViewMatrix() { @@ -151,6 +168,7 @@ bool GrDrawState::setIdentityViewMatrix() { fCoverageStages[s].localCoordChange(invVM); } } + this->invalidateOptState(); fViewMatrix.reset(); return true; } @@ -190,7 +208,7 @@ void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRende this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff()); this->setCoverage(paint.getCoverage()); - this->invalidateBlendOptFlags(); + this->invalidateOptState(); } //////////////////////////////////////////////////////////////////////////////// @@ -247,7 +265,7 @@ void GrDrawState::internalSetVertexAttribs(const GrVertexAttrib* attribs, int co overlapCheck |= (mask << offsetShift); #endif } - this->invalidateBlendOptFlags(); + this->invalidateOptState(); // Positions must be specified. SkASSERT(-1 != fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding]); } @@ -267,7 +285,7 @@ void GrDrawState::setDefaultVertexAttribs() { 0xff, sizeof(fFixedFunctionVertexAttribIndices)); fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding] = 0; - this->invalidateBlendOptFlags(); + this->invalidateOptState(); } //////////////////////////////////////////////////////////////////////////////// @@ -289,8 +307,7 @@ bool GrDrawState::couldApplyCoverage(const GrDrawTargetCaps& caps) const { ////////////////////////////////////////////////////////////////////////////// -GrDrawState::AutoVertexAttribRestore::AutoVertexAttribRestore( - GrDrawState* drawState) { +GrDrawState::AutoVertexAttribRestore::AutoVertexAttribRestore(GrDrawState* drawState) { SkASSERT(drawState); fDrawState = drawState; fVAPtr = drawState->fVAPtr; @@ -320,7 +337,7 @@ void GrDrawState::AutoRestoreEffects::set(GrDrawState* ds) { SkASSERT(n >= 0); fDrawState->fCoverageStages.pop_back_n(n); if (m + n > 0) { - fDrawState->invalidateBlendOptFlags(); + fDrawState->invalidateOptState(); } SkDEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;) } @@ -338,118 +355,6 @@ void GrDrawState::AutoRestoreEffects::set(GrDrawState* ds) { //////////////////////////////////////////////////////////////////////////////// -GrRODrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage, - GrBlendCoeff* srcCoeff, - GrBlendCoeff* dstCoeff) const { - GrBlendCoeff bogusSrcCoeff, bogusDstCoeff; - if (NULL == srcCoeff) { - srcCoeff = &bogusSrcCoeff; - } - if (NULL == dstCoeff) { - dstCoeff = &bogusDstCoeff; - } - - if (forceCoverage) { - return this->calcBlendOpts(true, srcCoeff, dstCoeff); - } - - if (0 == (fBlendOptFlags & kInvalid_BlendOptFlag)) { - *srcCoeff = fOptSrcBlend; - *dstCoeff = fOptDstBlend; - return fBlendOptFlags; - } - - fBlendOptFlags = this->calcBlendOpts(forceCoverage, srcCoeff, dstCoeff); - fOptSrcBlend = *srcCoeff; - fOptDstBlend = *dstCoeff; - - return fBlendOptFlags; -} - -GrRODrawState::BlendOptFlags GrDrawState::calcBlendOpts(bool forceCoverage, - GrBlendCoeff* srcCoeff, - GrBlendCoeff* dstCoeff) const { - *srcCoeff = this->getSrcBlendCoeff(); - *dstCoeff = this->getDstBlendCoeff(); - - if (this->isColorWriteDisabled()) { - *srcCoeff = kZero_GrBlendCoeff; - *dstCoeff = kOne_GrBlendCoeff; - } - - bool srcAIsOne = this->srcAlphaWillBeOne(); - bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff || - (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne); - bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff || - (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne); - - // When coeffs are (0,1) there is no reason to draw at all, unless - // stenciling is enabled. Having color writes disabled is effectively - // (0,1). - if ((kZero_GrBlendCoeff == *srcCoeff && dstCoeffIsOne)) { - if (this->getStencil().doesWrite()) { - return kEmitCoverage_BlendOptFlag; - } else { - return kSkipDraw_BlendOptFlag; - } - } - - bool hasCoverage = forceCoverage || !this->hasSolidCoverage(); - - // if we don't have coverage we can check whether the dst - // has to read at all. If not, we'll disable blending. - if (!hasCoverage) { - if (dstCoeffIsZero) { - if (kOne_GrBlendCoeff == *srcCoeff) { - // if there is no coverage and coeffs are (1,0) then we - // won't need to read the dst at all, it gets replaced by src - *dstCoeff = kZero_GrBlendCoeff; - return kNone_BlendOpt; - } else if (kZero_GrBlendCoeff == *srcCoeff) { - // if the op is "clear" then we don't need to emit a color - // or blend, just write transparent black into the dst. - *srcCoeff = kOne_GrBlendCoeff; - *dstCoeff = kZero_GrBlendCoeff; - return kEmitTransBlack_BlendOptFlag; - } - } - } else if (this->isCoverageDrawing()) { - // we have coverage but we aren't distinguishing it from alpha by request. - return kCoverageAsAlpha_BlendOptFlag; - } else { - // check whether coverage can be safely rolled into alpha - // of if we can skip color computation and just emit coverage - if (this->canTweakAlphaForCoverage()) { - return kCoverageAsAlpha_BlendOptFlag; - } - if (dstCoeffIsZero) { - if (kZero_GrBlendCoeff == *srcCoeff) { - // the source color is not included in the blend - // the dst coeff is effectively zero so blend works out to: - // (c)(0)D + (1-c)D = (1-c)D. - *dstCoeff = kISA_GrBlendCoeff; - return kEmitCoverage_BlendOptFlag; - } else if (srcAIsOne) { - // the dst coeff is effectively zero so blend works out to: - // cS + (c)(0)D + (1-c)D = cS + (1-c)D. - // If Sa is 1 then we can replace Sa with c - // and set dst coeff to 1-Sa. - *dstCoeff = kISA_GrBlendCoeff; - return kCoverageAsAlpha_BlendOptFlag; - } - } else if (dstCoeffIsOne) { - // the dst coeff is effectively one so blend works out to: - // cS + (c)(1)D + (1-c)D = cS + D. - *dstCoeff = kOne_GrBlendCoeff; - return kCoverageAsAlpha_BlendOptFlag; - } - } - - return kNone_BlendOpt; -} - -//////////////////////////////////////////////////////////////////////////////// - void GrDrawState::AutoViewMatrixRestore::restore() { if (fDrawState) { SkDEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;) @@ -469,6 +374,7 @@ void GrDrawState::AutoViewMatrixRestore::restore() { for (int s = 0; s < numCoverageStages; ++s, ++i) { fDrawState->fCoverageStages[s].restoreCoordChange(fSavedCoordChanges[i]); } + fDrawState->invalidateOptState(); fDrawState = NULL; } } @@ -488,6 +394,7 @@ void GrDrawState::AutoViewMatrixRestore::set(GrDrawState* drawState, this->doEffectCoordChanges(preconcatMatrix); SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;) + drawState->invalidateOptState(); } bool GrDrawState::AutoViewMatrixRestore::setIdentity(GrDrawState* drawState) { @@ -501,6 +408,7 @@ bool GrDrawState::AutoViewMatrixRestore::setIdentity(GrDrawState* drawState) { return true; } + drawState->invalidateOptState(); fViewMatrix = drawState->getViewMatrix(); if (0 == drawState->numTotalStages()) { drawState->fViewMatrix.reset(); @@ -547,69 +455,3 @@ void GrDrawState::AutoViewMatrixRestore::doEffectCoordChanges(const SkMatrix& co } } -bool GrDrawState::srcAlphaWillBeOne() const { - uint32_t validComponentFlags; - GrColor color; - // Check if per-vertex or constant color may have partial alpha - if (this->hasColorVertexAttribute()) { - if (fHints & kVertexColorsAreOpaque_Hint) { - validComponentFlags = kA_GrColorComponentFlag; - color = 0xFF << GrColor_SHIFT_A; - } else { - validComponentFlags = 0; - color = 0; // not strictly necessary but we get false alarms from tools about uninit. - } - } else { - validComponentFlags = kRGBA_GrColorComponentFlags; - color = this->getColor(); - } - - // Run through the color stages - for (int s = 0; s < this->numColorStages(); ++s) { - const GrEffect* effect = this->getColorStage(s).getEffect(); - effect->getConstantColorComponents(&color, &validComponentFlags); - } - - // Check whether coverage is treated as color. If so we run through the coverage computation. - if (this->isCoverageDrawing()) { - // The shader generated for coverage drawing runs the full coverage computation and then - // makes the shader output be the multiplication of color and coverage. We mirror that here. - GrColor coverage; - uint32_t coverageComponentFlags; - if (this->hasCoverageVertexAttribute()) { - coverageComponentFlags = 0; - coverage = 0; // suppresses any warnings. - } else { - coverageComponentFlags = kRGBA_GrColorComponentFlags; - coverage = this->getCoverageColor(); - } - - // Run through the coverage stages - if (this->hasGeometryProcessor()) { - const GrEffect* effect = fGeometryProcessor->getEffect(); - effect->getConstantColorComponents(&coverage, &coverageComponentFlags); - } - for (int s = 0; s < this->numCoverageStages(); ++s) { - const GrEffect* effect = this->getCoverageStage(s).getEffect(); - effect->getConstantColorComponents(&coverage, &coverageComponentFlags); - } - - // Since the shader will multiply coverage and color, the only way the final A==1 is if - // coverage and color both have A==1. - return (kA_GrColorComponentFlag & validComponentFlags & coverageComponentFlags) && - 0xFF == GrColorUnpackA(color) && 0xFF == GrColorUnpackA(coverage); - - } - - return (kA_GrColorComponentFlag & validComponentFlags) && 0xFF == GrColorUnpackA(color); -} - -//////////////////////////////////////////////////////////////////////////////// - -bool GrDrawState::canIgnoreColorAttribute() const { - if (fBlendOptFlags & kInvalid_BlendOptFlag) { - this->getBlendOpts(); - } - return SkToBool(fBlendOptFlags & (GrRODrawState::kEmitTransBlack_BlendOptFlag | - GrRODrawState::kEmitCoverage_BlendOptFlag)); -} diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h index 2fe56a3d34..4850fc911c 100644 --- a/src/gpu/GrDrawState.h +++ b/src/gpu/GrDrawState.h @@ -9,6 +9,7 @@ #define GrDrawState_DEFINED #include "GrBlend.h" +#include "GrOptDrawState.h" #include "GrProgramResource.h" #include "GrRODrawState.h" #include "effects/GrSimpleTextureEffect.h" @@ -23,12 +24,12 @@ class GrDrawState : public GrRODrawState { public: SK_DECLARE_INST_COUNT(GrDrawState) - GrDrawState() { + GrDrawState() : fCachedOptState(NULL) { SkDEBUGCODE(fBlockEffectRemovalCnt = 0;) this->reset(); } - GrDrawState(const SkMatrix& initialViewMatrix) { + GrDrawState(const SkMatrix& initialViewMatrix) : fCachedOptState(NULL) { SkDEBUGCODE(fBlockEffectRemovalCnt = 0;) this->reset(initialViewMatrix); } @@ -36,7 +37,7 @@ public: /** * Copies another draw state. **/ - GrDrawState(const GrDrawState& state) : INHERITED() { + GrDrawState(const GrDrawState& state) : INHERITED(), fCachedOptState(NULL) { SkDEBUGCODE(fBlockEffectRemovalCnt = 0;) *this = state; } @@ -46,7 +47,10 @@ public: **/ GrDrawState(const GrDrawState& state, const SkMatrix& preConcatMatrix); - virtual ~GrDrawState() { SkASSERT(0 == fBlockEffectRemovalCnt); } + virtual ~GrDrawState() { + SkSafeUnref(fCachedOptState); + SkASSERT(0 == fBlockEffectRemovalCnt); + } /** * Resets to the default state. GrEffects will be removed from all stages. @@ -100,7 +104,8 @@ public: public: AutoVertexAttribRestore(GrDrawState* drawState); - ~AutoVertexAttribRestore() { fDrawState->internalSetVertexAttribs(fVAPtr, fVACount, fVAStride); } + ~AutoVertexAttribRestore() { fDrawState->internalSetVertexAttribs(fVAPtr, fVACount, + fVAStride); } private: GrDrawState* fDrawState; @@ -135,8 +140,10 @@ public: * @param color the color to set. */ void setColor(GrColor color) { - fColor = color; - this->invalidateBlendOptFlags(); + if (color != fColor) { + fColor = color; + this->invalidateOptState(); + } } /** @@ -159,8 +166,10 @@ public: * coverage is ignored when per-vertex coverage is provided. */ void setCoverage(uint8_t coverage) { - fCoverage = coverage; - this->invalidateBlendOptFlags(); + if (coverage != fCoverage) { + fCoverage = coverage; + this->invalidateOptState(); + } } /// @} @@ -175,7 +184,7 @@ public: SkASSERT(effect); SkASSERT(!this->hasGeometryProcessor()); fGeometryProcessor.reset(new GrEffectStage(effect, attr0, attr1)); - this->invalidateBlendOptFlags(); + this->invalidateOptState(); return effect; } @@ -202,14 +211,14 @@ public: const GrEffect* addColorEffect(const GrEffect* effect, int attr0 = -1, int attr1 = -1) { SkASSERT(effect); SkNEW_APPEND_TO_TARRAY(&fColorStages, GrEffectStage, (effect, attr0, attr1)); - this->invalidateBlendOptFlags(); + this->invalidateOptState(); return effect; } const GrEffect* addCoverageEffect(const GrEffect* effect, int attr0 = -1, int attr1 = -1) { SkASSERT(effect); SkNEW_APPEND_TO_TARRAY(&fCoverageStages, GrEffectStage, (effect, attr0, attr1)); - this->invalidateBlendOptFlags(); + this->invalidateOptState(); return effect; } @@ -301,9 +310,11 @@ public: * @param dstCoef coefficient applied to the dst color. */ void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) { - fSrcBlend = srcCoeff; - fDstBlend = dstCoeff; - this->invalidateBlendOptFlags(); + if (srcCoeff != fSrcBlend || dstCoeff != fDstBlend) { + fSrcBlend = srcCoeff; + fDstBlend = dstCoeff; + this->invalidateOptState(); + } #ifdef SK_DEBUG if (GrBlendCoeffRefsDst(dstCoeff)) { GrPrintf("Unexpected dst blend coeff. Won't work correctly with coverage stages.\n"); @@ -325,34 +336,12 @@ public: * @param constant the constant to set */ void setBlendConstant(GrColor constant) { - fBlendConstant = constant; - this->invalidateBlendOptFlags(); + if (constant != fBlendConstant) { + fBlendConstant = constant; + this->invalidateOptState(); + } } - /** - * Determines what optimizations can be applied based on the blend. The coefficients may have - * to be tweaked in order for the optimization to work. srcCoeff and dstCoeff are optional - * params that receive the tweaked coefficients. Normally the function looks at the current - * state to see if coverage is enabled. By setting forceCoverage the caller can speculatively - * determine the blend optimizations that would be used if there was partial pixel coverage. - * - * Subclasses of GrDrawTarget that actually draw (as opposed to those that just buffer for - * playback) must call this function and respect the flags that replace the output color. - * - * If the cached BlendOptFlags does not have the invalidate bit set, then getBlendOpts will - * simply returned the cached flags and coefficients. Otherwise it will calculate the values. - */ - BlendOptFlags getBlendOpts(bool forceCoverage = false, - GrBlendCoeff* srcCoeff = NULL, - GrBlendCoeff* dstCoeff = NULL) const; - - /** - * We don't use suplied vertex color attributes if our blend mode is EmitCoverage or - * EmitTransBlack - */ - bool canIgnoreColorAttribute() const; - - /// @} /////////////////////////////////////////////////////////////////////////// @@ -416,6 +405,7 @@ public: */ void setRenderTarget(GrRenderTarget* target) { fRenderTarget.setResource(SkSafeRef(target), GrProgramResource::kWrite_IOType); + this->invalidateOptState(); } /// @} @@ -432,16 +422,20 @@ public: * @param settings the stencil settings to use. */ void setStencil(const GrStencilSettings& settings) { - fStencilSettings = settings; - this->invalidateBlendOptFlags(); + if (settings != fStencilSettings) { + fStencilSettings = settings; + this->invalidateOptState(); + } } /** * Shortcut to disable stencil testing and ops. */ void disableStencil() { - fStencilSettings.setDisabled(); - this->invalidateBlendOptFlags(); + if (!fStencilSettings.isDisabled()) { + fStencilSettings.setDisabled(); + this->invalidateOptState(); + } } GrStencilSettings* stencil() { return &fStencilSettings; } @@ -453,8 +447,10 @@ public: //// void resetStateFlags() { - fFlagBits = 0; - this->invalidateBlendOptFlags(); + if (0 != fFlagBits) { + fFlagBits = 0; + this->invalidateOptState(); + } } /** @@ -463,8 +459,10 @@ public: * @param stateBits bitfield of StateBits specifying the states to enable */ void enableState(uint32_t stateBits) { - fFlagBits |= stateBits; - this->invalidateBlendOptFlags(); + if (stateBits & ~fFlagBits) { + fFlagBits |= stateBits; + this->invalidateOptState(); + } } /** @@ -473,8 +471,10 @@ public: * @param stateBits bitfield of StateBits specifying the states to disable */ void disableState(uint32_t stateBits) { - fFlagBits &= ~(stateBits); - this->invalidateBlendOptFlags(); + if (stateBits & fFlagBits) { + fFlagBits &= ~(stateBits); + this->invalidateOptState(); + } } /** @@ -513,8 +513,6 @@ public: /// Hints that when provided can enable optimizations. //// - enum Hints { kVertexColorsAreOpaque_Hint = 0x1, }; - void setHint(Hints hint, bool value) { fHints = value ? (fHints | hint) : (fHints & ~hint); } /// @} @@ -542,33 +540,34 @@ public: GrDrawState& operator= (const GrDrawState& that); -private: - void onReset(const SkMatrix* initialViewMatrix); - /** - * Determines whether src alpha is guaranteed to be one for all src pixels + * Returns a snapshot of the current optimized state. If the current drawState has a valid + * cached optimiezed state it will simply return a pointer to it otherwise it will create a new + * GrOptDrawState. In all cases the GrOptDrawState is reffed and ownership is given to the + * caller. */ - bool srcAlphaWillBeOne() const; + GrOptDrawState* createOptState() const; - /** - * Helper function for getBlendOpts. - */ - BlendOptFlags calcBlendOpts(bool forceCoverage = false, - GrBlendCoeff* srcCoeff = NULL, - GrBlendCoeff* dstCoeff = NULL) const; +private: + void invalidateOptState() const { + SkSafeSetNull(fCachedOptState); + fBlendOptFlags = kInvalid_BlendOptFlag; + } + + void onReset(const SkMatrix* initialViewMatrix); void invalidateBlendOptFlags() { fBlendOptFlags = kInvalid_BlendOptFlag; } - uint32_t fHints; - // Some of the auto restore objects assume that no effects are removed during their lifetime. // This is used to assert that this condition holds. SkDEBUGCODE(int fBlockEffectRemovalCnt;) void internalSetVertexAttribs(const GrVertexAttrib attribs[], int count, size_t stride); + mutable GrOptDrawState* fCachedOptState; + typedef GrRODrawState INHERITED; }; diff --git a/src/gpu/GrOptDrawState.cpp b/src/gpu/GrOptDrawState.cpp new file mode 100644 index 0000000000..83546ba3b7 --- /dev/null +++ b/src/gpu/GrOptDrawState.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrOptDrawState.h" + +#include "GrDrawState.h" + +GrOptDrawState::GrOptDrawState(const GrDrawState& drawState) : INHERITED(drawState) { + fColor = drawState.getColor(); + fCoverage = drawState.getCoverage(); + fViewMatrix = drawState.getViewMatrix(); + fBlendConstant = drawState.getBlendConstant(); + fFlagBits = drawState.getFlagBits(); + fVAPtr = drawState.getVertexAttribs(); + fVACount = drawState.getVertexAttribCount(); + fVAStride = drawState.getVertexStride(); + fStencilSettings = drawState.getStencil(); + fDrawFace = drawState.getDrawFace(); + + fBlendOptFlags = drawState.getBlendOpts(false, &fSrcBlend, &fDstBlend); + + memcpy(fFixedFunctionVertexAttribIndices, + drawState.getFixedFunctionVertexAttribIndices(), + sizeof(fFixedFunctionVertexAttribIndices)); + + fInputColorIsUsed = true; + fInputCoverageIsUsed = true; + + if (drawState.hasGeometryProcessor()) { + fGeometryProcessor.reset(SkNEW_ARGS(GrEffectStage, (*drawState.getGeometryProcessor()))); + } else { + fGeometryProcessor.reset(NULL); + } + + this->copyEffectiveColorStages(drawState); + this->copyEffectiveCoverageStages(drawState); +}; + +void GrOptDrawState::removeFixedFunctionVertexAttribs(uint8_t removeVAFlag) { + int numToRemove = 0; + uint8_t maskCheck = 0x1; + // Count the number of vertex attributes that we will actually remove + for (int i = 0; i < kGrFixedFunctionVertexAttribBindingCnt; ++i) { + if ((maskCheck & removeVAFlag) && -1 != fFixedFunctionVertexAttribIndices[i]) { + ++numToRemove; + } + maskCheck <<= 1; + } + fOptVA.reset(fVACount - numToRemove); + + GrVertexAttrib* dst = fOptVA.get(); + const GrVertexAttrib* src = fVAPtr; + + for (int i = 0, newIdx = 0; i < fVACount; ++i, ++src) { + const GrVertexAttrib& currAttrib = *src; + if (currAttrib.fBinding < kGrFixedFunctionVertexAttribBindingCnt) { + uint8_t maskCheck = 0x1 << currAttrib.fBinding; + if (maskCheck & removeVAFlag) { + SkASSERT(-1 != fFixedFunctionVertexAttribIndices[currAttrib.fBinding]); + fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = -1; + continue; + } + } + memcpy(dst, src, sizeof(GrVertexAttrib)); + fFixedFunctionVertexAttribIndices[currAttrib.fBinding] = newIdx; + ++newIdx; + ++dst; + } + fVACount -= numToRemove; + fVAPtr = fOptVA.get(); +} + +void GrOptDrawState::copyEffectiveColorStages(const GrDrawState& ds) { + int firstColorStage = 0; + + // Set up color and flags for ConstantColorComponent checks + GrColor color; + uint32_t validComponentFlags; + if (!this->hasColorVertexAttribute()) { + color = ds.getColor(); + validComponentFlags = kRGBA_GrColorComponentFlags; + } else { + if (ds.vertexColorsAreOpaque()) { + color = 0xFF << GrColor_SHIFT_A; + validComponentFlags = kA_GrColorComponentFlag; + } else { + validComponentFlags = 0; + color = 0; // not strictly necessary but we get false alarms from tools about uninit. + } + } + + for (int i = 0; i < ds.numColorStages(); ++i) { + const GrEffect* effect = ds.getColorStage(i).getEffect(); + if (!effect->willUseInputColor()) { + firstColorStage = i; + fInputColorIsUsed = false; + } + effect->getConstantColorComponents(&color, &validComponentFlags); + if (kRGBA_GrColorComponentFlags == validComponentFlags) { + firstColorStage = i + 1; + fColor = color; + fInputColorIsUsed = true; + this->removeFixedFunctionVertexAttribs(0x1 << kColor_GrVertexAttribBinding); + } + } + if (firstColorStage < ds.numColorStages()) { + fColorStages.reset(&ds.getColorStage(firstColorStage), + ds.numColorStages() - firstColorStage); + } else { + fColorStages.reset(); + } +} + +void GrOptDrawState::copyEffectiveCoverageStages(const GrDrawState& ds) { + int firstCoverageStage = 0; + + // We do not try to optimize out constantColor coverage effects here. It is extremely rare + // to have a coverage effect that returns a constant value for all four channels. Thus we + // save having to make extra virtual calls by not checking for it. + + // Don't do any optimizations on coverage stages. It should not be the case where we do not use + // input coverage in an effect +#ifdef OptCoverageStages + for (int i = 0; i < ds.numCoverageStages(); ++i) { + const GrEffect* effect = ds.getCoverageStage(i).getEffect(); + if (!effect->willUseInputColor()) { + firstCoverageStage = i; + fInputCoverageIsUsed = false; + } + } +#endif + if (ds.numCoverageStages() > 0) { + fCoverageStages.reset(&ds.getCoverageStage(firstCoverageStage), + ds.numCoverageStages() - firstCoverageStage); + } else { + fCoverageStages.reset(); + } +} + +bool GrOptDrawState::operator== (const GrOptDrawState& that) const { + return this->isEqual(that); +} + diff --git a/src/gpu/GrOptDrawState.h b/src/gpu/GrOptDrawState.h new file mode 100644 index 0000000000..a4edc01c8a --- /dev/null +++ b/src/gpu/GrOptDrawState.h @@ -0,0 +1,67 @@ +/* + * Copyright 2014 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrOptDrawState_DEFINED +#define GrOptDrawState_DEFINED + +#include "GrRODrawState.h" + +class GrDrawState; + +/** + * Subclass of GrRODrawState that holds an optimized version of a GrDrawState. Like it's parent + * it is meant to be an immutable class, and simply adds a few helpful data members not in the + * base class. + */ +class GrOptDrawState : public GrRODrawState { +public: + /** + * Constructs and optimized drawState out of a GrRODrawState. + */ + explicit GrOptDrawState(const GrDrawState& drawState); + + bool operator== (const GrOptDrawState& that) const; + +private: + /* + * Loops through all the color stage effects to check if the stage will ignore color input or + * always output a constant color. In the ignore color input case we can ignore all previous + * stages. In the constant color case, we can ignore all previous stages and + * the current one and set the state color to the constant color. Once we determine the so + * called first effective stage, we copy all the effective stages into our optimized + * state. + */ + void copyEffectiveColorStages(const GrDrawState& ds); + + /* + * Loops through all the coverage stage effects to check if the stage will ignore color input. + * If a coverage stage will ignore input, then we can ignore all coverage stages before it. We + * loop to determine the first effective coverage stage, and then copy all of our effective + * coverage stages into our optimized state. + */ + void copyEffectiveCoverageStages(const GrDrawState& ds); + + /* + * This function takes in a flag and removes the corresponding fixed function vertex attributes. + * The flags are in the same order as GrVertexAttribBinding array. If bit i of removeVAFlags is + * set, then vertex attributes with binding (GrVertexAttribute)i will be removed. + */ + void removeFixedFunctionVertexAttribs(uint8_t removeVAFlags); + + void removeColorVertexAttrib(); + + // These flags are needed to protect the code from creating an unused uniform color/coverage + // which will cause shader compiler errors. + bool fInputColorIsUsed; + bool fInputCoverageIsUsed; + + SkAutoSTArray<4, GrVertexAttrib> fOptVA; + + typedef GrRODrawState INHERITED; +}; + +#endif diff --git a/src/gpu/GrRODrawState.cpp b/src/gpu/GrRODrawState.cpp index 8d6c283f7a..b79e8fce43 100644 --- a/src/gpu/GrRODrawState.cpp +++ b/src/gpu/GrRODrawState.cpp @@ -6,10 +6,17 @@ */ #include "GrRODrawState.h" + #include "GrDrawTargetCaps.h" +#include "GrRenderTarget.h" //////////////////////////////////////////////////////////////////////////////// +GrRODrawState::GrRODrawState(const GrRODrawState& drawState) : INHERITED() { + fRenderTarget.setResource(SkSafeRef(drawState.fRenderTarget.getResource()), + GrProgramResource::kWrite_IOType); +} + bool GrRODrawState::isEqual(const GrRODrawState& that) const { bool usingVertexColors = this->hasColorVertexAttribute(); if (!usingVertexColors && this->fColor != that.fColor) { @@ -164,6 +171,118 @@ bool GrRODrawState::willEffectReadDstColor() const { //////////////////////////////////////////////////////////////////////////////// +GrRODrawState::BlendOptFlags GrRODrawState::getBlendOpts(bool forceCoverage, + GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff) const { + GrBlendCoeff bogusSrcCoeff, bogusDstCoeff; + if (NULL == srcCoeff) { + srcCoeff = &bogusSrcCoeff; + } + if (NULL == dstCoeff) { + dstCoeff = &bogusDstCoeff; + } + + if (forceCoverage) { + return this->calcBlendOpts(true, srcCoeff, dstCoeff); + } + + if (0 == (fBlendOptFlags & kInvalid_BlendOptFlag)) { + *srcCoeff = fOptSrcBlend; + *dstCoeff = fOptDstBlend; + return fBlendOptFlags; + } + + fBlendOptFlags = this->calcBlendOpts(forceCoverage, srcCoeff, dstCoeff); + fOptSrcBlend = *srcCoeff; + fOptDstBlend = *dstCoeff; + + return fBlendOptFlags; +} + +GrRODrawState::BlendOptFlags GrRODrawState::calcBlendOpts(bool forceCoverage, + GrBlendCoeff* srcCoeff, + GrBlendCoeff* dstCoeff) const { + *srcCoeff = this->getSrcBlendCoeff(); + *dstCoeff = this->getDstBlendCoeff(); + + if (this->isColorWriteDisabled()) { + *srcCoeff = kZero_GrBlendCoeff; + *dstCoeff = kOne_GrBlendCoeff; + } + + bool srcAIsOne = this->srcAlphaWillBeOne(); + bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff || + (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne); + bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff || + (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne); + + // When coeffs are (0,1) there is no reason to draw at all, unless + // stenciling is enabled. Having color writes disabled is effectively + // (0,1). + if ((kZero_GrBlendCoeff == *srcCoeff && dstCoeffIsOne)) { + if (this->getStencil().doesWrite()) { + return kEmitCoverage_BlendOptFlag; + } else { + return kSkipDraw_BlendOptFlag; + } + } + + bool hasCoverage = forceCoverage || !this->hasSolidCoverage(); + + // if we don't have coverage we can check whether the dst + // has to read at all. If not, we'll disable blending. + if (!hasCoverage) { + if (dstCoeffIsZero) { + if (kOne_GrBlendCoeff == *srcCoeff) { + // if there is no coverage and coeffs are (1,0) then we + // won't need to read the dst at all, it gets replaced by src + *dstCoeff = kZero_GrBlendCoeff; + return kNone_BlendOpt; + } else if (kZero_GrBlendCoeff == *srcCoeff) { + // if the op is "clear" then we don't need to emit a color + // or blend, just write transparent black into the dst. + *srcCoeff = kOne_GrBlendCoeff; + *dstCoeff = kZero_GrBlendCoeff; + return kEmitTransBlack_BlendOptFlag; + } + } + } else if (this->isCoverageDrawing()) { + // we have coverage but we aren't distinguishing it from alpha by request. + return kCoverageAsAlpha_BlendOptFlag; + } else { + // check whether coverage can be safely rolled into alpha + // of if we can skip color computation and just emit coverage + if (this->canTweakAlphaForCoverage()) { + return kCoverageAsAlpha_BlendOptFlag; + } + if (dstCoeffIsZero) { + if (kZero_GrBlendCoeff == *srcCoeff) { + // the source color is not included in the blend + // the dst coeff is effectively zero so blend works out to: + // (c)(0)D + (1-c)D = (1-c)D. + *dstCoeff = kISA_GrBlendCoeff; + return kEmitCoverage_BlendOptFlag; + } else if (srcAIsOne) { + // the dst coeff is effectively zero so blend works out to: + // cS + (c)(0)D + (1-c)D = cS + (1-c)D. + // If Sa is 1 then we can replace Sa with c + // and set dst coeff to 1-Sa. + *dstCoeff = kISA_GrBlendCoeff; + return kCoverageAsAlpha_BlendOptFlag; + } + } else if (dstCoeffIsOne) { + // the dst coeff is effectively one so blend works out to: + // cS + (c)(1)D + (1-c)D = cS + D. + *dstCoeff = kOne_GrBlendCoeff; + return kCoverageAsAlpha_BlendOptFlag; + } + } + + return kNone_BlendOpt; +} + +//////////////////////////////////////////////////////////////////////////////// + // Some blend modes allow folding a fractional coverage value into the color's alpha channel, while // others will blend incorrectly. bool GrRODrawState::canTweakAlphaForCoverage() const { @@ -197,3 +316,67 @@ void GrRODrawState::convertToPendingExec() { fCoverageStages[i].convertToPendingExec(); } } + +bool GrRODrawState::srcAlphaWillBeOne() const { + uint32_t validComponentFlags; + GrColor color; + // Check if per-vertex or constant color may have partial alpha + if (this->hasColorVertexAttribute()) { + if (fHints & kVertexColorsAreOpaque_Hint) { + validComponentFlags = kA_GrColorComponentFlag; + color = 0xFF << GrColor_SHIFT_A; + } else { + validComponentFlags = 0; + color = 0; // not strictly necessary but we get false alarms from tools about uninit. + } + } else { + validComponentFlags = kRGBA_GrColorComponentFlags; + color = this->getColor(); + } + + // Run through the color stages + for (int s = 0; s < this->numColorStages(); ++s) { + const GrEffect* effect = this->getColorStage(s).getEffect(); + effect->getConstantColorComponents(&color, &validComponentFlags); + } + + // Check whether coverage is treated as color. If so we run through the coverage computation. + if (this->isCoverageDrawing()) { + // The shader generated for coverage drawing runs the full coverage computation and then + // makes the shader output be the multiplication of color and coverage. We mirror that here. + GrColor coverage; + uint32_t coverageComponentFlags; + if (this->hasCoverageVertexAttribute()) { + coverageComponentFlags = 0; + coverage = 0; // suppresses any warnings. + } else { + coverageComponentFlags = kRGBA_GrColorComponentFlags; + coverage = this->getCoverageColor(); + } + + // Run through the coverage stages + for (int s = 0; s < this->numCoverageStages(); ++s) { + const GrEffect* effect = this->getCoverageStage(s).getEffect(); + effect->getConstantColorComponents(&coverage, &coverageComponentFlags); + } + + // Since the shader will multiply coverage and color, the only way the final A==1 is if + // coverage and color both have A==1. + return (kA_GrColorComponentFlag & validComponentFlags & coverageComponentFlags) && + 0xFF == GrColorUnpackA(color) && 0xFF == GrColorUnpackA(coverage); + + } + + return (kA_GrColorComponentFlag & validComponentFlags) && 0xFF == GrColorUnpackA(color); +} + +//////////////////////////////////////////////////////////////////////////////// + +bool GrRODrawState::canIgnoreColorAttribute() const { + if (fBlendOptFlags & kInvalid_BlendOptFlag) { + this->getBlendOpts(); + } + return SkToBool(fBlendOptFlags & (GrRODrawState::kEmitTransBlack_BlendOptFlag | + GrRODrawState::kEmitCoverage_BlendOptFlag)); +} + diff --git a/src/gpu/GrRODrawState.h b/src/gpu/GrRODrawState.h index 718f017de3..d748e7d83a 100644 --- a/src/gpu/GrRODrawState.h +++ b/src/gpu/GrRODrawState.h @@ -13,6 +13,7 @@ #include "GrStencil.h" #include "SkMatrix.h" +class GrDrawState; class GrDrawTargetCaps; class GrPaint; class GrTexture; @@ -26,6 +27,10 @@ class GrRODrawState : public SkRefCnt { public: SK_DECLARE_INST_COUNT(GrRODrawState) + GrRODrawState() {} + + GrRODrawState& operator= (const GrRODrawState& that); + /////////////////////////////////////////////////////////////////////////// /// @name Vertex Attributes //// @@ -68,6 +73,10 @@ public: return -1 != fFixedFunctionVertexAttribIndices[kCoverage_GrVertexAttribBinding]; } + const int* getFixedFunctionVertexAttribIndices() const { + return fFixedFunctionVertexAttribIndices; + } + bool validateVertexAttribs() const; /// @} @@ -158,6 +167,12 @@ public: GrColor getBlendConstant() const { return fBlendConstant; } /** + * We don't use supplied vertex color attributes if our blend mode is EmitCoverage or + * EmitTransBlack + */ + bool canIgnoreColorAttribute() const; + + /** * Determines whether multiplying the computed per-pixel color by the pixel's fractional * coverage before the blend will give the correct final destination color. In general it * will not as coverage is applied after blending. @@ -198,6 +213,22 @@ public: }; GR_DECL_BITFIELD_OPS_FRIENDS(BlendOptFlags); + /** + * Determines what optimizations can be applied based on the blend. The coefficients may have + * to be tweaked in order for the optimization to work. srcCoeff and dstCoeff are optional + * params that receive the tweaked coefficients. Normally the function looks at the current + * state to see if coverage is enabled. By setting forceCoverage the caller can speculatively + * determine the blend optimizations that would be used if there was partial pixel coverage. + * + * Subclasses of GrDrawTarget that actually draw (as opposed to those that just buffer for + * playback) must call this function and respect the flags that replace the output color. + * + * If the cached BlendOptFlags does not have the invalidate bit set, then getBlendOpts will + * simply returned the cached flags and coefficients. Otherwise it will calculate the values. + */ + BlendOptFlags getBlendOpts(bool forceCoverage = false, + GrBlendCoeff* srcCoeff = NULL, + GrBlendCoeff* dstCoeff = NULL) const; /// @} /////////////////////////////////////////////////////////////////////////// @@ -301,6 +332,8 @@ public: kLastPublicStateBit = kDummyStateBit-1, }; + uint32_t getFlagBits() const { return fFlagBits; } + bool isStateFlagEnabled(uint32_t stateBit) const { return 0 != (stateBit & fFlagBits); } bool isDitherState() const { return 0 != (fFlagBits & kDither_StateBit); } @@ -333,6 +366,17 @@ public: /// @} /////////////////////////////////////////////////////////////////////////// + /// @name Hints + /// Hints that when provided can enable optimizations. + //// + + enum Hints { kVertexColorsAreOpaque_Hint = 0x1, }; + + bool vertexColorsAreOpaque() const { return kVertexColorsAreOpaque_Hint & fHints; } + + /// @} + + /////////////////////////////////////////////////////////////////////////// /** Return type for CombineIfPossible. */ enum CombinedState { @@ -359,6 +403,8 @@ protected: friend class GrDrawTarget; + explicit GrRODrawState(const GrRODrawState& drawState); + bool isEqual(const GrRODrawState& that) const; // These fields are roughly sorted by decreasing likelihood of being different in op== @@ -381,6 +427,8 @@ protected: EffectStageArray fColorStages; EffectStageArray fCoverageStages; + uint32_t fHints; + mutable GrBlendCoeff fOptSrcBlend; mutable GrBlendCoeff fOptDstBlend; mutable BlendOptFlags fBlendOptFlags; @@ -390,6 +438,18 @@ protected: int fFixedFunctionVertexAttribIndices[kGrFixedFunctionVertexAttribBindingCnt]; private: + /** + * Determines whether src alpha is guaranteed to be one for all src pixels + */ + bool srcAlphaWillBeOne() const; + + /** + * Helper function for getBlendOpts. + */ + BlendOptFlags calcBlendOpts(bool forceCoverage = false, + GrBlendCoeff* srcCoeff = NULL, + GrBlendCoeff* dstCoeff = NULL) const; + typedef SkRefCnt INHERITED; }; |