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