blob: 909ac76d62f73a2bd096f6ab12b0a1551c1280e9 [file] [log] [blame]
/*
* 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 GrGLProgramDesc& desc)
: fEffectEmitter(NULL)
, fFragOnly(SkToBool(desc.getHeader().fUseFragShaderOnly))
, fTexCoordSetCnt(0)
, fProgramID(0)
, fFS(this, desc)
, fSeparableVaryingInfos(kVarsPerBlock)
, fGrProcessorEmitter(this)
, 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<GrGLProgramEffects::Sampler, true>& 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<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;
}
}
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();
}