// // Copyright (c) 2002-2010 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/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.getNominalSize()) { case 2: return SH_FLOAT_MAT2; case 3: return SH_FLOAT_MAT3; 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 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 EbtSamplerCube: return SH_SAMPLER_CUBE; case EbtSamplerExternalOES: return SH_SAMPLER_EXTERNAL_OES; case EbtSampler2DRect: return SH_SAMPLER_2D_RECT_ARB; 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) { 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(); } else { varInfo.name = name.c_str(); varInfo.mappedName = mappedName.c_str(); varInfo.size = 1; } 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); const TFieldList& 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), precision(EbpUndefined), staticUse(false) { } TVariableInfo::TVariableInfo(ShDataType type, int size) : type(type), size(size), 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 == EvqUniform || qualifier == EvqVaryingIn || qualifier == EvqVaryingOut || qualifier == EvqInvariantVaryingIn || qualifier == EvqInvariantVaryingOut) { TVariableInfoList& infoList = qualifier == EvqAttribute ? mAttribs : (qualifier == EvqUniform ? mUniforms : mVaryings); 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->getOriginalSymbol(), mHashFunction); getVariableInfo(variable->getType(), variable->getOriginalSymbol(), processedSymbol, infoList, mHashFunction); visitChildren = false; } } break; } default: break; } return visitChildren; }