blob: 8662bf51c72e6aad3b72d023f9e23764a6d5cd34 [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.
//
// CollectVariables.cpp: Collect lists of shader interface variables based on the AST.
#include "compiler/translator/CollectVariables.h"
#include "angle_gl.h"
#include "common/utilities.h"
#include "compiler/translator/HashNames.h"
#include "compiler/translator/IntermTraverse.h"
#include "compiler/translator/SymbolTable.h"
#include "compiler/translator/util.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;
}
}
// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
BlockType GetBlockType(TQualifier qualifier)
{
switch (qualifier)
{
case EvqUniform:
return BlockType::BLOCK_UNIFORM;
case EvqBuffer:
return BlockType::BLOCK_BUFFER;
case EvqPerVertexIn:
return BlockType::BLOCK_IN;
default:
UNREACHABLE();
return BlockType::BLOCK_UNIFORM;
}
}
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;
}
// Note that this shouldn't be called for interface blocks - static use information is collected for
// individual fields in case of interface blocks.
void MarkStaticallyUsed(ShaderVariable *variable)
{
if (!variable->staticUse)
{
if (variable->isStruct())
{
// Conservatively assume all fields are statically used as well.
for (auto &field : variable->fields)
{
MarkStaticallyUsed(&field);
}
}
variable->staticUse = true;
}
}
// Traverses the intermediate tree to collect all attributes, uniforms, varyings, fragment outputs,
// and interface blocks.
class CollectVariablesTraverser : public TIntermTraverser
{
public:
CollectVariablesTraverser(std::vector<Attribute> *attribs,
std::vector<OutputVariable> *outputVariables,
std::vector<Uniform> *uniforms,
std::vector<Varying> *inputVaryings,
std::vector<Varying> *outputVaryings,
std::vector<InterfaceBlock> *uniformBlocks,
std::vector<InterfaceBlock> *shaderStorageBlocks,
std::vector<InterfaceBlock> *inBlocks,
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
int shaderVersion,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior);
void visitSymbol(TIntermSymbol *symbol) override;
bool visitDeclaration(Visit, TIntermDeclaration *node) override;
bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
private:
std::string getMappedName(const TName &name) const;
void setCommonVariableProperties(const TType &type,
const TName &name,
ShaderVariable *variableOut) const;
Attribute recordAttribute(const TIntermSymbol &variable) const;
OutputVariable recordOutputVariable(const TIntermSymbol &variable) const;
Varying recordVarying(const TIntermSymbol &variable) const;
void recordInterfaceBlock(const TType &interfaceBlockType,
InterfaceBlock *interfaceBlock) const;
Uniform recordUniform(const TIntermSymbol &variable) const;
void setBuiltInInfoFromSymbolTable(const char *name, ShaderVariable *info);
void recordBuiltInVaryingUsed(const char *name,
bool *addedFlag,
std::vector<Varying> *varyings);
void recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag);
void recordBuiltInAttributeUsed(const char *name, bool *addedFlag);
InterfaceBlock *recordGLInUsed(const TType &glInType);
InterfaceBlock *findNamedInterfaceBlock(const TString &name) const;
std::vector<Attribute> *mAttribs;
std::vector<OutputVariable> *mOutputVariables;
std::vector<Uniform> *mUniforms;
std::vector<Varying> *mInputVaryings;
std::vector<Varying> *mOutputVaryings;
std::vector<InterfaceBlock> *mUniformBlocks;
std::vector<InterfaceBlock> *mShaderStorageBlocks;
std::vector<InterfaceBlock> *mInBlocks;
std::map<std::string, InterfaceBlockField *> mInterfaceBlockFields;
// Shader uniforms
bool mDepthRangeAdded;
// Vertex Shader builtins
bool mInstanceIDAdded;
bool mVertexIDAdded;
bool mPointSizeAdded;
// Vertex Shader and Geometry Shader builtins
bool mPositionAdded;
// Fragment Shader builtins
bool mPointCoordAdded;
bool mFrontFacingAdded;
bool mFragCoordAdded;
bool mLastFragDataAdded;
bool mFragColorAdded;
bool mFragDataAdded;
bool mFragDepthEXTAdded;
bool mFragDepthAdded;
bool mSecondaryFragColorEXTAdded;
bool mSecondaryFragDataEXTAdded;
// Geometry Shader builtins
bool mPerVertexInAdded;
bool mPrimitiveIDInAdded;
bool mInvocationIDAdded;
// Geometry Shader and Fragment Shader builtins
bool mPrimitiveIDAdded;
bool mLayerAdded;
ShHashFunction64 mHashFunction;
int mShaderVersion;
GLenum mShaderType;
const TExtensionBehavior &mExtensionBehavior;
};
CollectVariablesTraverser::CollectVariablesTraverser(
std::vector<sh::Attribute> *attribs,
std::vector<sh::OutputVariable> *outputVariables,
std::vector<sh::Uniform> *uniforms,
std::vector<sh::Varying> *inputVaryings,
std::vector<sh::Varying> *outputVaryings,
std::vector<sh::InterfaceBlock> *uniformBlocks,
std::vector<sh::InterfaceBlock> *shaderStorageBlocks,
std::vector<sh::InterfaceBlock> *inBlocks,
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
int shaderVersion,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior)
: TIntermTraverser(true, false, false, symbolTable),
mAttribs(attribs),
mOutputVariables(outputVariables),
mUniforms(uniforms),
mInputVaryings(inputVaryings),
mOutputVaryings(outputVaryings),
mUniformBlocks(uniformBlocks),
mShaderStorageBlocks(shaderStorageBlocks),
mInBlocks(inBlocks),
mDepthRangeAdded(false),
mInstanceIDAdded(false),
mVertexIDAdded(false),
mPointSizeAdded(false),
mPositionAdded(false),
mPointCoordAdded(false),
mFrontFacingAdded(false),
mFragCoordAdded(false),
mLastFragDataAdded(false),
mFragColorAdded(false),
mFragDataAdded(false),
mFragDepthEXTAdded(false),
mFragDepthAdded(false),
mSecondaryFragColorEXTAdded(false),
mSecondaryFragDataEXTAdded(false),
mPerVertexInAdded(false),
mPrimitiveIDInAdded(false),
mInvocationIDAdded(false),
mPrimitiveIDAdded(false),
mLayerAdded(false),
mHashFunction(hashFunction),
mShaderVersion(shaderVersion),
mShaderType(shaderType),
mExtensionBehavior(extensionBehavior)
{
}
std::string CollectVariablesTraverser::getMappedName(const TName &name) const
{
return HashName(name, mHashFunction, nullptr).c_str();
}
void CollectVariablesTraverser::setBuiltInInfoFromSymbolTable(const char *name,
ShaderVariable *info)
{
TVariable *symbolTableVar =
reinterpret_cast<TVariable *>(mSymbolTable->findBuiltIn(name, mShaderVersion));
ASSERT(symbolTableVar);
const TType &type = symbolTableVar->getType();
info->name = name;
info->mappedName = name;
info->type = GLVariableType(type);
ASSERT(!type.isArrayOfArrays());
info->arraySize = type.isArray() ? type.getOutermostArraySize() : 0;
info->precision = GLVariablePrecision(type);
}
void CollectVariablesTraverser::recordBuiltInVaryingUsed(const char *name,
bool *addedFlag,
std::vector<Varying> *varyings)
{
ASSERT(varyings);
if (!(*addedFlag))
{
Varying info;
setBuiltInInfoFromSymbolTable(name, &info);
info.staticUse = true;
info.isInvariant = mSymbolTable->isVaryingInvariant(name);
varyings->push_back(info);
(*addedFlag) = true;
}
}
void CollectVariablesTraverser::recordBuiltInFragmentOutputUsed(const char *name, bool *addedFlag)
{
if (!(*addedFlag))
{
OutputVariable info;
setBuiltInInfoFromSymbolTable(name, &info);
info.staticUse = true;
mOutputVariables->push_back(info);
(*addedFlag) = true;
}
}
void CollectVariablesTraverser::recordBuiltInAttributeUsed(const char *name, bool *addedFlag)
{
if (!(*addedFlag))
{
Attribute info;
setBuiltInInfoFromSymbolTable(name, &info);
info.staticUse = true;
info.location = -1;
mAttribs->push_back(info);
(*addedFlag) = true;
}
}
InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
{
if (!mPerVertexInAdded)
{
ASSERT(glInType.getQualifier() == EvqPerVertexIn);
InterfaceBlock info;
recordInterfaceBlock(glInType, &info);
info.staticUse = true;
mPerVertexInAdded = true;
mInBlocks->push_back(info);
return &mInBlocks->back();
}
else
{
return FindVariable("gl_PerVertex", mInBlocks);
}
}
// 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 CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
{
ASSERT(symbol != nullptr);
if (symbol->getName().isInternal())
{
// Internal variables are not collected.
return;
}
ShaderVariable *var = nullptr;
const TString &symbolName = symbol->getName().getString();
if (IsVaryingIn(symbol->getQualifier()))
{
var = FindVariable(symbolName, mInputVaryings);
}
else if (IsVaryingOut(symbol->getQualifier()))
{
var = FindVariable(symbolName, mOutputVaryings);
}
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_NONE;
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(), mUniformBlocks);
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:
recordBuiltInVaryingUsed("gl_FragCoord", &mFragCoordAdded, mInputVaryings);
return;
case EvqFrontFacing:
recordBuiltInVaryingUsed("gl_FrontFacing", &mFrontFacingAdded, mInputVaryings);
return;
case EvqPointCoord:
recordBuiltInVaryingUsed("gl_PointCoord", &mPointCoordAdded, mInputVaryings);
return;
case EvqInstanceID:
// Whenever the SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW option is set,
// gl_InstanceID is added inside expressions to initialize ViewID_OVR and
// InstanceID. gl_InstanceID is not added to the symbol table for ESSL1 shaders
// which makes it necessary to populate the type information explicitly instead of
// extracting it from the symbol table.
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:
recordBuiltInAttributeUsed("gl_VertexID", &mVertexIDAdded);
return;
case EvqPosition:
recordBuiltInVaryingUsed("gl_Position", &mPositionAdded, mOutputVaryings);
return;
case EvqPointSize:
recordBuiltInVaryingUsed("gl_PointSize", &mPointSizeAdded, mOutputVaryings);
return;
case EvqLastFragData:
recordBuiltInVaryingUsed("gl_LastFragData", &mLastFragDataAdded, mInputVaryings);
return;
case EvqFragColor:
recordBuiltInFragmentOutputUsed("gl_FragColor", &mFragColorAdded);
return;
case EvqFragData:
if (!mFragDataAdded)
{
OutputVariable info;
setBuiltInInfoFromSymbolTable("gl_FragData", &info);
if (!::IsExtensionEnabled(mExtensionBehavior, "GL_EXT_draw_buffers"))
{
info.arraySize = 1;
}
info.staticUse = true;
mOutputVariables->push_back(info);
mFragDataAdded = true;
}
return;
case EvqFragDepthEXT:
recordBuiltInFragmentOutputUsed("gl_FragDepthEXT", &mFragDepthEXTAdded);
return;
case EvqFragDepth:
recordBuiltInFragmentOutputUsed("gl_FragDepth", &mFragDepthAdded);
return;
case EvqSecondaryFragColorEXT:
recordBuiltInFragmentOutputUsed("gl_SecondaryFragColorEXT",
&mSecondaryFragColorEXTAdded);
return;
case EvqSecondaryFragDataEXT:
recordBuiltInFragmentOutputUsed("gl_SecondaryFragDataEXT",
&mSecondaryFragDataEXTAdded);
return;
case EvqInvocationID:
recordBuiltInVaryingUsed("gl_InvocationID", &mInvocationIDAdded, mInputVaryings);
break;
case EvqPrimitiveIDIn:
recordBuiltInVaryingUsed("gl_PrimitiveIDIn", &mPrimitiveIDInAdded, mInputVaryings);
break;
case EvqPrimitiveID:
if (mShaderType == GL_GEOMETRY_SHADER_OES)
{
recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mOutputVaryings);
}
else
{
ASSERT(mShaderType == GL_FRAGMENT_SHADER);
recordBuiltInVaryingUsed("gl_PrimitiveID", &mPrimitiveIDAdded, mInputVaryings);
}
break;
case EvqLayer:
if (mShaderType == GL_GEOMETRY_SHADER_OES)
{
recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mOutputVaryings);
}
else if (mShaderType == GL_FRAGMENT_SHADER)
{
recordBuiltInVaryingUsed("gl_Layer", &mLayerAdded, mInputVaryings);
}
else
{
ASSERT(mShaderType == GL_VERTEX_SHADER &&
IsExtensionEnabled(mExtensionBehavior, "GL_OVR_multiview"));
}
break;
default:
break;
}
}
if (var)
{
MarkStaticallyUsed(var);
}
}
void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
const TName &name,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
const TStructure *structure = type.getStruct();
if (!structure)
{
variableOut->type = GLVariableType(type);
variableOut->precision = GLVariablePrecision(type);
}
else
{
// Structures use a NONE type that isn't exposed outside ANGLE.
variableOut->type = GL_NONE;
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(), TName(field->name()), &fieldVariable);
variableOut->fields.push_back(fieldVariable);
}
}
variableOut->name = name.getString().c_str();
variableOut->mappedName = getMappedName(name);
// TODO(oetuaho@nvidia.com): Uniforms can be arrays of arrays, so this assert will need to be
// removed.
ASSERT(!type.isArrayOfArrays());
variableOut->arraySize = type.isArray() ? type.getOutermostArraySize() : 0;
}
Attribute CollectVariablesTraverser::recordAttribute(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
ASSERT(!type.getStruct());
Attribute attribute;
setCommonVariableProperties(type, variable.getName(), &attribute);
attribute.location = type.getLayoutQualifier().location;
return attribute;
}
OutputVariable CollectVariablesTraverser::recordOutputVariable(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
ASSERT(!type.getStruct());
OutputVariable outputVariable;
setCommonVariableProperties(type, variable.getName(), &outputVariable);
outputVariable.location = type.getLayoutQualifier().location;
return outputVariable;
}
Varying CollectVariablesTraverser::recordVarying(const TIntermSymbol &variable) const
{
const TType &type = variable.getType();
Varying varying;
setCommonVariableProperties(type, variable.getName(), &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;
}
// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
void CollectVariablesTraverser::recordInterfaceBlock(const TType &interfaceBlockType,
InterfaceBlock *interfaceBlock) const
{
ASSERT(interfaceBlockType.getBasicType() == EbtInterfaceBlock);
ASSERT(interfaceBlock);
const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
ASSERT(blockType);
interfaceBlock->name = blockType->name().c_str();
interfaceBlock->mappedName = getMappedName(TName(blockType->name()));
interfaceBlock->instanceName =
(blockType->hasInstanceName() ? blockType->instanceName().c_str() : "");
ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9
interfaceBlock->arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
interfaceBlock->blockType = GetBlockType(interfaceBlockType.getQualifier());
if (interfaceBlock->blockType == BlockType::BLOCK_UNIFORM ||
interfaceBlock->blockType == BlockType::BLOCK_BUFFER)
{
interfaceBlock->isRowMajorLayout = (blockType->matrixPacking() == EmpRowMajor);
interfaceBlock->binding = blockType->blockBinding();
interfaceBlock->layout = GetBlockLayoutType(blockType->blockStorage());
}
// Gather field information
for (const TField *field : blockType->fields())
{
const TType &fieldType = *field->type();
InterfaceBlockField fieldVariable;
setCommonVariableProperties(fieldType, TName(field->name()), &fieldVariable);
fieldVariable.isRowMajorLayout =
(fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
interfaceBlock->fields.push_back(fieldVariable);
}
}
Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
{
Uniform uniform;
setCommonVariableProperties(variable.getType(), variable.getName(), &uniform);
uniform.binding = variable.getType().getLayoutQualifier().binding;
uniform.location = variable.getType().getLayoutQualifier().location;
uniform.offset = variable.getType().getLayoutQualifier().offset;
return uniform;
}
bool CollectVariablesTraverser::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 (variable.getName().isInternal())
{
// Internal variables are not collected.
continue;
}
// TODO(jiawei.shao@intel.com): implement GL_OES_shader_io_blocks.
if (typedNode.getBasicType() == EbtInterfaceBlock)
{
InterfaceBlock interfaceBlock;
recordInterfaceBlock(variable.getType(), &interfaceBlock);
switch (qualifier)
{
case EvqUniform:
mUniformBlocks->push_back(interfaceBlock);
break;
case EvqBuffer:
mShaderStorageBlocks->push_back(interfaceBlock);
break;
default:
UNREACHABLE();
}
}
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:
if (IsVaryingIn(qualifier))
{
mInputVaryings->push_back(recordVarying(variable));
}
else
{
ASSERT(IsVaryingOut(qualifier));
mOutputVaryings->push_back(recordVarying(variable));
}
break;
}
}
}
// None of the recorded variables can have initializers, so we don't need to traverse the
// declarators.
return false;
}
// TODO(jiawei.shao@intel.com): add search on mInBlocks and mOutBlocks when implementing
// GL_OES_shader_io_blocks.
InterfaceBlock *CollectVariablesTraverser::findNamedInterfaceBlock(const TString &blockName) const
{
InterfaceBlock *namedBlock = FindVariable(blockName, mUniformBlocks);
if (!namedBlock)
{
namedBlock = FindVariable(blockName, mShaderStorageBlocks);
}
return namedBlock;
}
bool CollectVariablesTraverser::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);
InterfaceBlock *namedBlock = nullptr;
bool traverseIndexExpression = false;
TIntermBinary *interfaceIndexingNode = blockNode->getAsBinaryNode();
if (interfaceIndexingNode)
{
TIntermTyped *interfaceNode = interfaceIndexingNode->getLeft()->getAsTyped();
ASSERT(interfaceNode);
const TType &interfaceType = interfaceNode->getType();
if (interfaceType.getQualifier() == EvqPerVertexIn)
{
namedBlock = recordGLInUsed(interfaceType);
ASSERT(namedBlock);
// We need to continue traversing to collect useful variables in the index
// expression of gl_in.
traverseIndexExpression = true;
}
}
const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
if (!namedBlock)
{
namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
}
ASSERT(namedBlock);
namedBlock->staticUse = true;
unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
ASSERT(fieldIndex < namedBlock->fields.size());
namedBlock->fields[fieldIndex].staticUse = true;
if (traverseIndexExpression)
{
ASSERT(interfaceIndexingNode);
interfaceIndexingNode->getRight()->traverse(this);
}
return false;
}
return true;
}
} // anonymous namespace
void CollectVariables(TIntermBlock *root,
std::vector<Attribute> *attributes,
std::vector<OutputVariable> *outputVariables,
std::vector<Uniform> *uniforms,
std::vector<Varying> *inputVaryings,
std::vector<Varying> *outputVaryings,
std::vector<InterfaceBlock> *uniformBlocks,
std::vector<InterfaceBlock> *shaderStorageBlocks,
std::vector<InterfaceBlock> *inBlocks,
ShHashFunction64 hashFunction,
TSymbolTable *symbolTable,
int shaderVersion,
GLenum shaderType,
const TExtensionBehavior &extensionBehavior)
{
CollectVariablesTraverser collect(attributes, outputVariables, uniforms, inputVaryings,
outputVaryings, uniformBlocks, shaderStorageBlocks, inBlocks,
hashFunction, symbolTable, shaderVersion, shaderType,
extensionBehavior);
root->traverse(&collect);
}
} // namespace sh