/* * 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 "gl/GrGLProgram.h" #include "gl/GrGLSLPrettyPrint.h" #include "gl/GrGLUniformHandle.h" #include "GrCoordTransform.h" #include "../GrGpuGL.h" #include "GrGLFragmentShaderBuilder.h" #include "GrGLProgramBuilder.h" #include "GrTexture.h" #include "GrGLVertexShaderBuilder.h" #include "SkRTConf.h" #include "SkTraceEvent.h" namespace { #define GL_CALL(X) GR_GL_CALL(this->gpu()->glInterface(), X) #define GL_CALL_RET(R, X) GR_GL_CALL_RET(this->gpu()->glInterface(), R, X) // number of each input/output type in a single allocation block static const int kVarsPerBlock = 8; // ES2 FS only guarantees mediump and lowp support static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision; } /////////////////////////////////////////////////////////////////////////////////////////////////// bool GrGLProgramBuilder::genProgram(const GrEffectStage* geometryProcessor, const GrEffectStage* colorStages[], const GrEffectStage* coverageStages[]) { const GrGLProgramDesc::KeyHeader& header = this->desc().getHeader(); fFS.emitCodeBeforeEffects(); /////////////////////////////////////////////////////////////////////////// // get the initial color and coverage to feed into the first effect in each effect chain GrGLSLExpr4 inputColor; GrGLSLExpr4 inputCoverage; if (GrGLProgramDesc::kUniform_ColorInput == header.fColorInput) { const char* name; fUniformHandles.fColorUni = this->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, "Color", &name); inputColor = GrGLSLExpr4(name); } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fColorInput) { inputColor = GrGLSLExpr4(1); } if (GrGLProgramDesc::kUniform_ColorInput == header.fCoverageInput) { const char* name; fUniformHandles.fCoverageUni = this->addUniform(GrGLProgramBuilder::kFragment_Visibility, kVec4f_GrSLType, "Coverage", &name); inputCoverage = GrGLSLExpr4(name); } else if (GrGLProgramDesc::kAllOnes_ColorInput == header.fCoverageInput) { inputCoverage = GrGLSLExpr4(1); } this->emitCodeBeforeEffects(&inputColor, &inputCoverage); /////////////////////////////////////////////////////////////////////////// // emit the per-effect code for both color and coverage effects GrGLProgramDesc::EffectKeyProvider colorKeyProvider( &this->desc(), GrGLProgramDesc::EffectKeyProvider::kColor_EffectType); fColorEffects.reset(this->createAndEmitEffects(colorStages, this->desc().numColorEffects(), colorKeyProvider, &inputColor)); this->emitGeometryProcessor(geometryProcessor, &inputCoverage); GrGLProgramDesc::EffectKeyProvider coverageKeyProvider( &this->desc(), GrGLProgramDesc::EffectKeyProvider::kCoverage_EffectType); fCoverageEffects.reset(this->createAndEmitEffects(coverageStages, this->desc().numCoverageEffects(), coverageKeyProvider, &inputCoverage)); this->emitCodeAfterEffects(); fFS.emitCodeAfterEffects(inputColor, inputCoverage); if (!this->finish()) { return false; } return true; } ////////////////////////////////////////////////////////////////////////////// GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu, const GrGLProgramDesc& desc) : fFragOnly(!desc.getHeader().fRequiresVertexShader && gpu->glCaps().pathRenderingSupport() && gpu->glPathRendering()->texturingMode() == GrGLPathRendering::FixedFunction_TexturingMode) , fTexCoordSetCnt(0) , fProgramID(0) , fFS(this, desc) , fSeparableVaryingInfos(kVarsPerBlock) , fDesc(desc) , fGpu(gpu) , fUniforms(kVarsPerBlock) { } void GrGLProgramBuilder::nameVariable(SkString* out, char prefix, const char* name) { if ('\0' == prefix) { *out = name; } else { out->printf("%c%s", prefix, name); } if (fCodeStage.inStageCode()) { if (out->endsWith('_')) { // Names containing "__" are reserved. out->append("x"); } out->appendf("_Stage%d", fCodeStage.stageIndex()); } } GrGLProgramDataManager::UniformHandle GrGLProgramBuilder::addUniformArray(uint32_t visibility, GrSLType type, const char* name, int count, const char** outName) { SkASSERT(name && strlen(name)); SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_Visibility | kFragment_Visibility); SkASSERT(0 == (~kVisibilityMask & visibility)); SkASSERT(0 != visibility); UniformInfo& uni = fUniforms.push_back(); uni.fVariable.setType(type); uni.fVariable.setTypeModifier(GrGLShaderVar::kUniform_TypeModifier); this->nameVariable(uni.fVariable.accessName(), 'u', name); uni.fVariable.setArrayCount(count); uni.fVisibility = visibility; // If it is visible in both the VS and FS, the precision must match. // We declare a default FS precision, but not a default VS. So set the var // to use the default FS precision. if ((kVertex_Visibility | kFragment_Visibility) == visibility) { // the fragment and vertex precisions must match uni.fVariable.setPrecision(kDefaultFragmentPrecision); } if (outName) { *outName = uni.fVariable.c_str(); } return GrGLProgramDataManager::UniformHandle::CreateFromUniformIndex(fUniforms.count() - 1); } void GrGLProgramBuilder::appendDecls(const VarArray& vars, SkString* out) const { for (int i = 0; i < vars.count(); ++i) { vars[i].appendDecl(this->ctxInfo(), out); out->append(";\n"); } } void GrGLProgramBuilder::appendUniformDecls(ShaderVisibility visibility, SkString* out) const { for (int i = 0; i < fUniforms.count(); ++i) { if (fUniforms[i].fVisibility & visibility) { fUniforms[i].fVariable.appendDecl(this->ctxInfo(), out); out->append(";\n"); } } } void GrGLProgramBuilder::createAndEmitEffects(GrGLProgramEffectsBuilder* programEffectsBuilder, const GrEffectStage* effectStages[], int effectCnt, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* fsInOutColor) { bool effectEmitted = false; GrGLSLExpr4 inColor = *fsInOutColor; GrGLSLExpr4 outColor; for (int e = 0; e < effectCnt; ++e) { SkASSERT(effectStages[e] && effectStages[e]->getEffect()); const GrEffectStage& stage = *effectStages[e]; CodeStage::AutoStageRestore csar(&fCodeStage, &stage); if (inColor.isZeros()) { SkString inColorName; // Effects have no way to communicate zeros, they treat an empty string as ones. this->nameVariable(&inColorName, '\0', "input"); fFS.codeAppendf("\tvec4 %s = %s;\n", inColorName.c_str(), inColor.c_str()); inColor = inColorName; } // create var to hold stage result SkString outColorName; this->nameVariable(&outColorName, '\0', "output"); fFS.codeAppendf("\tvec4 %s;\n", outColorName.c_str()); outColor = outColorName; programEffectsBuilder->emitEffect(stage, keyProvider.get(e), outColor.c_str(), inColor.isOnes() ? NULL : inColor.c_str(), fCodeStage.stageIndex()); inColor = outColor; effectEmitted = true; } if (effectEmitted) { *fsInOutColor = outColor; } } bool GrGLProgramBuilder::finish() { SkASSERT(0 == fProgramID); GL_CALL_RET(fProgramID, CreateProgram()); if (!fProgramID) { return false; } SkTDArray shadersToDelete; if (!this->compileAndAttachShaders(fProgramID, &shadersToDelete)) { GL_CALL(DeleteProgram(fProgramID)); return false; } this->bindProgramLocations(fProgramID); GL_CALL(LinkProgram(fProgramID)); // Calling GetProgramiv is expensive in Chromium. Assume success in release builds. bool checkLinked = !fGpu->ctxInfo().isChromium(); #ifdef SK_DEBUG checkLinked = true; #endif if (checkLinked) { GrGLint linked = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked)); if (!linked) { GrGLint infoLen = GR_GL_INIT_ZERO; GL_CALL(GetProgramiv(fProgramID, GR_GL_INFO_LOG_LENGTH, &infoLen)); SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger if (infoLen > 0) { // retrieve length even though we don't need it to workaround // bug in chrome cmd buffer param validation. GrGLsizei length = GR_GL_INIT_ZERO; GL_CALL(GetProgramInfoLog(fProgramID, infoLen+1, &length, (char*)log.get())); GrPrintf((char*)log.get()); } SkDEBUGFAIL("Error linking program"); GL_CALL(DeleteProgram(fProgramID)); fProgramID = 0; return false; } } this->resolveProgramLocations(fProgramID); for (int i = 0; i < shadersToDelete.count(); ++i) { GL_CALL(DeleteShader(shadersToDelete[i])); } return true; } bool GrGLProgramBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray* shaderIds) const { return fFS.compileAndAttachShaders(programId, shaderIds); } void GrGLProgramBuilder::bindProgramLocations(GrGLuint programId) { fFS.bindProgramLocations(programId); // skbug.com/2056 bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL; if (usingBindUniform) { int count = fUniforms.count(); for (int i = 0; i < count; ++i) { GL_CALL(BindUniformLocation(programId, i, fUniforms[i].fVariable.c_str())); fUniforms[i].fLocation = i; } } } void GrGLProgramBuilder::resolveProgramLocations(GrGLuint programId) { bool usingBindUniform = fGpu->glInterface()->fFunctions.fBindUniformLocation != NULL; if (!usingBindUniform) { int count = fUniforms.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetUniformLocation(programId, fUniforms[i].fVariable.c_str())); fUniforms[i].fLocation = location; } } int count = fSeparableVaryingInfos.count(); for (int i = 0; i < count; ++i) { GrGLint location; GL_CALL_RET(location, GetProgramResourceLocation(programId, GR_GL_FRAGMENT_INPUT, fSeparableVaryingInfos[i].fVariable.c_str())); fSeparableVaryingInfos[i].fLocation = location; } } const GrGLContextInfo& GrGLProgramBuilder::ctxInfo() const { return fGpu->ctxInfo(); } //////////////////////////////////////////////////////////////////////////////// GrGLFullProgramBuilder::GrGLFullProgramBuilder(GrGpuGL* gpu, const GrGLProgramDesc& desc) : INHERITED(gpu, desc) , fGS(this) , fVS(this) { } void GrGLFullProgramBuilder::emitCodeBeforeEffects(GrGLSLExpr4* color, GrGLSLExpr4* coverage) { fVS.emitCodeBeforeEffects(color, coverage); } void GrGLFullProgramBuilder::emitGeometryProcessor(const GrEffectStage* geometryProcessor, GrGLSLExpr4* coverage) { if (geometryProcessor) { GrGLProgramDesc::EffectKeyProvider geometryProcessorKeyProvider( &this->desc(), GrGLProgramDesc::EffectKeyProvider::kGeometryProcessor_EffectType); fGeometryProcessor.reset(this->createAndEmitEffect( geometryProcessor, geometryProcessorKeyProvider, coverage)); } } void GrGLFullProgramBuilder::emitCodeAfterEffects() { fVS.emitCodeAfterEffects(); } void GrGLFullProgramBuilder::addVarying(GrSLType type, const char* name, const char** vsOutName, const char** fsInName, GrGLShaderVar::Precision fsPrecision) { fVS.addVarying(type, name, vsOutName); SkString* fsInputName = fVS.fOutputs.back().accessName(); #if GR_GL_EXPERIMENTAL_GS if (desc().getHeader().fExperimentalGS) { // TODO let the caller use these names fGS.addVarying(type, fsInputName->c_str(), NULL); fsInputName = fGS.fOutputs.back().accessName(); } #endif fFS.addVarying(type, fsInputName->c_str(), fsInName, fsPrecision); } GrGLFullProgramBuilder::VaryingHandle GrGLFullProgramBuilder::addSeparableVarying(GrSLType type, const char* name, const char** vsOutName, const char** fsInName) { addVarying(type, name, vsOutName, fsInName); SeparableVaryingInfo& varying = fSeparableVaryingInfos.push_back(); varying.fVariable = fFS.fInputs.back(); return VaryingHandle::CreateFromSeparableVaryingIndex(fSeparableVaryingInfos.count() - 1); } GrGLProgramEffects* GrGLFullProgramBuilder::createAndEmitEffects( const GrEffectStage* effectStages[], int effectCnt, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* inOutFSColor) { GrGLVertexProgramEffectsBuilder programEffectsBuilder(this, effectCnt); this->INHERITED::createAndEmitEffects(&programEffectsBuilder, effectStages, effectCnt, keyProvider, inOutFSColor); return programEffectsBuilder.finish(); } void GrGLFullProgramBuilder::createAndEmitEffect(GrGLProgramEffectsBuilder* programEffectsBuilder, const GrEffectStage* effectStages, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* fsInOutColor) { GrGLSLExpr4 inColor = *fsInOutColor; GrGLSLExpr4 outColor; SkASSERT(effectStages && effectStages->getEffect()); const GrEffectStage& stage = *effectStages; // Using scope to force ASR destructor to be triggered { CodeStage::AutoStageRestore csar(&fCodeStage, &stage); if (inColor.isZeros()) { SkString inColorName; // Effects have no way to communicate zeros, they treat an empty string as ones. this->nameVariable(&inColorName, '\0', "input"); fFS.codeAppendf("vec4 %s = %s;", inColorName.c_str(), inColor.c_str()); inColor = inColorName; } // create var to hold stage result SkString outColorName; this->nameVariable(&outColorName, '\0', "output"); fFS.codeAppendf("vec4 %s;", outColorName.c_str()); outColor = outColorName; programEffectsBuilder->emitEffect(stage, keyProvider.get(0), outColor.c_str(), inColor.isOnes() ? NULL : inColor.c_str(), fCodeStage.stageIndex()); } *fsInOutColor = outColor; } GrGLProgramEffects* GrGLFullProgramBuilder::createAndEmitEffect( const GrEffectStage* geometryProcessor, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* inOutFSColor) { GrGLVertexProgramEffectsBuilder programEffectsBuilder(this, 1); this->createAndEmitEffect(&programEffectsBuilder, geometryProcessor, keyProvider, inOutFSColor); return programEffectsBuilder.finish(); } bool GrGLFullProgramBuilder::compileAndAttachShaders(GrGLuint programId, SkTDArray* shaderIds) const { return INHERITED::compileAndAttachShaders(programId, shaderIds) && fVS.compileAndAttachShaders(programId, shaderIds) #if GR_GL_EXPERIMENTAL_GS && (!desc().getHeader().fExperimentalGS || fGS.compileAndAttachShaders(programId, shaderIds)) #endif ; } void GrGLFullProgramBuilder::bindProgramLocations(GrGLuint programId) { fVS.bindProgramLocations(programId); INHERITED::bindProgramLocations(programId); } //////////////////////////////////////////////////////////////////////////////// GrGLFragmentOnlyProgramBuilder::GrGLFragmentOnlyProgramBuilder(GrGpuGL* gpu, const GrGLProgramDesc& desc) : INHERITED(gpu, desc) { SkASSERT(!desc.getHeader().fRequiresVertexShader); SkASSERT(gpu->glCaps().pathRenderingSupport()); SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fColorInput); SkASSERT(GrGLProgramDesc::kAttribute_ColorInput != desc.getHeader().fCoverageInput); } int GrGLFragmentOnlyProgramBuilder::addTexCoordSets(int count) { int firstFreeCoordSet = fTexCoordSetCnt; fTexCoordSetCnt += count; SkASSERT(gpu()->glCaps().maxFixedFunctionTextureCoords() >= fTexCoordSetCnt); return firstFreeCoordSet; } GrGLProgramEffects* GrGLFragmentOnlyProgramBuilder::createAndEmitEffects( const GrEffectStage* effectStages[], int effectCnt, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* inOutFSColor) { GrGLPathTexGenProgramEffectsBuilder pathTexGenEffectsBuilder(this, effectCnt); this->INHERITED::createAndEmitEffects(&pathTexGenEffectsBuilder, effectStages, effectCnt, keyProvider, inOutFSColor); return pathTexGenEffectsBuilder.finish(); }