blob: d24f2f89f642b1d59eeea5348e5336d68985d4a4 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gl/GrGLShaderBuilder.h"
#include "gl/GrGLProgram.h"
// number of each input/output type in a single allocation block
static const int kVarsPerBlock = 8;
// except FS outputs where we expect 2 at most.
static const int kMaxFSOutputs = 2;
// Architectural assumption: always 2-d input coords.
// Likely to become non-constant and non-static, perhaps even
// varying by stage, if we use 1D textures for gradients!
//const int GrGLShaderBuilder::fCoordDims = 2;
GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctx)
: fVSUnis(kVarsPerBlock)
, fVSAttrs(kVarsPerBlock)
, fVSOutputs(kVarsPerBlock)
, fGSInputs(kVarsPerBlock)
, fGSOutputs(kVarsPerBlock)
, fFSInputs(kVarsPerBlock)
, fFSUnis(kVarsPerBlock)
, fFSOutputs(kMaxFSOutputs)
, fUsesGS(false)
, fVaryingDims(0)
, fComplexCoord(false)
, fContext(ctx) {
}
void GrGLShaderBuilder::computeSwizzle(uint32_t configFlags) {
static const uint32_t kMulByAlphaMask =
(GrGLProgram::StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
GrGLProgram::StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag);
fSwizzle = "";
if (configFlags & GrGLProgram::StageDesc::kSwapRAndB_InConfigFlag) {
GrAssert(!(configFlags &
GrGLProgram::StageDesc::kSmearAlpha_InConfigFlag));
GrAssert(!(configFlags &
GrGLProgram::StageDesc::kSmearRed_InConfigFlag));
fSwizzle = ".bgra";
} else if (configFlags & GrGLProgram::StageDesc::kSmearAlpha_InConfigFlag) {
GrAssert(!(configFlags & kMulByAlphaMask));
GrAssert(!(configFlags &
GrGLProgram::StageDesc::kSmearRed_InConfigFlag));
fSwizzle = ".aaaa";
} else if (configFlags & GrGLProgram::StageDesc::kSmearRed_InConfigFlag) {
GrAssert(!(configFlags & kMulByAlphaMask));
GrAssert(!(configFlags &
GrGLProgram::StageDesc::kSmearAlpha_InConfigFlag));
fSwizzle = ".rrrr";
}
}
void GrGLShaderBuilder::computeModulate(const char* fsInColor) {
if (NULL != fsInColor) {
fModulate.printf(" * %s", fsInColor);
} else {
fModulate.reset();
}
}
void GrGLShaderBuilder::setupTextureAccess(SamplerMode samplerMode,
int stageNum) {
SkString retval;
fTexFunc = "texture2D";
switch (samplerMode) {
case kDefault_SamplerMode:
GrAssert(fVaryingDims == fCoordDims);
// Do nothing
break;
case kProj_SamplerMode:
fTexFunc.append("Proj");
break;
case kExplicitDivide_SamplerMode:
retval = "inCoord";
retval.appendS32(stageNum);
fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
GrGLShaderVar::TypeString
(GrSLFloatVectorType(fCoordDims)),
retval.c_str(),
fSampleCoords.c_str(),
GrGLSLVectorNonhomogCoords(fVaryingDims),
fSampleCoords.c_str(),
GrGLSLVectorHomogCoord(fVaryingDims));
fSampleCoords = retval;
break;
}
fComplexCoord = false;
}
void GrGLShaderBuilder::emitTextureLookup(const char* samplerName,
const char* coordName) {
if (NULL == coordName) {
coordName = fSampleCoords.c_str();
}
fFSCode.appendf("%s(%s, %s)", fTexFunc.c_str(), samplerName, coordName);
}
void GrGLShaderBuilder::emitDefaultFetch(const char* outColor,
const char* samplerName) {
fFSCode.appendf("\t%s = ", outColor);
this->emitTextureLookup(samplerName);
fFSCode.appendf("%s%s;\n", fSwizzle.c_str(), fModulate.c_str());
}
const GrGLShaderVar& GrGLShaderBuilder::addUniform(uint32_t visibility,
GrSLType type,
const char* name,
int stageNum,
int count) {
GrAssert(name && strlen(name));
static const uint32_t kVisibilityMask = kVertex_ShaderType | kFragment_ShaderType;
GrAssert(0 == (~kVisibilityMask & visibility));
GrAssert(0 != visibility);
GrGLShaderVar* var = NULL;
if (kVertex_ShaderType & visibility) {
var = &fVSUnis.push_back();
} else {
GrAssert(kFragment_ShaderType & visibility);
var = &fFSUnis.push_back();
}
var->setType(type);
var->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
var->setName(name);
if (stageNum >= 0) {
var->accessName()->appendS32(stageNum);
}
var->setArrayCount(count);
if ((kVertex_ShaderType | kFragment_ShaderType) == visibility) {
fFSUnis.push_back(*var);
// If it's shared between VS and FS, VS must override
// default highp and specify mediump.
var->setEmitPrecision(true);
}
return *var;
}
void GrGLShaderBuilder::addVarying(GrSLType type,
const char* name,
const char** vsOutName,
const char** fsInName) {
fVSOutputs.push_back();
fVSOutputs.back().setType(type);
fVSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier);
fVSOutputs.back().accessName()->printf("v%s", name);
if (vsOutName) {
*vsOutName = fVSOutputs.back().getName().c_str();
}
// input to FS comes either from VS or GS
const SkString* fsName;
if (fUsesGS) {
// if we have a GS take each varying in as an array
// and output as non-array.
fGSInputs.push_back();
fGSInputs.back().setType(type);
fGSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier);
fGSInputs.back().setUnsizedArray();
*fGSInputs.back().accessName() = fVSOutputs.back().getName();
fGSOutputs.push_back();
fGSOutputs.back().setType(type);
fGSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier);
fGSOutputs.back().accessName()->printf("g%s", name);
fsName = fGSOutputs.back().accessName();
} else {
fsName = fVSOutputs.back().accessName();
}
fFSInputs.push_back();
fFSInputs.back().setType(type);
fFSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier);
fFSInputs.back().setName(*fsName);
if (fsInName) {
*fsInName = fsName->c_str();
}
}
void GrGLShaderBuilder::addVarying(GrSLType type,
const char* name,
int stageNum,
const char** vsOutName,
const char** fsInName) {
SkString nameWithStage(name);
nameWithStage.appendS32(stageNum);
this->addVarying(type, nameWithStage.c_str(), vsOutName, fsInName);
}
namespace {
void append_decls(const GrGLShaderBuilder::VarArray& vars,
const GrGLContextInfo& ctx,
SkString* string) {
for (int i = 0; i < vars.count(); ++i) {
vars[i].appendDecl(ctx, string);
}
}
}
void GrGLShaderBuilder::getShader(ShaderType type, SkString* shaderStr) const {
switch (type) {
case kVertex_ShaderType:
*shaderStr = fHeader;
append_decls(fVSUnis, fContext, shaderStr);
append_decls(fVSAttrs, fContext, shaderStr);
append_decls(fVSOutputs, fContext, shaderStr);
shaderStr->append(fVSCode);
break;
case kGeometry_ShaderType:
if (fUsesGS) {
*shaderStr = fHeader;
shaderStr->append(fGSHeader);
append_decls(fGSInputs, fContext, shaderStr);
append_decls(fGSOutputs, fContext, shaderStr);
shaderStr->append(fGSCode);
} else {
shaderStr->reset();
}
break;
case kFragment_ShaderType:
*shaderStr = fHeader;
shaderStr->append(GrGetGLSLShaderPrecisionDecl(fContext.binding()));
append_decls(fFSUnis, fContext, shaderStr);
append_decls(fFSInputs, fContext, shaderStr);
// We shouldn't have declared outputs on 1.10
GrAssert(k110_GrGLSLGeneration != fContext.glslGeneration() || fFSOutputs.empty());
append_decls(fFSOutputs, fContext, shaderStr);
shaderStr->append(fFSFunctions);
shaderStr->append(fFSCode);
break;
}
}