blob: 7673edca62d1185475d27271551c4242ad87bcba [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 "angle_gl.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/VariableInfo.h"
#include "compiler/translator/util.h"
#include "common/utilities.h"
namespace sh
{
namespace
{
BlockLayoutType GetBlockLayoutType(TLayoutBlockStorage blockStorage)
{
switch (blockStorage)
{
case EbsPacked:
return BLOCKLAYOUT_PACKED;
case EbsShared:
return BLOCKLAYOUT_SHARED;
case EbsStd140:
return BLOCKLAYOUT_STANDARD;
default:
UNREACHABLE();
return BLOCKLAYOUT_SHARED;
}
}
void ExpandUserDefinedVariable(const ShaderVariable &variable,
const std::string &name,
const std::string &mappedName,
bool markStaticUse,
std::vector<ShaderVariable> *expanded)
{
ASSERT(variable.isStruct());
const std::vector<ShaderVariable> &fields = variable.fields;
for (size_t fieldIndex = 0; fieldIndex < fields.size(); fieldIndex++)
{
const ShaderVariable &field = fields[fieldIndex];
ExpandVariable(field, name + "." + field.name, mappedName + "." + field.mappedName,
markStaticUse, expanded);
}
}
template <class VarT>
VarT *FindVariable(const TString &name, std::vector<VarT> *infoList)
{
// TODO(zmo): optimize this function.
for (size_t ii = 0; ii < infoList->size(); ++ii)
{
if ((*infoList)[ii].name.c_str() == name)
return &((*infoList)[ii]);
}
return nullptr;
}
}
CollectVariables::CollectVariables(std::vector<sh::Attribute> *attribs,
std::vector<sh::OutputVariable> *outputVariables,
std::vector<sh::Uniform> *uniforms,
std::vector<sh::Varying> *varyings,
std::vector<sh::InterfaceBlock> *interfaceBlocks,
ShHashFunction64 hashFunction,
const TSymbolTable &symbolTable,
const TExtensionBehavior &extensionBehavior)
: TIntermTraverser(true, false, false),
mAttribs(attribs),
mOutputVariables(outputVariables),
mUniforms(uniforms),
mVaryings(varyings),
mInterfaceBlocks(interfaceBlocks),
mDepthRangeAdded(false),
mPointCoordAdded(false),
mFrontFacingAdded(false),
mFragCoordAdded(false),
mInstanceIDAdded(false),
mVertexIDAdded(false),
mPositionAdded(false),
mPointSizeAdded(false),
mLastFragDataAdded(false),
mFragColorAdded(false),
mFragDataAdded(false),
mFragDepthEXTAdded(false),
mFragDepthAdded(false),
mSecondaryFragColorEXTAdded(false),
mSecondaryFragDataEXTAdded(false),
mHashFunction(hashFunction),
mSymbolTable(symbolTable),
mExtensionBehavior(extensionBehavior)
{
}
// 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 != nullptr);
ShaderVariable *var = nullptr;
const TString &symbolName = symbol->getSymbol();
if (IsVarying(symbol->getQualifier()))
{
var = FindVariable(symbolName, mVaryings);
}
else if (symbol->getType().getBasicType() == EbtInterfaceBlock)
{
UNREACHABLE();
}
else if (symbolName == "gl_DepthRange")
{
ASSERT(symbol->getQualifier() == EvqUniform);
if (!mDepthRangeAdded)
{
Uniform info;
const char kName[] = "gl_DepthRange";
info.name = kName;
info.mappedName = kName;
info.type = GL_STRUCT_ANGLEX;
info.arraySize = 0;
info.precision = GL_NONE;
info.staticUse = true;
ShaderVariable nearInfo;
const char kNearName[] = "near";
nearInfo.name = kNearName;
nearInfo.mappedName = kNearName;
nearInfo.type = GL_FLOAT;
nearInfo.arraySize = 0;
nearInfo.precision = GL_HIGH_FLOAT;
nearInfo.staticUse = true;
ShaderVariable farInfo;
const char kFarName[] = "far";
farInfo.name = kFarName;
farInfo.mappedName = kFarName;
farInfo.type = GL_FLOAT;
farInfo.arraySize = 0;
farInfo.precision = GL_HIGH_FLOAT;
farInfo.staticUse = true;
ShaderVariable diffInfo;
const char kDiffName[] = "diff";
diffInfo.name = kDiffName;
diffInfo.mappedName = kDiffName;
diffInfo.type = GL_FLOAT;
diffInfo.arraySize = 0;
diffInfo.precision = GL_HIGH_FLOAT;
diffInfo.staticUse = true;
info.fields.push_back(nearInfo);
info.fields.push_back(farInfo);
info.fields.push_back(diffInfo);
mUniforms->push_back(info);
mDepthRangeAdded = true;
}
}
else
{
switch (symbol->getQualifier())
{
case EvqAttribute:
case EvqVertexIn:
var = FindVariable(symbolName, mAttribs);
break;
case EvqFragmentOut:
var = FindVariable(symbolName, mOutputVariables);
break;
case EvqUniform:
{
const TInterfaceBlock *interfaceBlock = symbol->getType().getInterfaceBlock();
if (interfaceBlock)
{
InterfaceBlock *namedBlock =
FindVariable(interfaceBlock->name(), mInterfaceBlocks);
ASSERT(namedBlock);
var = FindVariable(symbolName, &namedBlock->fields);
// Set static use on the parent interface block here
namedBlock->staticUse = true;
}
else
{
var = FindVariable(symbolName, mUniforms);
}
// It's an internal error to reference an undefined user uniform
ASSERT(symbolName.compare(0, 3, "gl_") != 0 || var);
}
break;
case EvqFragCoord:
if (!mFragCoordAdded)
{
Varying info;
const char kName[] = "gl_FragCoord";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mFragCoordAdded = true;
}
return;
case EvqFrontFacing:
if (!mFrontFacingAdded)
{
Varying info;
const char kName[] = "gl_FrontFacing";
info.name = kName;
info.mappedName = kName;
info.type = GL_BOOL;
info.arraySize = 0;
info.precision = GL_NONE;
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mFrontFacingAdded = true;
}
return;
case EvqPointCoord:
if (!mPointCoordAdded)
{
Varying info;
const char kName[] = "gl_PointCoord";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC2;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mPointCoordAdded = true;
}
return;
case EvqInstanceID:
if (!mInstanceIDAdded)
{
Attribute info;
const char kName[] = "gl_InstanceID";
info.name = kName;
info.mappedName = kName;
info.type = GL_INT;
info.arraySize = 0;
info.precision = GL_HIGH_INT; // Defined by spec.
info.staticUse = true;
info.location = -1;
mAttribs->push_back(info);
mInstanceIDAdded = true;
}
return;
case EvqVertexID:
if (!mVertexIDAdded)
{
Attribute info;
const char kName[] = "gl_VertexID";
info.name = kName;
info.mappedName = kName;
info.type = GL_INT;
info.arraySize = 0;
info.precision = GL_HIGH_INT; // Defined by spec.
info.staticUse = true;
info.location = -1;
mAttribs->push_back(info);
mVertexIDAdded = true;
}
return;
case EvqPosition:
if (!mPositionAdded)
{
Varying info;
const char kName[] = "gl_Position";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = 0;
info.precision = GL_HIGH_FLOAT; // Defined by spec.
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mPositionAdded = true;
}
return;
case EvqPointSize:
if (!mPointSizeAdded)
{
Varying info;
const char kName[] = "gl_PointSize";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mPointSizeAdded = true;
}
return;
case EvqLastFragData:
if (!mLastFragDataAdded)
{
Varying info;
const char kName[] = "gl_LastFragData";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = static_cast<const TVariable *>(
mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))
->getConstPointer()
->getIConst();
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
info.isInvariant = mSymbolTable.isVaryingInvariant(kName);
mVaryings->push_back(info);
mLastFragDataAdded = true;
}
return;
case EvqFragColor:
if (!mFragColorAdded)
{
OutputVariable info;
const char kName[] = "gl_FragColor";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mFragColorAdded = true;
}
return;
case EvqFragData:
if (!mFragDataAdded)
{
OutputVariable info;
const char kName[] = "gl_FragData";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
if (::IsExtensionEnabled(mExtensionBehavior, "GL_EXT_draw_buffers"))
{
info.arraySize = static_cast<const TVariable *>(
mSymbolTable.findBuiltIn("gl_MaxDrawBuffers", 100))
->getConstPointer()
->getIConst();
}
else
{
info.arraySize = 1;
}
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mFragDataAdded = true;
}
return;
case EvqFragDepthEXT:
if (!mFragDepthEXTAdded)
{
OutputVariable info;
const char kName[] = "gl_FragDepthEXT";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT;
info.arraySize = 0;
info.precision =
GLVariablePrecision(static_cast<const TVariable *>(
mSymbolTable.findBuiltIn("gl_FragDepthEXT", 100))
->getType());
info.staticUse = true;
mOutputVariables->push_back(info);
mFragDepthEXTAdded = true;
}
return;
case EvqFragDepth:
if (!mFragDepthAdded)
{
OutputVariable info;
const char kName[] = "gl_FragDepth";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT;
info.arraySize = 0;
info.precision = GL_HIGH_FLOAT;
info.staticUse = true;
mOutputVariables->push_back(info);
mFragDepthAdded = true;
}
return;
case EvqSecondaryFragColorEXT:
if (!mSecondaryFragColorEXTAdded)
{
OutputVariable info;
const char kName[] = "gl_SecondaryFragColorEXT";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
info.arraySize = 0;
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mSecondaryFragColorEXTAdded = true;
}
return;
case EvqSecondaryFragDataEXT:
if (!mSecondaryFragDataEXTAdded)
{
OutputVariable info;
const char kName[] = "gl_SecondaryFragDataEXT";
info.name = kName;
info.mappedName = kName;
info.type = GL_FLOAT_VEC4;
const TVariable *maxDualSourceDrawBuffersVar = static_cast<const TVariable *>(
mSymbolTable.findBuiltIn("gl_MaxDualSourceDrawBuffersEXT", 100));
info.arraySize = maxDualSourceDrawBuffersVar->getConstPointer()->getIConst();
info.precision = GL_MEDIUM_FLOAT; // Defined by spec.
info.staticUse = true;
mOutputVariables->push_back(info);
mSecondaryFragDataEXTAdded = true;
}
return;
default:
break;
}
}
if (var)
{
var->staticUse = true;
}
}
void CollectVariables::setCommonVariableProperties(const TType &type,
const TString &name,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
const TStructure *structure = type.getStruct();
if (!structure)
{
variableOut->type = GLVariableType(type);
variableOut->precision = GLVariablePrecision(type);
}
else
{
// Note: this enum value is not exposed outside ANGLE
variableOut->type = GL_STRUCT_ANGLEX;
variableOut->structName = structure->name().c_str();
const TFieldList &fields = structure->fields();
for (TField *field : fields)
{
// Regardless of the variable type (uniform, in/out etc.) its fields are always plain
// ShaderVariable objects.
ShaderVariable fieldVariable;
setCommonVariableProperties(*field->type(), field->name(), &fieldVariable);
variableOut->fields.push_back(fieldVariable);
}
}
variableOut->name = name.c_str();
variableOut->mappedName = TIntermTraverser::hash(name, mHashFunction).c_str();
variableOut->arraySize = type.getArraySize();
}
Attribute CollectVariables::recordAttribute(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
ASSERT(!type.getStruct());
Attribute attribute;
setCommonVariableProperties(type, variable.getSymbol(), &attribute);
attribute.location = type.getLayoutQualifier().location;
return attribute;
}
OutputVariable CollectVariables::recordOutputVariable(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
ASSERT(!type.getStruct());
OutputVariable outputVariable;
setCommonVariableProperties(type, variable.getSymbol(), &outputVariable);
outputVariable.location = type.getLayoutQualifier().location;
return outputVariable;
}
Varying CollectVariables::recordVarying(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
Varying varying;
setCommonVariableProperties(type, variable.getSymbol(), &varying);
switch (type.getQualifier())
{
case EvqVaryingIn:
case EvqVaryingOut:
case EvqVertexOut:
case EvqSmoothOut:
case EvqFlatOut:
case EvqCentroidOut:
if (mSymbolTable.isVaryingInvariant(std::string(variable.getSymbol().c_str())) ||
type.isInvariant())
{
varying.isInvariant = true;
}
break;
default:
break;
}
varying.interpolation = GetInterpolationType(type.getQualifier());
return varying;
}
InterfaceBlock CollectVariables::recordInterfaceBlock(const TIntermSymbol &variable) const
{
const TInterfaceBlock *blockType = variable.getType().getInterfaceBlock();
ASSERT(blockType);
InterfaceBlock interfaceBlock;
interfaceBlock.name = blockType->name().c_str();
interfaceBlock.mappedName =
TIntermTraverser::hash(blockType->name().c_str(), mHashFunction).c_str();
interfaceBlock.instanceName =
(blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
interfaceBlock.arraySize = variable.getArraySize();
interfaceBlock.isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
interfaceBlock.layout = GetBlockLayoutType(blockType->blockStorage());
// Gather field information
for (const TField *field : blockType->fields())
{
const TType &fieldType = *field->type();
InterfaceBlockField fieldVariable;
setCommonVariableProperties(fieldType, field->name(), &fieldVariable);
fieldVariable.isRowMajorLayout =
(fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
interfaceBlock.fields.push_back(fieldVariable);
}
return interfaceBlock;
}
Uniform CollectVariables::recordUniform(const TIntermSymbol &variable) const
{
Uniform uniform;
setCommonVariableProperties(variable.getType(), variable.getSymbol(), &uniform);
uniform.binding = variable.getType().getLayoutQualifier().binding;
uniform.location = variable.getType().getLayoutQualifier().location;
return uniform;
}
bool CollectVariables::visitDeclaration(Visit, TIntermDeclaration *node)
{
const TIntermSequence &sequence = *(node->getSequence());
ASSERT(!sequence.empty());
const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
TQualifier qualifier = typedNode.getQualifier();
bool isShaderVariable = qualifier == EvqAttribute || qualifier == EvqVertexIn ||
qualifier == EvqFragmentOut || qualifier == EvqUniform ||
IsVarying(qualifier);
if (typedNode.getBasicType() != EbtInterfaceBlock && !isShaderVariable)
{
return true;
}
for (TIntermNode *variableNode : sequence)
{
// 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, varyings, outputs and interface blocks cannot be initialized in a shader, we
// must have only TIntermSymbol nodes in the sequence in the cases we are interested in.
const TIntermSymbol &variable = *variableNode->getAsSymbolNode();
if (typedNode.getBasicType() == EbtInterfaceBlock)
{
mInterfaceBlocks->push_back(recordInterfaceBlock(variable));
}
else
{
switch (qualifier)
{
case EvqAttribute:
case EvqVertexIn:
mAttribs->push_back(recordAttribute(variable));
break;
case EvqFragmentOut:
mOutputVariables->push_back(recordOutputVariable(variable));
break;
case EvqUniform:
mUniforms->push_back(recordUniform(variable));
break;
default:
mVaryings->push_back(recordVarying(variable));
break;
}
}
}
// None of the recorded variables can have initializers, so we don't need to traverse the
// declarators.
return false;
}
bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode)
{
if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
{
// NOTE: we do not determine static use for individual blocks of an array
TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
ASSERT(blockNode);
TIntermConstantUnion *constantUnion = binaryNode->getRight()->getAsConstantUnion();
ASSERT(constantUnion);
const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), mInterfaceBlocks);
ASSERT(namedBlock);
namedBlock->staticUse = true;
unsigned int fieldIndex = constantUnion->getUConst(0);
ASSERT(fieldIndex < namedBlock->fields.size());
namedBlock->fields[fieldIndex].staticUse = true;
return false;
}
return true;
}
void ExpandVariable(const ShaderVariable &variable,
const std::string &name,
const std::string &mappedName,
bool markStaticUse,
std::vector<ShaderVariable> *expanded)
{
if (variable.isStruct())
{
if (variable.isArray())
{
for (unsigned int elementIndex = 0; elementIndex < variable.elementCount();
elementIndex++)
{
std::string lname = name + ::ArrayString(elementIndex);
std::string lmappedName = mappedName + ::ArrayString(elementIndex);
ExpandUserDefinedVariable(variable, lname, lmappedName, markStaticUse, expanded);
}
}
else
{
ExpandUserDefinedVariable(variable, name, mappedName, markStaticUse, expanded);
}
}
else
{
ShaderVariable expandedVar = variable;
expandedVar.name = name;
expandedVar.mappedName = mappedName;
// Mark all expanded fields as used if the parent is used
if (markStaticUse)
{
expandedVar.staticUse = true;
}
if (expandedVar.isArray())
{
expandedVar.name += "[0]";
expandedVar.mappedName += "[0]";
}
expanded->push_back(expandedVar);
}
}
void ExpandUniforms(const std::vector<Uniform> &compact, std::vector<ShaderVariable> *expanded)
{
for (size_t variableIndex = 0; variableIndex < compact.size(); variableIndex++)
{
const ShaderVariable &variable = compact[variableIndex];
ExpandVariable(variable, variable.name, variable.mappedName, variable.staticUse, expanded);
}
}
}