blob: d0b19907f4c02500c6a4981ef4006528fd733730 [file] [log] [blame]
//
// Copyright (c) 2002-2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "compiler/translator/VariableInfo.h"
namespace {
TString arrayBrackets(int index)
{
TStringStream stream;
stream << "[" << index << "]";
return stream.str();
}
// Returns the data type for an attribute, uniform, or varying.
ShDataType getVariableDataType(const TType& type)
{
switch (type.getBasicType()) {
case EbtFloat:
if (type.isMatrix()) {
switch (type.getCols())
{
case 2:
switch (type.getRows())
{
case 2: return SH_FLOAT_MAT2;
case 3: return SH_FLOAT_MAT2x3;
case 4: return SH_FLOAT_MAT2x4;
default: UNREACHABLE();
}
case 3:
switch (type.getRows())
{
case 2: return SH_FLOAT_MAT3x2;
case 3: return SH_FLOAT_MAT3;
case 4: return SH_FLOAT_MAT3x4;
default: UNREACHABLE();
}
case 4:
switch (type.getRows())
{
case 2: return SH_FLOAT_MAT4x2;
case 3: return SH_FLOAT_MAT4x3;
case 4: return SH_FLOAT_MAT4;
default: UNREACHABLE();
}
}
} else if (type.isVector()) {
switch (type.getNominalSize()) {
case 2: return SH_FLOAT_VEC2;
case 3: return SH_FLOAT_VEC3;
case 4: return SH_FLOAT_VEC4;
default: UNREACHABLE();
}
} else {
return SH_FLOAT;
}
case EbtInt:
if (type.isMatrix()) {
UNREACHABLE();
} else if (type.isVector()) {
switch (type.getNominalSize()) {
case 2: return SH_INT_VEC2;
case 3: return SH_INT_VEC3;
case 4: return SH_INT_VEC4;
default: UNREACHABLE();
}
} else {
return SH_INT;
}
case EbtUInt:
if (type.isMatrix()) {
UNREACHABLE();
} else if (type.isVector()) {
switch (type.getNominalSize()) {
case 2: return SH_UNSIGNED_INT_VEC2;
case 3: return SH_UNSIGNED_INT_VEC3;
case 4: return SH_UNSIGNED_INT_VEC4;
default: UNREACHABLE();
}
} else {
return SH_UNSIGNED_INT;
}
case EbtBool:
if (type.isMatrix()) {
UNREACHABLE();
} else if (type.isVector()) {
switch (type.getNominalSize()) {
case 2: return SH_BOOL_VEC2;
case 3: return SH_BOOL_VEC3;
case 4: return SH_BOOL_VEC4;
default: UNREACHABLE();
}
} else {
return SH_BOOL;
}
case EbtSampler2D: return SH_SAMPLER_2D;
case EbtSampler3D: return SH_SAMPLER_3D;
case EbtSamplerCube: return SH_SAMPLER_CUBE;
case EbtSamplerExternalOES: return SH_SAMPLER_EXTERNAL_OES;
case EbtSampler2DRect: return SH_SAMPLER_2D_RECT_ARB;
case EbtSampler2DArray: return SH_SAMPLER_2D_ARRAY;
case EbtISampler2D: return SH_INT_SAMPLER_2D;
case EbtISampler3D: return SH_INT_SAMPLER_3D;
case EbtISamplerCube: return SH_INT_SAMPLER_CUBE;
case EbtISampler2DArray: return SH_INT_SAMPLER_2D_ARRAY;
case EbtUSampler2D: return SH_UNSIGNED_INT_SAMPLER_2D;
case EbtUSampler3D: return SH_UNSIGNED_INT_SAMPLER_3D;
case EbtUSamplerCube: return SH_UNSIGNED_INT_SAMPLER_CUBE;
case EbtUSampler2DArray: return SH_UNSIGNED_INT_SAMPLER_2D_ARRAY;
case EbtSampler2DShadow: return SH_SAMPLER_2D_SHADOW;
case EbtSamplerCubeShadow: return SH_SAMPLER_CUBE_SHADOW;
case EbtSampler2DArrayShadow: return SH_SAMPLER_2D_ARRAY_SHADOW;
default: UNREACHABLE();
}
return SH_NONE;
}
void getBuiltInVariableInfo(const TType& type,
const TString& name,
const TString& mappedName,
TVariableInfoList& infoList);
void getUserDefinedVariableInfo(const TType& type,
const TString& name,
const TString& mappedName,
TVariableInfoList& infoList,
ShHashFunction64 hashFunction);
// Returns info for an attribute, uniform, or varying.
void getVariableInfo(const TType& type,
const TString& name,
const TString& mappedName,
TVariableInfoList& infoList,
ShHashFunction64 hashFunction)
{
if (type.getBasicType() == EbtStruct || type.isInterfaceBlock()) {
if (type.isArray()) {
for (int i = 0; i < type.getArraySize(); ++i) {
TString lname = name + arrayBrackets(i);
TString lmappedName = mappedName + arrayBrackets(i);
getUserDefinedVariableInfo(type, lname, lmappedName, infoList, hashFunction);
}
} else {
getUserDefinedVariableInfo(type, name, mappedName, infoList, hashFunction);
}
} else {
getBuiltInVariableInfo(type, name, mappedName, infoList);
}
}
void getBuiltInVariableInfo(const TType& type,
const TString& name,
const TString& mappedName,
TVariableInfoList& infoList)
{
ASSERT(type.getBasicType() != EbtStruct);
TVariableInfo varInfo;
if (type.isArray()) {
varInfo.name = (name + "[0]").c_str();
varInfo.mappedName = (mappedName + "[0]").c_str();
varInfo.size = type.getArraySize();
varInfo.isArray = true;
} else {
varInfo.name = name.c_str();
varInfo.mappedName = mappedName.c_str();
varInfo.size = 1;
varInfo.isArray = false;
}
varInfo.precision = type.getPrecision();
varInfo.type = getVariableDataType(type);
infoList.push_back(varInfo);
}
void getUserDefinedVariableInfo(const TType& type,
const TString& name,
const TString& mappedName,
TVariableInfoList& infoList,
ShHashFunction64 hashFunction)
{
ASSERT(type.getBasicType() == EbtStruct || type.isInterfaceBlock());
const TFieldList& fields = type.isInterfaceBlock() ?
type.getInterfaceBlock()->fields() :
type.getStruct()->fields();
for (size_t i = 0; i < fields.size(); ++i) {
const TType& fieldType = *(fields[i]->type());
const TString& fieldName = fields[i]->name();
getVariableInfo(fieldType,
name + "." + fieldName,
mappedName + "." + TIntermTraverser::hash(fieldName, hashFunction),
infoList,
hashFunction);
}
}
TVariableInfo* findVariable(const TType& type,
const TString& name,
TVariableInfoList& infoList)
{
// TODO(zmo): optimize this function.
TString myName = name;
if (type.isArray())
myName += "[0]";
for (size_t ii = 0; ii < infoList.size(); ++ii)
{
if (infoList[ii].name.c_str() == myName)
return &(infoList[ii]);
}
return NULL;
}
} // namespace anonymous
TVariableInfo::TVariableInfo()
: type(SH_NONE),
size(0),
isArray(false),
precision(EbpUndefined),
staticUse(false)
{
}
TVariableInfo::TVariableInfo(ShDataType type, int size)
: type(type),
size(size),
isArray(false),
precision(EbpUndefined),
staticUse(false)
{
}
CollectVariables::CollectVariables(TVariableInfoList& attribs,
TVariableInfoList& uniforms,
TVariableInfoList& varyings,
ShHashFunction64 hashFunction)
: mAttribs(attribs),
mUniforms(uniforms),
mVaryings(varyings),
mPointCoordAdded(false),
mFrontFacingAdded(false),
mFragCoordAdded(false),
mHashFunction(hashFunction)
{
}
// We want to check whether a uniform/varying is statically used
// because we only count the used ones in packing computing.
// Also, gl_FragCoord, gl_PointCoord, and gl_FrontFacing count
// toward varying counting if they are statically used in a fragment
// shader.
void CollectVariables::visitSymbol(TIntermSymbol* symbol)
{
ASSERT(symbol != NULL);
TVariableInfo* var = NULL;
switch (symbol->getQualifier())
{
case EvqVaryingOut:
case EvqInvariantVaryingOut:
case EvqVaryingIn:
case EvqInvariantVaryingIn:
var = findVariable(symbol->getType(), symbol->getSymbol(), mVaryings);
break;
case EvqUniform:
var = findVariable(symbol->getType(), symbol->getSymbol(), mUniforms);
break;
case EvqFragCoord:
if (!mFragCoordAdded) {
TVariableInfo info;
info.name = "gl_FragCoord";
info.mappedName = "gl_FragCoord";
info.type = SH_FLOAT_VEC4;
info.size = 1;
info.precision = EbpMedium; // Use mediump as it doesn't really matter.
info.staticUse = true;
mVaryings.push_back(info);
mFragCoordAdded = true;
}
return;
case EvqFrontFacing:
if (!mFrontFacingAdded) {
TVariableInfo info;
info.name = "gl_FrontFacing";
info.mappedName = "gl_FrontFacing";
info.type = SH_BOOL;
info.size = 1;
info.precision = EbpUndefined;
info.staticUse = true;
mVaryings.push_back(info);
mFrontFacingAdded = true;
}
return;
case EvqPointCoord:
if (!mPointCoordAdded) {
TVariableInfo info;
info.name = "gl_PointCoord";
info.mappedName = "gl_PointCoord";
info.type = SH_FLOAT_VEC2;
info.size = 1;
info.precision = EbpMedium; // Use mediump as it doesn't really matter.
info.staticUse = true;
mVaryings.push_back(info);
mPointCoordAdded = true;
}
return;
default:
break;
}
if (var)
var->staticUse = true;
}
bool CollectVariables::visitAggregate(Visit, TIntermAggregate* node)
{
bool visitChildren = true;
switch (node->getOp())
{
case EOpDeclaration: {
const TIntermSequence& sequence = node->getSequence();
TQualifier qualifier = sequence.front()->getAsTyped()->getQualifier();
if (qualifier == EvqAttribute || qualifier == EvqVertexIn || qualifier == EvqUniform ||
qualifier == EvqVaryingIn || qualifier == EvqVaryingOut ||
qualifier == EvqInvariantVaryingIn || qualifier == EvqInvariantVaryingOut)
{
TVariableInfoList *infoList = NULL;
switch (qualifier)
{
case EvqAttribute:
case EvqVertexIn:
infoList = &mAttribs;
break;
case EvqUniform:
infoList = &mUniforms;
break;
default:
infoList = &mVaryings;
break;
}
for (TIntermSequence::const_iterator i = sequence.begin();
i != sequence.end(); ++i)
{
const TIntermSymbol* variable = (*i)->getAsSymbolNode();
// The only case in which the sequence will not contain a
// TIntermSymbol node is initialization. It will contain a
// TInterBinary node in that case. Since attributes, uniforms,
// and varyings cannot be initialized in a shader, we must have
// only TIntermSymbol nodes in the sequence.
ASSERT(variable != NULL);
TString processedSymbol;
if (mHashFunction == NULL)
processedSymbol = variable->getSymbol();
else
processedSymbol = TIntermTraverser::hash(variable->getSymbol(), mHashFunction);
getVariableInfo(variable->getType(),
variable->getSymbol(),
processedSymbol,
*infoList,
mHashFunction);
visitChildren = false;
}
}
break;
}
default: break;
}
return visitChildren;
}