michael@0: /* michael@0: * Copyright 2013 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 "GrGLProgramDesc.h" michael@0: #include "GrBackendEffectFactory.h" michael@0: #include "GrDrawEffect.h" michael@0: #include "GrEffect.h" michael@0: #include "GrGLShaderBuilder.h" michael@0: #include "GrGpuGL.h" michael@0: michael@0: #include "SkChecksum.h" michael@0: michael@0: namespace { michael@0: inline GrGLEffect::EffectKey get_key_and_update_stats(const GrEffectStage& stage, michael@0: const GrGLCaps& caps, michael@0: bool useExplicitLocalCoords, michael@0: bool* setTrueIfReadsDst, michael@0: bool* setTrueIfReadsPos, michael@0: bool* setTrueIfHasVertexCode) { michael@0: const GrEffectRef& effect = *stage.getEffect(); michael@0: const GrBackendEffectFactory& factory = effect->getFactory(); michael@0: GrDrawEffect drawEffect(stage, useExplicitLocalCoords); michael@0: if (effect->willReadDstColor()) { michael@0: *setTrueIfReadsDst = true; michael@0: } michael@0: if (effect->willReadFragmentPosition()) { michael@0: *setTrueIfReadsPos = true; michael@0: } michael@0: if (effect->hasVertexCode()) { michael@0: *setTrueIfHasVertexCode = true; michael@0: } michael@0: return factory.glEffectKey(drawEffect, caps); michael@0: } michael@0: } michael@0: void GrGLProgramDesc::Build(const GrDrawState& drawState, michael@0: bool isPoints, michael@0: GrDrawState::BlendOptFlags blendOpts, michael@0: GrBlendCoeff srcCoeff, michael@0: GrBlendCoeff dstCoeff, michael@0: const GrGpuGL* gpu, michael@0: const GrDeviceCoordTexture* dstCopy, michael@0: SkTArray* colorStages, michael@0: SkTArray* coverageStages, michael@0: GrGLProgramDesc* desc) { michael@0: colorStages->reset(); michael@0: coverageStages->reset(); michael@0: michael@0: // This should already have been caught michael@0: SkASSERT(!(GrDrawState::kSkipDraw_BlendOptFlag & blendOpts)); michael@0: michael@0: bool skipCoverage = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); michael@0: michael@0: bool skipColor = SkToBool(blendOpts & (GrDrawState::kEmitTransBlack_BlendOptFlag | michael@0: GrDrawState::kEmitCoverage_BlendOptFlag)); michael@0: int firstEffectiveColorStage = 0; michael@0: bool inputColorIsUsed = true; michael@0: if (!skipColor) { michael@0: firstEffectiveColorStage = drawState.numColorStages(); michael@0: while (firstEffectiveColorStage > 0 && inputColorIsUsed) { michael@0: --firstEffectiveColorStage; michael@0: const GrEffect* effect = drawState.getColorStage(firstEffectiveColorStage).getEffect()->get(); michael@0: inputColorIsUsed = effect->willUseInputColor(); michael@0: } michael@0: } michael@0: michael@0: int firstEffectiveCoverageStage = 0; michael@0: bool inputCoverageIsUsed = true; michael@0: if (!skipCoverage) { michael@0: firstEffectiveCoverageStage = drawState.numCoverageStages(); michael@0: while (firstEffectiveCoverageStage > 0 && inputCoverageIsUsed) { michael@0: --firstEffectiveCoverageStage; michael@0: const GrEffect* effect = drawState.getCoverageStage(firstEffectiveCoverageStage).getEffect()->get(); michael@0: inputCoverageIsUsed = effect->willUseInputColor(); michael@0: } michael@0: } michael@0: michael@0: // The descriptor is used as a cache key. Thus when a field of the michael@0: // descriptor will not affect program generation (because of the attribute michael@0: // bindings in use or other descriptor field settings) it should be set michael@0: // to a canonical value to avoid duplicate programs with different keys. michael@0: michael@0: bool requiresColorAttrib = !skipColor && drawState.hasColorVertexAttribute(); michael@0: bool requiresCoverageAttrib = !skipCoverage && drawState.hasCoverageVertexAttribute(); michael@0: // we only need the local coords if we're actually going to generate effect code michael@0: bool requiresLocalCoordAttrib = !(skipCoverage && skipColor) && michael@0: drawState.hasLocalCoordAttribute(); michael@0: michael@0: bool colorIsTransBlack = SkToBool(blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag); michael@0: bool colorIsSolidWhite = (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) || michael@0: (!requiresColorAttrib && 0xffffffff == drawState.getColor()) || michael@0: (!inputColorIsUsed); michael@0: michael@0: int numEffects = (skipColor ? 0 : (drawState.numColorStages() - firstEffectiveColorStage)) + michael@0: (skipCoverage ? 0 : (drawState.numCoverageStages() - firstEffectiveCoverageStage)); michael@0: michael@0: size_t newKeyLength = KeyLength(numEffects); michael@0: bool allocChanged; michael@0: desc->fKey.reset(newKeyLength, SkAutoMalloc::kAlloc_OnShrink, &allocChanged); michael@0: if (allocChanged || !desc->fInitialized) { michael@0: // make sure any padding in the header is zero if we we haven't used this allocation before. michael@0: memset(desc->header(), 0, kHeaderSize); michael@0: } michael@0: // write the key length michael@0: *desc->atOffset() = SkToU32(newKeyLength); michael@0: michael@0: KeyHeader* header = desc->header(); michael@0: EffectKey* effectKeys = desc->effectKeys(); michael@0: michael@0: int currEffectKey = 0; michael@0: bool readsDst = false; michael@0: bool readFragPosition = false; michael@0: bool hasVertexCode = false; michael@0: if (!skipColor) { michael@0: for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { michael@0: effectKeys[currEffectKey++] = michael@0: get_key_and_update_stats(drawState.getColorStage(s), gpu->glCaps(), michael@0: requiresLocalCoordAttrib, &readsDst, &readFragPosition, michael@0: &hasVertexCode); michael@0: } michael@0: } michael@0: if (!skipCoverage) { michael@0: for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { michael@0: effectKeys[currEffectKey++] = michael@0: get_key_and_update_stats(drawState.getCoverageStage(s), gpu->glCaps(), michael@0: requiresLocalCoordAttrib, &readsDst, &readFragPosition, michael@0: &hasVertexCode); michael@0: } michael@0: } michael@0: michael@0: header->fHasVertexCode = hasVertexCode || requiresLocalCoordAttrib; michael@0: header->fEmitsPointSize = isPoints; michael@0: michael@0: // Currently the experimental GS will only work with triangle prims (and it doesn't do anything michael@0: // other than pass through values from the VS to the FS anyway). michael@0: #if GR_GL_EXPERIMENTAL_GS michael@0: #if 0 michael@0: header->fExperimentalGS = gpu->caps().geometryShaderSupport(); michael@0: #else michael@0: header->fExperimentalGS = false; michael@0: #endif michael@0: #endif michael@0: bool defaultToUniformInputs = GR_GL_NO_CONSTANT_ATTRIBUTES || gpu->caps()->pathRenderingSupport(); michael@0: michael@0: if (colorIsTransBlack) { michael@0: header->fColorInput = kTransBlack_ColorInput; michael@0: } else if (colorIsSolidWhite) { michael@0: header->fColorInput = kSolidWhite_ColorInput; michael@0: } else if (defaultToUniformInputs && !requiresColorAttrib) { michael@0: header->fColorInput = kUniform_ColorInput; michael@0: } else { michael@0: header->fColorInput = kAttribute_ColorInput; michael@0: header->fHasVertexCode = true; michael@0: } michael@0: michael@0: bool covIsSolidWhite = !requiresCoverageAttrib && 0xffffffff == drawState.getCoverageColor(); michael@0: michael@0: if (skipCoverage) { michael@0: header->fCoverageInput = kTransBlack_ColorInput; michael@0: } else if (covIsSolidWhite || !inputCoverageIsUsed) { michael@0: header->fCoverageInput = kSolidWhite_ColorInput; michael@0: } else if (defaultToUniformInputs && !requiresCoverageAttrib) { michael@0: header->fCoverageInput = kUniform_ColorInput; michael@0: } else { michael@0: header->fCoverageInput = kAttribute_ColorInput; michael@0: header->fHasVertexCode = true; michael@0: } michael@0: michael@0: if (readsDst) { michael@0: SkASSERT(NULL != dstCopy || gpu->caps()->dstReadInShaderSupport()); michael@0: const GrTexture* dstCopyTexture = NULL; michael@0: if (NULL != dstCopy) { michael@0: dstCopyTexture = dstCopy->texture(); michael@0: } michael@0: header->fDstReadKey = GrGLShaderBuilder::KeyForDstRead(dstCopyTexture, gpu->glCaps()); michael@0: SkASSERT(0 != header->fDstReadKey); michael@0: } else { michael@0: header->fDstReadKey = 0; michael@0: } michael@0: michael@0: if (readFragPosition) { michael@0: header->fFragPosKey = GrGLShaderBuilder::KeyForFragmentPosition(drawState.getRenderTarget(), michael@0: gpu->glCaps()); michael@0: } else { michael@0: header->fFragPosKey = 0; michael@0: } michael@0: michael@0: // Record attribute indices michael@0: header->fPositionAttributeIndex = drawState.positionAttributeIndex(); michael@0: header->fLocalCoordAttributeIndex = drawState.localCoordAttributeIndex(); michael@0: michael@0: // For constant color and coverage we need an attribute with an index beyond those already set michael@0: int availableAttributeIndex = drawState.getVertexAttribCount(); michael@0: if (requiresColorAttrib) { michael@0: header->fColorAttributeIndex = drawState.colorVertexAttributeIndex(); michael@0: } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fColorInput) { michael@0: SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); michael@0: header->fColorAttributeIndex = availableAttributeIndex; michael@0: availableAttributeIndex++; michael@0: } else { michael@0: header->fColorAttributeIndex = -1; michael@0: } michael@0: michael@0: if (requiresCoverageAttrib) { michael@0: header->fCoverageAttributeIndex = drawState.coverageVertexAttributeIndex(); michael@0: } else if (GrGLProgramDesc::kAttribute_ColorInput == header->fCoverageInput) { michael@0: SkASSERT(availableAttributeIndex < GrDrawState::kMaxVertexAttribCnt); michael@0: header->fCoverageAttributeIndex = availableAttributeIndex; michael@0: } else { michael@0: header->fCoverageAttributeIndex = -1; michael@0: } michael@0: michael@0: // Here we deal with whether/how we handle color and coverage separately. michael@0: michael@0: // Set this default and then possibly change our mind if there is coverage. michael@0: header->fCoverageOutput = kModulate_CoverageOutput; michael@0: michael@0: // If we do have coverage determine whether it matters. michael@0: bool separateCoverageFromColor = false; michael@0: if (!drawState.isCoverageDrawing() && !skipCoverage && michael@0: (drawState.numCoverageStages() > 0 || requiresCoverageAttrib)) { michael@0: michael@0: if (gpu->caps()->dualSourceBlendingSupport() && michael@0: !(blendOpts & (GrDrawState::kEmitCoverage_BlendOptFlag | michael@0: GrDrawState::kCoverageAsAlpha_BlendOptFlag))) { michael@0: if (kZero_GrBlendCoeff == dstCoeff) { michael@0: // write the coverage value to second color michael@0: header->fCoverageOutput = kSecondaryCoverage_CoverageOutput; michael@0: separateCoverageFromColor = true; michael@0: } else if (kSA_GrBlendCoeff == dstCoeff) { michael@0: // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. michael@0: header->fCoverageOutput = kSecondaryCoverageISA_CoverageOutput; michael@0: separateCoverageFromColor = true; michael@0: } else if (kSC_GrBlendCoeff == dstCoeff) { michael@0: // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered. michael@0: header->fCoverageOutput = kSecondaryCoverageISC_CoverageOutput; michael@0: separateCoverageFromColor = true; michael@0: } michael@0: } else if (readsDst && michael@0: kOne_GrBlendCoeff == srcCoeff && michael@0: kZero_GrBlendCoeff == dstCoeff) { michael@0: header->fCoverageOutput = kCombineWithDst_CoverageOutput; michael@0: separateCoverageFromColor = true; michael@0: } michael@0: } michael@0: if (!skipColor) { michael@0: for (int s = firstEffectiveColorStage; s < drawState.numColorStages(); ++s) { michael@0: colorStages->push_back(&drawState.getColorStage(s)); michael@0: } michael@0: } michael@0: if (!skipCoverage) { michael@0: SkTArray* array; michael@0: if (separateCoverageFromColor) { michael@0: array = coverageStages; michael@0: } else { michael@0: array = colorStages; michael@0: } michael@0: for (int s = firstEffectiveCoverageStage; s < drawState.numCoverageStages(); ++s) { michael@0: array->push_back(&drawState.getCoverageStage(s)); michael@0: } michael@0: } michael@0: header->fColorEffectCnt = colorStages->count(); michael@0: header->fCoverageEffectCnt = coverageStages->count(); michael@0: michael@0: *desc->checksum() = 0; michael@0: *desc->checksum() = SkChecksum::Compute(reinterpret_cast(desc->fKey.get()), michael@0: newKeyLength); michael@0: desc->fInitialized = true; michael@0: } michael@0: michael@0: GrGLProgramDesc& GrGLProgramDesc::operator= (const GrGLProgramDesc& other) { michael@0: fInitialized = other.fInitialized; michael@0: if (fInitialized) { michael@0: size_t keyLength = other.keyLength(); michael@0: fKey.reset(keyLength); michael@0: memcpy(fKey.get(), other.fKey.get(), keyLength); michael@0: } michael@0: return *this; michael@0: }