/* * Copyright 2011 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "GrGLProgram.h" #include "GrAllocator.h" #include "GrEffect.h" #include "GrCoordTransform.h" #include "GrDrawEffect.h" #include "GrGLEffect.h" #include "GrGpuGL.h" #include "GrGLShaderVar.h" #include "GrGLSL.h" #include "SkXfermode.h" SK_DEFINE_INST_COUNT(GrGLProgram) #define GL_CALL(X) GR_GL_CALL(fGpu->glInterface(), X) #define GL_CALL_RET(R, X) GR_GL_CALL_RET(fGpu->glInterface(), R, X) GrGLProgram* GrGLProgram::Create(GrGpuGL* gpu, const GrGLProgramDesc& desc, const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]) { GrGLProgram* program = SkNEW_ARGS(GrGLProgram, (gpu, desc, colorStages, coverageStages)); if (!program->succeeded()) { delete program; program = NULL; } return program; } GrGLProgram::GrGLProgram(GrGpuGL* gpu, const GrGLProgramDesc& desc, const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]) : fGpu(gpu) , fUniformManager(gpu) , fHasVertexShader(false) , fNumTexCoordSets(0) { fDesc = desc; fProgramID = 0; fDstCopyTexUnit = -1; fColor = GrColor_ILLEGAL; if (fDesc.getHeader().fHasVertexCode || !fGpu->shouldUseFixedFunctionTexturing()) { GrGLFullShaderBuilder fullBuilder(fGpu, fUniformManager, fDesc); if (this->genProgram(&fullBuilder, colorStages, coverageStages)) { fUniformHandles.fViewMatrixUni = fullBuilder.getViewMatrixUniform(); fHasVertexShader = true; } } else { GrGLFragmentOnlyShaderBuilder fragmentOnlyBuilder(fGpu, fUniformManager, fDesc); if (this->genProgram(&fragmentOnlyBuilder, colorStages, coverageStages)) { fNumTexCoordSets = fragmentOnlyBuilder.getNumTexCoordSets(); } } } GrGLProgram::~GrGLProgram() { if (fProgramID) { GL_CALL(DeleteProgram(fProgramID)); } } void GrGLProgram::abandon() { fProgramID = 0; } void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const { switch (fDesc.getHeader().fCoverageOutput) { case GrGLProgramDesc::kModulate_CoverageOutput: break; // The prog will write a coverage value to the secondary // output and the dst is blended by one minus that value. case GrGLProgramDesc::kSecondaryCoverage_CoverageOutput: case GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput: case GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput: *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff; break; case GrGLProgramDesc::kCombineWithDst_CoverageOutput: // We should only have set this if the blend was specified as (1, 0) SkASSERT(kOne_GrBlendCoeff == *srcCoeff && kZero_GrBlendCoeff == *dstCoeff); break; default: GrCrash("Unexpected coverage output"); break; } } bool GrGLProgram::genProgram(GrGLShaderBuilder* builder, const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]) { SkASSERT(0 == fProgramID); const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); // incoming color to current stage being processed. GrGLSLExpr4 inColor = builder->getInputColor(); fColorEffects.reset( builder->createAndEmitEffects(colorStages, fDesc.effectKeys(), fDesc.numColorEffects(), &inColor)); /////////////////////////////////////////////////////////////////////////// // compute the partial coverage GrGLSLExpr4 inCoverage = builder->getInputCoverage(); fCoverageEffects.reset( builder->createAndEmitEffects(coverageStages, fDesc.getEffectKeys() + fDesc.numColorEffects(), fDesc.numCoverageEffects(), &inCoverage)); // discard if coverage is zero if (header.fDiscardIfZeroCoverage && !inCoverage.isOnes()) { if (inCoverage.isZeros()) { // This is unfortunate. builder->fsCodeAppend("\tdiscard;\n"); } else { builder->fsCodeAppendf("\tif (all(lessThanEqual(%s, vec4(0.0)))) {\n\t\tdiscard;\n\t}\n", inCoverage.c_str()); } } if (GrGLProgramDesc::CoverageOutputUsesSecondaryOutput(header.fCoverageOutput)) { const char* secondaryOutputName = builder->enableSecondaryOutput(); // default coeff to ones for kCoverage_DualSrcOutput GrGLSLExpr4 coeff(1); if (GrGLProgramDesc::kSecondaryCoverageISA_CoverageOutput == header.fCoverageOutput) { // Get (1-A) into coeff coeff = GrGLSLExpr4::VectorCast(GrGLSLExpr1(1) - inColor.a()); } else if (GrGLProgramDesc::kSecondaryCoverageISC_CoverageOutput == header.fCoverageOutput) { // Get (1-RGBA) into coeff coeff = GrGLSLExpr4(1) - inColor; } // Get coeff * coverage into modulate and then write that to the dual source output. builder->fsCodeAppendf("\t%s = %s;\n", secondaryOutputName, (coeff * inCoverage).c_str()); } /////////////////////////////////////////////////////////////////////////// // combine color and coverage as frag color // Get "color * coverage" into fragColor GrGLSLExpr4 fragColor = inColor * inCoverage; // Now tack on "+(1-coverage)dst onto the frag color if we were asked to do so. if (GrGLProgramDesc::kCombineWithDst_CoverageOutput == header.fCoverageOutput) { GrGLSLExpr4 dstCoeff = GrGLSLExpr4(1) - inCoverage; GrGLSLExpr4 dstContribution = dstCoeff * GrGLSLExpr4(builder->dstColor()); fragColor = fragColor + dstContribution; } builder->fsCodeAppendf("\t%s = %s;\n", builder->getColorOutputName(), fragColor.c_str()); if (!builder->finish(&fProgramID)) { return false; } fUniformHandles.fRTHeightUni = builder->getRTHeightUniform(); fUniformHandles.fDstCopyTopLeftUni = builder->getDstCopyTopLeftUniform(); fUniformHandles.fDstCopyScaleUni = builder->getDstCopyScaleUniform(); fUniformHandles.fColorUni = builder->getColorUniform(); fUniformHandles.fCoverageUni = builder->getCoverageUniform(); fUniformHandles.fDstCopySamplerUni = builder->getDstCopySamplerUniform(); // This must be called after we set fDstCopySamplerUni above. this->initSamplerUniforms(); return true; } void GrGLProgram::initSamplerUniforms() { GL_CALL(UseProgram(fProgramID)); GrGLint texUnitIdx = 0; if (fUniformHandles.fDstCopySamplerUni.isValid()) { fUniformManager.setSampler(fUniformHandles.fDstCopySamplerUni, texUnitIdx); fDstCopyTexUnit = texUnitIdx++; } fColorEffects->initSamplers(fUniformManager, &texUnitIdx); fCoverageEffects->initSamplers(fUniformManager, &texUnitIdx); } /////////////////////////////////////////////////////////////////////////////// void GrGLProgram::setData(GrDrawState::BlendOptFlags blendOpts, const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[], const GrDeviceCoordTexture* dstCopy, SharedGLState* sharedState) { const GrDrawState& drawState = fGpu->getDrawState(); GrColor color; GrColor coverage; if (blendOpts & GrDrawState::kEmitTransBlack_BlendOptFlag) { color = 0; coverage = 0; } else if (blendOpts & GrDrawState::kEmitCoverage_BlendOptFlag) { color = 0xffffffff; coverage = drawState.getCoverage(); } else { color = drawState.getColor(); coverage = drawState.getCoverage(); } this->setColor(drawState, color, sharedState); this->setCoverage(drawState, coverage, sharedState); this->setMatrixAndRenderTargetHeight(drawState); if (NULL != dstCopy) { if (fUniformHandles.fDstCopyTopLeftUni.isValid()) { fUniformManager.set2f(fUniformHandles.fDstCopyTopLeftUni, static_cast(dstCopy->offset().fX), static_cast(dstCopy->offset().fY)); fUniformManager.set2f(fUniformHandles.fDstCopyScaleUni, 1.f / dstCopy->texture()->width(), 1.f / dstCopy->texture()->height()); GrGLTexture* texture = static_cast(dstCopy->texture()); static GrTextureParams kParams; // the default is clamp, nearest filtering. fGpu->bindTexture(fDstCopyTexUnit, kParams, texture); } else { SkASSERT(!fUniformHandles.fDstCopyScaleUni.isValid()); SkASSERT(!fUniformHandles.fDstCopySamplerUni.isValid()); } } else { SkASSERT(!fUniformHandles.fDstCopyTopLeftUni.isValid()); SkASSERT(!fUniformHandles.fDstCopyScaleUni.isValid()); SkASSERT(!fUniformHandles.fDstCopySamplerUni.isValid()); } fColorEffects->setData(fGpu, fUniformManager, colorStages); fCoverageEffects->setData(fGpu, fUniformManager, coverageStages); // TexGen state applies to the the fixed function vertex shader. For custom shaders, it's // ignored, so we don't need to change the texgen settings in that case. if (!fHasVertexShader) { fGpu->flushTexGenSettings(fNumTexCoordSets); } } void GrGLProgram::setColor(const GrDrawState& drawState, GrColor color, SharedGLState* sharedState) { const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); if (!drawState.hasColorVertexAttribute()) { switch (header.fColorInput) { case GrGLProgramDesc::kAttribute_ColorInput: SkASSERT(-1 != header.fColorAttributeIndex); if (sharedState->fConstAttribColor != color || sharedState->fConstAttribColorIndex != header.fColorAttributeIndex) { // OpenGL ES only supports the float varieties of glVertexAttrib GrGLfloat c[4]; GrColorToRGBAFloat(color, c); GL_CALL(VertexAttrib4fv(header.fColorAttributeIndex, c)); sharedState->fConstAttribColor = color; sharedState->fConstAttribColorIndex = header.fColorAttributeIndex; } break; case GrGLProgramDesc::kUniform_ColorInput: if (fColor != color && fUniformHandles.fColorUni.isValid()) { // OpenGL ES doesn't support unsigned byte varieties of glUniform GrGLfloat c[4]; GrColorToRGBAFloat(color, c); fUniformManager.set4fv(fUniformHandles.fColorUni, 1, c); fColor = color; } sharedState->fConstAttribColorIndex = -1; break; case GrGLProgramDesc::kSolidWhite_ColorInput: case GrGLProgramDesc::kTransBlack_ColorInput: sharedState->fConstAttribColorIndex = -1; break; default: GrCrash("Unknown color type."); } } else { sharedState->fConstAttribColorIndex = -1; } } void GrGLProgram::setCoverage(const GrDrawState& drawState, GrColor coverage, SharedGLState* sharedState) { const GrGLProgramDesc::KeyHeader& header = fDesc.getHeader(); if (!drawState.hasCoverageVertexAttribute()) { switch (header.fCoverageInput) { case GrGLProgramDesc::kAttribute_ColorInput: if (sharedState->fConstAttribCoverage != coverage || sharedState->fConstAttribCoverageIndex != header.fCoverageAttributeIndex) { // OpenGL ES only supports the float varieties of glVertexAttrib GrGLfloat c[4]; GrColorToRGBAFloat(coverage, c); GL_CALL(VertexAttrib4fv(header.fCoverageAttributeIndex, c)); sharedState->fConstAttribCoverage = coverage; sharedState->fConstAttribCoverageIndex = header.fCoverageAttributeIndex; } break; case GrGLProgramDesc::kUniform_ColorInput: if (fCoverage != coverage) { // OpenGL ES doesn't support unsigned byte varieties of glUniform GrGLfloat c[4]; GrColorToRGBAFloat(coverage, c); fUniformManager.set4fv(fUniformHandles.fCoverageUni, 1, c); fCoverage = coverage; } sharedState->fConstAttribCoverageIndex = -1; break; case GrGLProgramDesc::kSolidWhite_ColorInput: case GrGLProgramDesc::kTransBlack_ColorInput: sharedState->fConstAttribCoverageIndex = -1; break; default: GrCrash("Unknown coverage type."); } } else { sharedState->fConstAttribCoverageIndex = -1; } } void GrGLProgram::setMatrixAndRenderTargetHeight(const GrDrawState& drawState) { const GrRenderTarget* rt = drawState.getRenderTarget(); SkISize size; size.set(rt->width(), rt->height()); // Load the RT height uniform if it is needed to y-flip gl_FragCoord. if (fUniformHandles.fRTHeightUni.isValid() && fMatrixState.fRenderTargetSize.fHeight != size.fHeight) { fUniformManager.set1f(fUniformHandles.fRTHeightUni, SkIntToScalar(size.fHeight)); } if (!fHasVertexShader) { SkASSERT(!fUniformHandles.fViewMatrixUni.isValid()); fGpu->setProjectionMatrix(drawState.getViewMatrix(), size, rt->origin()); } else if (fMatrixState.fRenderTargetOrigin != rt->origin() || fMatrixState.fRenderTargetSize != size || !fMatrixState.fViewMatrix.cheapEqualTo(drawState.getViewMatrix())) { SkASSERT(fUniformHandles.fViewMatrixUni.isValid()); fMatrixState.fViewMatrix = drawState.getViewMatrix(); fMatrixState.fRenderTargetSize = size; fMatrixState.fRenderTargetOrigin = rt->origin(); GrGLfloat viewMatrix[3 * 3]; fMatrixState.getGLMatrix<3>(viewMatrix); fUniformManager.setMatrix3f(fUniformHandles.fViewMatrixUni, viewMatrix); } }