/* * 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 GrGeometryStage* geometryProcessor, const GrFragmentStage* colorStages[], const GrFragmentStage* 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); } // Subclasses drive effect emitting this->createAndEmitEffects(geometryProcessor, colorStages, coverageStages, &inputColor, &inputCoverage); fFS.emitCodeAfterEffects(inputColor, inputCoverage); if (!this->finish()) { return false; } return true; } ////////////////////////////////////////////////////////////////////////////// GrGLProgramBuilder::GrGLProgramBuilder(GrGpuGL* gpu, const GrOptDrawState& optState, const GrGLProgramDesc& desc) : fEffectEmitter(NULL) , fFragOnly(SkToBool(desc.getHeader().fUseFragShaderOnly)) , fTexCoordSetCnt(0) , fProgramID(0) , fFS(this, desc) , fSeparableVaryingInfos(kVarsPerBlock) , fGrProcessorEmitter(this) , fOptState(optState) , 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(const GrFragmentStage* effectStages[], int effectCnt, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* fsInOutColor) { bool effectEmitted = false; GrGLSLExpr4 inColor = *fsInOutColor; GrGLSLExpr4 outColor; for (int e = 0; e < effectCnt; ++e) { fGrProcessorEmitter.set(effectStages[e]->getFragmentProcessor()); fEffectEmitter = &fGrProcessorEmitter; // calls into the subclass to emit the actual effect into the program effect object this->emitEffect(*effectStages[e], e, keyProvider, &inColor, &outColor); effectEmitted = true; } if (effectEmitted) { *fsInOutColor = outColor; } } void GrGLProgramBuilder::emitEffect(const GrProcessorStage& effectStage, int effectIndex, const GrGLProgramDesc::EffectKeyProvider& keyProvider, GrGLSLExpr4* inColor, GrGLSLExpr4* outColor) { SkASSERT(effectStage.getProcessor()); CodeStage::AutoStageRestore csar(&fCodeStage, &effectStage); 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; this->emitEffect(effectStage, keyProvider.get(effectIndex), outColor->c_str(), inColor->isOnes() ? NULL : inColor->c_str(), fCodeStage.stageIndex()); *inColor = *outColor; } void GrGLProgramBuilder::emitSamplers(const GrProcessor& effect, GrGLProcessor::TextureSamplerArray* outSamplers) { SkTArray& samplers = this->getProgramEffects()->addSamplers(); int numTextures = effect.numTextures(); samplers.push_back_n(numTextures); SkString name; for (int t = 0; t < numTextures; ++t) { name.printf("Sampler%d", t); samplers[t].fUniform = this->addUniform(GrGLProgramBuilder::kFragment_Visibility, kSampler2D_GrSLType, name.c_str()); SkNEW_APPEND_TO_TARRAY(outSamplers, GrGLProcessor::TextureSampler, (samplers[t].fUniform, effect.textureAccess(t))); } } 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(); }