Index: src/gpu/gl/builders/GrGLProgramBuilder.cpp |
diff --git a/src/gpu/gl/builders/GrGLProgramBuilder.cpp b/src/gpu/gl/builders/GrGLProgramBuilder.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f4ee32b48db2579ff4b3ff9259c63d7829020690 |
--- /dev/null |
+++ b/src/gpu/gl/builders/GrGLProgramBuilder.cpp |
@@ -0,0 +1,409 @@ |
+/* |
+ * 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 "GrDrawEffect.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* 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); |
+ } |
+ |
+ 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::kSolidWhite_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)); |
+ |
+ 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().fHasVertexCode && gpu->shouldUseFixedFunctionTexturing()) |
+ , fTexCoordSetCnt(0) |
+ , fProgramID(0) |
+ , fFS(this, desc) |
+ , 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 (NULL != 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(NULL != effectStages[e] && NULL != 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<GrGLuint> 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<GrGLuint>* 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; |
+ } |
+ } |
+} |
+ |
+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::emitCodeAfterEffects() { |
+ fVS.emitCodeAfterEffects(); |
+} |
+ |
+void GrGLFullProgramBuilder::addVarying(GrSLType type, |
+ const char* name, |
+ const char** vsOutName, |
+ const char** fsInName) { |
+ 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); |
+} |
+ |
+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(); |
+} |
+ |
+bool GrGLFullProgramBuilder::compileAndAttachShaders(GrGLuint programId, |
+ SkTDArray<GrGLuint>* 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().fHasVertexCode); |
+ 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(); |
+} |