michael@0: /* michael@0: * Copyright 2012 Google Inc. michael@0: * michael@0: * Use of this source code is governed by a BSD-style license that can be michael@0: * found in the LICENSE file. michael@0: */ michael@0: michael@0: #include "GrDrawState.h" michael@0: #include "GrPaint.h" michael@0: michael@0: bool GrDrawState::setIdentityViewMatrix() { michael@0: if (fColorStages.count() || fCoverageStages.count()) { michael@0: SkMatrix invVM; michael@0: if (!fCommon.fViewMatrix.invert(&invVM)) { michael@0: // sad trombone sound michael@0: return false; michael@0: } michael@0: for (int s = 0; s < fColorStages.count(); ++s) { michael@0: fColorStages[s].localCoordChange(invVM); michael@0: } michael@0: for (int s = 0; s < fCoverageStages.count(); ++s) { michael@0: fCoverageStages[s].localCoordChange(invVM); michael@0: } michael@0: } michael@0: fCommon.fViewMatrix.reset(); michael@0: return true; michael@0: } michael@0: michael@0: void GrDrawState::setFromPaint(const GrPaint& paint, const SkMatrix& vm, GrRenderTarget* rt) { michael@0: SkASSERT(0 == fBlockEffectRemovalCnt || 0 == this->numTotalStages()); michael@0: michael@0: fColorStages.reset(); michael@0: fCoverageStages.reset(); michael@0: michael@0: for (int i = 0; i < paint.numColorStages(); ++i) { michael@0: fColorStages.push_back(paint.getColorStage(i)); michael@0: } michael@0: michael@0: for (int i = 0; i < paint.numCoverageStages(); ++i) { michael@0: fCoverageStages.push_back(paint.getCoverageStage(i)); michael@0: } michael@0: michael@0: this->setRenderTarget(rt); michael@0: michael@0: fCommon.fViewMatrix = vm; michael@0: michael@0: // These have no equivalent in GrPaint, set them to defaults michael@0: fCommon.fBlendConstant = 0x0; michael@0: fCommon.fDrawFace = kBoth_DrawFace; michael@0: fCommon.fStencilSettings.setDisabled(); michael@0: this->resetStateFlags(); michael@0: michael@0: // Enable the clip bit michael@0: this->enableState(GrDrawState::kClip_StateBit); michael@0: michael@0: this->setColor(paint.getColor()); michael@0: this->setState(GrDrawState::kDither_StateBit, paint.isDither()); michael@0: this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias()); michael@0: michael@0: this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff()); michael@0: this->setCoverage(paint.getCoverage()); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: static size_t vertex_size(const GrVertexAttrib* attribs, int count) { michael@0: // this works as long as we're 4 byte-aligned michael@0: #ifdef SK_DEBUG michael@0: uint32_t overlapCheck = 0; michael@0: #endif michael@0: SkASSERT(count <= GrDrawState::kMaxVertexAttribCnt); michael@0: size_t size = 0; michael@0: for (int index = 0; index < count; ++index) { michael@0: size_t attribSize = GrVertexAttribTypeSize(attribs[index].fType); michael@0: size += attribSize; michael@0: #ifdef SK_DEBUG michael@0: size_t dwordCount = attribSize >> 2; michael@0: uint32_t mask = (1 << dwordCount)-1; michael@0: size_t offsetShift = attribs[index].fOffset >> 2; michael@0: SkASSERT(!(overlapCheck & (mask << offsetShift))); michael@0: overlapCheck |= (mask << offsetShift); michael@0: #endif michael@0: } michael@0: return size; michael@0: } michael@0: michael@0: size_t GrDrawState::getVertexSize() const { michael@0: return vertex_size(fCommon.fVAPtr, fCommon.fVACount); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrDrawState::setVertexAttribs(const GrVertexAttrib* attribs, int count) { michael@0: SkASSERT(count <= kMaxVertexAttribCnt); michael@0: michael@0: fCommon.fVAPtr = attribs; michael@0: fCommon.fVACount = count; michael@0: michael@0: // Set all the indices to -1 michael@0: memset(fCommon.fFixedFunctionVertexAttribIndices, michael@0: 0xff, michael@0: sizeof(fCommon.fFixedFunctionVertexAttribIndices)); michael@0: #ifdef SK_DEBUG michael@0: uint32_t overlapCheck = 0; michael@0: #endif michael@0: for (int i = 0; i < count; ++i) { michael@0: if (attribs[i].fBinding < kGrFixedFunctionVertexAttribBindingCnt) { michael@0: // The fixed function attribs can only be specified once michael@0: SkASSERT(-1 == fCommon.fFixedFunctionVertexAttribIndices[attribs[i].fBinding]); michael@0: SkASSERT(GrFixedFunctionVertexAttribVectorCount(attribs[i].fBinding) == michael@0: GrVertexAttribTypeVectorCount(attribs[i].fType)); michael@0: fCommon.fFixedFunctionVertexAttribIndices[attribs[i].fBinding] = i; michael@0: } michael@0: #ifdef SK_DEBUG michael@0: size_t dwordCount = GrVertexAttribTypeSize(attribs[i].fType) >> 2; michael@0: uint32_t mask = (1 << dwordCount)-1; michael@0: size_t offsetShift = attribs[i].fOffset >> 2; michael@0: SkASSERT(!(overlapCheck & (mask << offsetShift))); michael@0: overlapCheck |= (mask << offsetShift); michael@0: #endif michael@0: } michael@0: // Positions must be specified. michael@0: SkASSERT(-1 != fCommon.fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding]); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrDrawState::setDefaultVertexAttribs() { michael@0: static const GrVertexAttrib kPositionAttrib = michael@0: {kVec2f_GrVertexAttribType, 0, kPosition_GrVertexAttribBinding}; michael@0: michael@0: fCommon.fVAPtr = &kPositionAttrib; michael@0: fCommon.fVACount = 1; michael@0: michael@0: // set all the fixed function indices to -1 except position. michael@0: memset(fCommon.fFixedFunctionVertexAttribIndices, michael@0: 0xff, michael@0: sizeof(fCommon.fFixedFunctionVertexAttribIndices)); michael@0: fCommon.fFixedFunctionVertexAttribIndices[kPosition_GrVertexAttribBinding] = 0; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool GrDrawState::validateVertexAttribs() const { michael@0: // check consistency of effects and attributes michael@0: GrSLType slTypes[kMaxVertexAttribCnt]; michael@0: for (int i = 0; i < kMaxVertexAttribCnt; ++i) { michael@0: slTypes[i] = static_cast(-1); michael@0: } michael@0: int totalStages = fColorStages.count() + fCoverageStages.count(); michael@0: for (int s = 0; s < totalStages; ++s) { michael@0: int covIdx = s - fColorStages.count(); michael@0: const GrEffectStage& stage = covIdx < 0 ? fColorStages[s] : fCoverageStages[covIdx]; michael@0: const GrEffectRef* effect = stage.getEffect(); michael@0: SkASSERT(NULL != effect); michael@0: // make sure that any attribute indices have the correct binding type, that the attrib michael@0: // type and effect's shader lang type are compatible, and that attributes shared by michael@0: // multiple effects use the same shader lang type. michael@0: const int* attributeIndices = stage.getVertexAttribIndices(); michael@0: int numAttributes = stage.getVertexAttribIndexCount(); michael@0: for (int i = 0; i < numAttributes; ++i) { michael@0: int attribIndex = attributeIndices[i]; michael@0: if (attribIndex >= fCommon.fVACount || michael@0: kEffect_GrVertexAttribBinding != fCommon.fVAPtr[attribIndex].fBinding) { michael@0: return false; michael@0: } michael@0: michael@0: GrSLType effectSLType = (*effect)->vertexAttribType(i); michael@0: GrVertexAttribType attribType = fCommon.fVAPtr[attribIndex].fType; michael@0: int slVecCount = GrSLTypeVectorCount(effectSLType); michael@0: int attribVecCount = GrVertexAttribTypeVectorCount(attribType); michael@0: if (slVecCount != attribVecCount || michael@0: (static_cast(-1) != slTypes[attribIndex] && michael@0: slTypes[attribIndex] != effectSLType)) { michael@0: return false; michael@0: } michael@0: slTypes[attribIndex] = effectSLType; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool GrDrawState::willEffectReadDstColor() const { michael@0: if (!this->isColorWriteDisabled()) { michael@0: for (int s = 0; s < fColorStages.count(); ++s) { michael@0: if ((*fColorStages[s].getEffect())->willReadDstColor()) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: for (int s = 0; s < fCoverageStages.count(); ++s) { michael@0: if ((*fCoverageStages[s].getEffect())->willReadDstColor()) { michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: bool GrDrawState::srcAlphaWillBeOne() const { michael@0: uint32_t validComponentFlags; michael@0: GrColor color; michael@0: // Check if per-vertex or constant color may have partial alpha michael@0: if (this->hasColorVertexAttribute()) { michael@0: validComponentFlags = 0; michael@0: color = 0; // not strictly necessary but we get false alarms from tools about uninit. michael@0: } else { michael@0: validComponentFlags = kRGBA_GrColorComponentFlags; michael@0: color = this->getColor(); michael@0: } michael@0: michael@0: // Run through the color stages michael@0: for (int s = 0; s < fColorStages.count(); ++s) { michael@0: const GrEffectRef* effect = fColorStages[s].getEffect(); michael@0: (*effect)->getConstantColorComponents(&color, &validComponentFlags); michael@0: } michael@0: michael@0: // Check whether coverage is treated as color. If so we run through the coverage computation. michael@0: if (this->isCoverageDrawing()) { michael@0: GrColor coverageColor = this->getCoverageColor(); michael@0: GrColor oldColor = color; michael@0: color = 0; michael@0: for (int c = 0; c < 4; ++c) { michael@0: if (validComponentFlags & (1 << c)) { michael@0: U8CPU a = (oldColor >> (c * 8)) & 0xff; michael@0: U8CPU b = (coverageColor >> (c * 8)) & 0xff; michael@0: color |= (SkMulDiv255Round(a, b) << (c * 8)); michael@0: } michael@0: } michael@0: for (int s = 0; s < fCoverageStages.count(); ++s) { michael@0: const GrEffectRef* effect = fCoverageStages[s].getEffect(); michael@0: (*effect)->getConstantColorComponents(&color, &validComponentFlags); michael@0: } michael@0: } michael@0: return (kA_GrColorComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color); michael@0: } michael@0: michael@0: bool GrDrawState::hasSolidCoverage() const { michael@0: // If we're drawing coverage directly then coverage is effectively treated as color. michael@0: if (this->isCoverageDrawing()) { michael@0: return true; michael@0: } michael@0: michael@0: GrColor coverage; michael@0: uint32_t validComponentFlags; michael@0: // Initialize to an unknown starting coverage if per-vertex coverage is specified. michael@0: if (this->hasCoverageVertexAttribute()) { michael@0: validComponentFlags = 0; michael@0: } else { michael@0: coverage = fCommon.fCoverage; michael@0: validComponentFlags = kRGBA_GrColorComponentFlags; michael@0: } michael@0: michael@0: // Run through the coverage stages and see if the coverage will be all ones at the end. michael@0: for (int s = 0; s < fCoverageStages.count(); ++s) { michael@0: const GrEffectRef* effect = fCoverageStages[s].getEffect(); michael@0: (*effect)->getConstantColorComponents(&coverage, &validComponentFlags); michael@0: } michael@0: return (kRGBA_GrColorComponentFlags == validComponentFlags) && (0xffffffff == coverage); michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: // Some blend modes allow folding a fractional coverage value into the color's alpha channel, while michael@0: // others will blend incorrectly. michael@0: bool GrDrawState::canTweakAlphaForCoverage() const { michael@0: /* michael@0: The fractional coverage is f. michael@0: The src and dst coeffs are Cs and Cd. michael@0: The dst and src colors are S and D. michael@0: We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D. By tweaking the source color's alpha michael@0: we're replacing S with S'=fS. It's obvious that that first term will always be ok. The second michael@0: term can be rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities for Cd we michael@0: find that only 1, ISA, and ISC produce the correct destination when applied to S' and D. michael@0: Also, if we're directly rendering coverage (isCoverageDrawing) then coverage is treated as michael@0: color by definition. michael@0: */ michael@0: return kOne_GrBlendCoeff == fCommon.fDstBlend || michael@0: kISA_GrBlendCoeff == fCommon.fDstBlend || michael@0: kISC_GrBlendCoeff == fCommon.fDstBlend || michael@0: this->isCoverageDrawing(); michael@0: } michael@0: michael@0: GrDrawState::BlendOptFlags GrDrawState::getBlendOpts(bool forceCoverage, michael@0: GrBlendCoeff* srcCoeff, michael@0: GrBlendCoeff* dstCoeff) const { michael@0: michael@0: GrBlendCoeff bogusSrcCoeff, bogusDstCoeff; michael@0: if (NULL == srcCoeff) { michael@0: srcCoeff = &bogusSrcCoeff; michael@0: } michael@0: *srcCoeff = this->getSrcBlendCoeff(); michael@0: michael@0: if (NULL == dstCoeff) { michael@0: dstCoeff = &bogusDstCoeff; michael@0: } michael@0: *dstCoeff = this->getDstBlendCoeff(); michael@0: michael@0: if (this->isColorWriteDisabled()) { michael@0: *srcCoeff = kZero_GrBlendCoeff; michael@0: *dstCoeff = kOne_GrBlendCoeff; michael@0: } michael@0: michael@0: bool srcAIsOne = this->srcAlphaWillBeOne(); michael@0: bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff || michael@0: (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne); michael@0: bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff || michael@0: (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne); michael@0: michael@0: bool covIsZero = !this->isCoverageDrawing() && michael@0: !this->hasCoverageVertexAttribute() && michael@0: 0 == this->getCoverageColor(); michael@0: // When coeffs are (0,1) there is no reason to draw at all, unless michael@0: // stenciling is enabled. Having color writes disabled is effectively michael@0: // (0,1). The same applies when coverage is known to be 0. michael@0: if ((kZero_GrBlendCoeff == *srcCoeff && dstCoeffIsOne) || covIsZero) { michael@0: if (this->getStencil().doesWrite()) { michael@0: return kDisableBlend_BlendOptFlag | michael@0: kEmitCoverage_BlendOptFlag; michael@0: } else { michael@0: return kSkipDraw_BlendOptFlag; michael@0: } michael@0: } michael@0: michael@0: // check for coverage due to constant coverage, per-vertex coverage, or coverage stage michael@0: bool hasCoverage = forceCoverage || michael@0: 0xffffffff != this->getCoverageColor() || michael@0: this->hasCoverageVertexAttribute() || michael@0: fCoverageStages.count() > 0; michael@0: michael@0: // if we don't have coverage we can check whether the dst michael@0: // has to read at all. If not, we'll disable blending. michael@0: if (!hasCoverage) { michael@0: if (dstCoeffIsZero) { michael@0: if (kOne_GrBlendCoeff == *srcCoeff) { michael@0: // if there is no coverage and coeffs are (1,0) then we michael@0: // won't need to read the dst at all, it gets replaced by src michael@0: return kDisableBlend_BlendOptFlag; michael@0: } else if (kZero_GrBlendCoeff == *srcCoeff) { michael@0: // if the op is "clear" then we don't need to emit a color michael@0: // or blend, just write transparent black into the dst. michael@0: *srcCoeff = kOne_GrBlendCoeff; michael@0: *dstCoeff = kZero_GrBlendCoeff; michael@0: return kDisableBlend_BlendOptFlag | kEmitTransBlack_BlendOptFlag; michael@0: } michael@0: } michael@0: } else if (this->isCoverageDrawing()) { michael@0: // we have coverage but we aren't distinguishing it from alpha by request. michael@0: return kCoverageAsAlpha_BlendOptFlag; michael@0: } else { michael@0: // check whether coverage can be safely rolled into alpha michael@0: // of if we can skip color computation and just emit coverage michael@0: if (this->canTweakAlphaForCoverage()) { michael@0: return kCoverageAsAlpha_BlendOptFlag; michael@0: } michael@0: if (dstCoeffIsZero) { michael@0: if (kZero_GrBlendCoeff == *srcCoeff) { michael@0: // the source color is not included in the blend michael@0: // the dst coeff is effectively zero so blend works out to: michael@0: // (c)(0)D + (1-c)D = (1-c)D. michael@0: *dstCoeff = kISA_GrBlendCoeff; michael@0: return kEmitCoverage_BlendOptFlag; michael@0: } else if (srcAIsOne) { michael@0: // the dst coeff is effectively zero so blend works out to: michael@0: // cS + (c)(0)D + (1-c)D = cS + (1-c)D. michael@0: // If Sa is 1 then we can replace Sa with c michael@0: // and set dst coeff to 1-Sa. michael@0: *dstCoeff = kISA_GrBlendCoeff; michael@0: return kCoverageAsAlpha_BlendOptFlag; michael@0: } michael@0: } else if (dstCoeffIsOne) { michael@0: // the dst coeff is effectively one so blend works out to: michael@0: // cS + (c)(1)D + (1-c)D = cS + D. michael@0: *dstCoeff = kOne_GrBlendCoeff; michael@0: return kCoverageAsAlpha_BlendOptFlag; michael@0: } michael@0: } michael@0: if (kOne_GrBlendCoeff == *srcCoeff && michael@0: kZero_GrBlendCoeff == *dstCoeff && michael@0: this->willEffectReadDstColor()) { michael@0: // In this case the shader will fully resolve the color, coverage, and dst and we don't michael@0: // need blending. michael@0: return kDisableBlend_BlendOptFlag; michael@0: } michael@0: return kNone_BlendOpt; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: michael@0: void GrDrawState::AutoViewMatrixRestore::restore() { michael@0: if (NULL != fDrawState) { michael@0: SkDEBUGCODE(--fDrawState->fBlockEffectRemovalCnt;) michael@0: fDrawState->fCommon.fViewMatrix = fViewMatrix; michael@0: SkASSERT(fDrawState->numColorStages() >= fNumColorStages); michael@0: int numCoverageStages = fSavedCoordChanges.count() - fNumColorStages; michael@0: SkASSERT(fDrawState->numCoverageStages() >= numCoverageStages); michael@0: michael@0: int i = 0; michael@0: for (int s = 0; s < fNumColorStages; ++s, ++i) { michael@0: fDrawState->fColorStages[s].restoreCoordChange(fSavedCoordChanges[i]); michael@0: } michael@0: for (int s = 0; s < numCoverageStages; ++s, ++i) { michael@0: fDrawState->fCoverageStages[s].restoreCoordChange(fSavedCoordChanges[i]); michael@0: } michael@0: fDrawState = NULL; michael@0: } michael@0: } michael@0: michael@0: void GrDrawState::AutoViewMatrixRestore::set(GrDrawState* drawState, michael@0: const SkMatrix& preconcatMatrix) { michael@0: this->restore(); michael@0: michael@0: SkASSERT(NULL == fDrawState); michael@0: if (NULL == drawState || preconcatMatrix.isIdentity()) { michael@0: return; michael@0: } michael@0: fDrawState = drawState; michael@0: michael@0: fViewMatrix = drawState->getViewMatrix(); michael@0: drawState->fCommon.fViewMatrix.preConcat(preconcatMatrix); michael@0: michael@0: this->doEffectCoordChanges(preconcatMatrix); michael@0: SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;) michael@0: } michael@0: michael@0: bool GrDrawState::AutoViewMatrixRestore::setIdentity(GrDrawState* drawState) { michael@0: this->restore(); michael@0: michael@0: if (NULL == drawState) { michael@0: return false; michael@0: } michael@0: michael@0: if (drawState->getViewMatrix().isIdentity()) { michael@0: return true; michael@0: } michael@0: michael@0: fViewMatrix = drawState->getViewMatrix(); michael@0: if (0 == drawState->numTotalStages()) { michael@0: drawState->fCommon.fViewMatrix.reset(); michael@0: fDrawState = drawState; michael@0: fNumColorStages = 0; michael@0: fSavedCoordChanges.reset(0); michael@0: SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;) michael@0: return true; michael@0: } else { michael@0: SkMatrix inv; michael@0: if (!fViewMatrix.invert(&inv)) { michael@0: return false; michael@0: } michael@0: drawState->fCommon.fViewMatrix.reset(); michael@0: fDrawState = drawState; michael@0: this->doEffectCoordChanges(inv); michael@0: SkDEBUGCODE(++fDrawState->fBlockEffectRemovalCnt;) michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: void GrDrawState::AutoViewMatrixRestore::doEffectCoordChanges(const SkMatrix& coordChangeMatrix) { michael@0: fSavedCoordChanges.reset(fDrawState->numTotalStages()); michael@0: int i = 0; michael@0: michael@0: fNumColorStages = fDrawState->numColorStages(); michael@0: for (int s = 0; s < fNumColorStages; ++s, ++i) { michael@0: fDrawState->fColorStages[s].saveCoordChange(&fSavedCoordChanges[i]); michael@0: fDrawState->fColorStages[s].localCoordChange(coordChangeMatrix); michael@0: } michael@0: michael@0: int numCoverageStages = fDrawState->numCoverageStages(); michael@0: for (int s = 0; s < numCoverageStages; ++s, ++i) { michael@0: fDrawState->fCoverageStages[s].saveCoordChange(&fSavedCoordChanges[i]); michael@0: fDrawState->fCoverageStages[s].localCoordChange(coordChangeMatrix); michael@0: } michael@0: }