Commit 94bbed1e by Olli Etuaho Committed by Commit Bot

Collect static use information during parsing

We now collect metadata for variables in the symbol table. The metadata is stored in a map using the variable unique id as a key, so we can store the variables themselves as constexpr while still having dynamic metadata. For now we collect whether a variable is statically read or written. This can be used to more accurately determine whether a variable is statically used, but can also enable more optimizations in the future, such as pruning variables that are never read or folding variables that are never written after initialization. The collection is done during parsing, so that nothing is pruned from the AST before the static use is recorded. Static writes are flagged in ParseContext::checkCanBeLValue, as that function is already called for all variables that are written. Static reads are flagged whenever there's an operation that requires a variable to be read. This includes: * Unary and binary math ops * Comma ops * Ternary ops * Assignments * Returning the variable * Passing the variable as an in or inout argument to a function * Using the variable as a constructor argument * Using the variable as an if statement condition * Using the variable as a loop condition or expression * Using the variable as an index * Using the variable as a switch statement init expression In case there are statements that simply refer to a variable without doing operations on it, the variable is being treated as statically read. Examples of such statements: my_var; my_arr[2]; These are a bit of a corner case, but it makes sense to treat them as static use for validation purposes. Collecting correct static use information costs us a bit of compiler performance, but the regression is on the order of just a few percent in the compiler perf tests. BUG=angleproject:2262 TEST=angle_unittests, angle_end2end_tests Change-Id: Ib0d7add7e4a7d11bffeb2a4861eeea982c562234 Reviewed-on: https://chromium-review.googlesource.com/977964Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 89398b65
...@@ -68,21 +68,21 @@ VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *infoList) ...@@ -68,21 +68,21 @@ VarT *FindVariable(const ImmutableString &name, std::vector<VarT> *infoList)
return nullptr; return nullptr;
} }
// Note that this shouldn't be called for interface blocks - static use information is collected for // Note that this shouldn't be called for interface blocks - active information is collected for
// individual fields in case of interface blocks. // individual fields in case of interface blocks.
void MarkStaticallyUsed(ShaderVariable *variable) void MarkActive(ShaderVariable *variable)
{ {
if (!variable->staticUse) if (!variable->active)
{ {
if (variable->isStruct()) if (variable->isStruct())
{ {
// Conservatively assume all fields are statically used as well. // Conservatively assume all fields are statically used as well.
for (auto &field : variable->fields) for (auto &field : variable->fields)
{ {
MarkStaticallyUsed(&field); MarkActive(&field);
} }
} }
variable->staticUse = true; ASSERT(variable->staticUse);
variable->active = true; variable->active = true;
} }
} }
...@@ -126,9 +126,12 @@ class CollectVariablesTraverser : public TIntermTraverser ...@@ -126,9 +126,12 @@ class CollectVariablesTraverser : public TIntermTraverser
private: private:
std::string getMappedName(const TSymbol *symbol) const; std::string getMappedName(const TSymbol *symbol) const;
void setFieldOrVariableProperties(const TType &type, ShaderVariable *variableOut) const; void setFieldOrVariableProperties(const TType &type,
bool staticUse,
ShaderVariable *variableOut) const;
void setFieldProperties(const TType &type, void setFieldProperties(const TType &type,
const ImmutableString &name, const ImmutableString &name,
bool staticUse,
ShaderVariable *variableOut) const; ShaderVariable *variableOut) const;
void setCommonVariableProperties(const TType &type, void setCommonVariableProperties(const TType &type,
const TVariable &variable, const TVariable &variable,
...@@ -322,8 +325,6 @@ InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType) ...@@ -322,8 +325,6 @@ InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
ASSERT(glInType.getQualifier() == EvqPerVertexIn); ASSERT(glInType.getQualifier() == EvqPerVertexIn);
InterfaceBlock info; InterfaceBlock info;
recordInterfaceBlock("gl_in", glInType, &info); recordInterfaceBlock("gl_in", glInType, &info);
info.staticUse = true;
info.active = true;
mPerVertexInAdded = true; mPerVertexInAdded = true;
mInBlocks->push_back(info); mInBlocks->push_back(info);
...@@ -551,15 +552,18 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol) ...@@ -551,15 +552,18 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
} }
if (var) if (var)
{ {
MarkStaticallyUsed(var); MarkActive(var);
} }
} }
void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type, void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
bool staticUse,
ShaderVariable *variableOut) const ShaderVariable *variableOut) const
{ {
ASSERT(variableOut); ASSERT(variableOut);
variableOut->staticUse = staticUse;
const TStructure *structure = type.getStruct(); const TStructure *structure = type.getStruct();
if (!structure) if (!structure)
{ {
...@@ -582,7 +586,7 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type, ...@@ -582,7 +586,7 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
// Regardless of the variable type (uniform, in/out etc.) its fields are always plain // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
// ShaderVariable objects. // ShaderVariable objects.
ShaderVariable fieldVariable; ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), &fieldVariable); setFieldProperties(*field->type(), field->name(), staticUse, &fieldVariable);
variableOut->fields.push_back(fieldVariable); variableOut->fields.push_back(fieldVariable);
} }
} }
...@@ -594,10 +598,11 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type, ...@@ -594,10 +598,11 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
void CollectVariablesTraverser::setFieldProperties(const TType &type, void CollectVariablesTraverser::setFieldProperties(const TType &type,
const ImmutableString &name, const ImmutableString &name,
bool staticUse,
ShaderVariable *variableOut) const ShaderVariable *variableOut) const
{ {
ASSERT(variableOut); ASSERT(variableOut);
setFieldOrVariableProperties(type, variableOut); setFieldOrVariableProperties(type, staticUse, variableOut);
variableOut->name.assign(name.data(), name.length()); variableOut->name.assign(name.data(), name.length());
variableOut->mappedName = HashName(name, mHashFunction, nullptr).data(); variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
} }
...@@ -608,7 +613,8 @@ void CollectVariablesTraverser::setCommonVariableProperties(const TType &type, ...@@ -608,7 +613,8 @@ void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
{ {
ASSERT(variableOut); ASSERT(variableOut);
setFieldOrVariableProperties(type, variableOut); variableOut->staticUse = mSymbolTable->isStaticallyUsed(variable);
setFieldOrVariableProperties(type, variableOut->staticUse, variableOut);
ASSERT(variable.symbolType() != SymbolType::Empty); ASSERT(variable.symbolType() != SymbolType::Empty);
variableOut->name.assign(variable.name().data(), variable.name().length()); variableOut->name.assign(variable.name().data(), variable.name().length());
variableOut->mappedName = getMappedName(&variable); variableOut->mappedName = getMappedName(&variable);
...@@ -684,6 +690,18 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName, ...@@ -684,6 +690,18 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
if (instanceName != nullptr) if (instanceName != nullptr)
{ {
interfaceBlock->instanceName = instanceName; interfaceBlock->instanceName = instanceName;
const TSymbol *blockSymbol = nullptr;
if (strncmp(instanceName, "gl_in", 5u) == 0)
{
blockSymbol = mSymbolTable->getGlInVariableWithArraySize();
}
else
{
blockSymbol = mSymbolTable->findGlobal(ImmutableString(instanceName));
}
ASSERT(blockSymbol && blockSymbol->isVariable());
interfaceBlock->staticUse =
mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(blockSymbol));
} }
ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9 ASSERT(!interfaceBlockType.isArrayOfArrays()); // Disallowed by GLSL ES 3.10 section 4.3.9
interfaceBlock->arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0; interfaceBlock->arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
...@@ -699,16 +717,36 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName, ...@@ -699,16 +717,36 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
} }
// Gather field information // Gather field information
bool anyFieldStaticallyUsed = false;
for (const TField *field : blockType->fields()) for (const TField *field : blockType->fields())
{ {
const TType &fieldType = *field->type(); const TType &fieldType = *field->type();
bool staticUse = false;
if (instanceName == nullptr)
{
// Static use of individual fields has been recorded, since they are present in the
// symbol table as variables.
const TSymbol *fieldSymbol = mSymbolTable->findGlobal(field->name());
ASSERT(fieldSymbol && fieldSymbol->isVariable());
staticUse =
mSymbolTable->isStaticallyUsed(*static_cast<const TVariable *>(fieldSymbol));
if (staticUse)
{
anyFieldStaticallyUsed = true;
}
}
InterfaceBlockField fieldVariable; InterfaceBlockField fieldVariable;
setFieldProperties(fieldType, field->name(), &fieldVariable); setFieldProperties(fieldType, field->name(), staticUse, &fieldVariable);
fieldVariable.isRowMajorLayout = fieldVariable.isRowMajorLayout =
(fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor); (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
interfaceBlock->fields.push_back(fieldVariable); interfaceBlock->fields.push_back(fieldVariable);
} }
if (anyFieldStaticallyUsed)
{
interfaceBlock->staticUse = true;
}
} }
Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
...@@ -826,7 +864,7 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode) ...@@ -826,7 +864,7 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
{ {
if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock) if (binaryNode->getOp() == EOpIndexDirectInterfaceBlock)
{ {
// NOTE: we do not determine static use for individual blocks of an array // NOTE: we do not determine static use / activeness for individual blocks of an array.
TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped(); TIntermTyped *blockNode = binaryNode->getLeft()->getAsTyped();
ASSERT(blockNode); ASSERT(blockNode);
...@@ -860,10 +898,13 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode) ...@@ -860,10 +898,13 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
namedBlock = findNamedInterfaceBlock(interfaceBlock->name()); namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
} }
ASSERT(namedBlock); ASSERT(namedBlock);
namedBlock->staticUse = true; ASSERT(namedBlock->staticUse);
namedBlock->active = true; namedBlock->active = true;
unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0)); unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
ASSERT(fieldIndex < namedBlock->fields.size()); ASSERT(fieldIndex < namedBlock->fields.size());
// TODO(oetuaho): Would be nicer to record static use of fields of named interface blocks
// more accurately at parse time - now we only mark the fields statically used if they are
// active. http://anglebug.com/2440
namedBlock->fields[fieldIndex].staticUse = true; namedBlock->fields[fieldIndex].staticUse = true;
namedBlock->fields[fieldIndex].active = true; namedBlock->fields[fieldIndex].active = true;
......
...@@ -219,8 +219,7 @@ TParseContext::TParseContext(TSymbolTable &symt, ...@@ -219,8 +219,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mGeometryShaderInvocations(0), mGeometryShaderInvocations(0),
mGeometryShaderMaxVertices(-1), mGeometryShaderMaxVertices(-1),
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations), mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices), mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices)
mGlInVariableWithArraySize(nullptr)
{ {
} }
...@@ -443,6 +442,36 @@ void TParseContext::checkPrecisionSpecified(const TSourceLoc &line, ...@@ -443,6 +442,36 @@ void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
} }
} }
void TParseContext::markStaticReadIfSymbol(TIntermNode *node)
{
TIntermSwizzle *swizzleNode = node->getAsSwizzleNode();
if (swizzleNode)
{
markStaticReadIfSymbol(swizzleNode->getOperand());
return;
}
TIntermBinary *binaryNode = node->getAsBinaryNode();
if (binaryNode)
{
switch (binaryNode->getOp())
{
case EOpIndexDirect:
case EOpIndexIndirect:
case EOpIndexDirectStruct:
case EOpIndexDirectInterfaceBlock:
markStaticReadIfSymbol(binaryNode->getLeft());
return;
default:
return;
}
}
TIntermSymbol *symbolNode = node->getAsSymbolNode();
if (symbolNode)
{
symbolTable.markStaticRead(symbolNode->variable());
}
}
// Both test and if necessary, spit out an error, to see if the node is really // Both test and if necessary, spit out an error, to see if the node is really
// an l-value that can be operated on this way. // an l-value that can be operated on this way.
bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node) bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIntermTyped *node)
...@@ -584,6 +613,7 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn ...@@ -584,6 +613,7 @@ bool TParseContext::checkCanBeLValue(const TSourceLoc &line, const char *op, TIn
TIntermSymbol *symNode = node->getAsSymbolNode(); TIntermSymbol *symNode = node->getAsSymbolNode();
if (message.empty() && symNode != nullptr) if (message.empty() && symNode != nullptr)
{ {
symbolTable.markStaticWrite(symNode->variable());
return true; return true;
} }
...@@ -689,6 +719,7 @@ bool TParseContext::checkConstructorArguments(const TSourceLoc &line, ...@@ -689,6 +719,7 @@ bool TParseContext::checkConstructorArguments(const TSourceLoc &line,
for (TIntermNode *arg : arguments) for (TIntermNode *arg : arguments)
{ {
markStaticReadIfSymbol(arg);
const TIntermTyped *argTyped = arg->getAsTyped(); const TIntermTyped *argTyped = arg->getAsTyped();
ASSERT(argTyped != nullptr); ASSERT(argTyped != nullptr);
if (type.getBasicType() != EbtStruct && IsOpaqueType(argTyped->getBasicType())) if (type.getBasicType() != EbtStruct && IsOpaqueType(argTyped->getBasicType()))
...@@ -1665,15 +1696,20 @@ void TParseContext::functionCallRValueLValueErrorCheck(const TFunction *fnCandid ...@@ -1665,15 +1696,20 @@ void TParseContext::functionCallRValueLValueErrorCheck(const TFunction *fnCandid
{ {
TQualifier qual = fnCandidate->getParam(i)->getType().getQualifier(); TQualifier qual = fnCandidate->getParam(i)->getType().getQualifier();
TIntermTyped *argument = (*(fnCall->getSequence()))[i]->getAsTyped(); TIntermTyped *argument = (*(fnCall->getSequence()))[i]->getAsTyped();
if (!IsImage(argument->getBasicType()) && (IsQualifierUnspecified(qual) || qual == EvqIn || bool argumentIsRead = (IsQualifierUnspecified(qual) || qual == EvqIn || qual == EvqInOut ||
qual == EvqInOut || qual == EvqConstReadOnly)) qual == EvqConstReadOnly);
if (argumentIsRead)
{ {
if (argument->getMemoryQualifier().writeonly) markStaticReadIfSymbol(argument);
if (!IsImage(argument->getBasicType()))
{ {
error(argument->getLine(), if (argument->getMemoryQualifier().writeonly)
"Writeonly value cannot be passed for 'in' or 'inout' parameters.", {
fnCall->functionName()); error(argument->getLine(),
return; "Writeonly value cannot be passed for 'in' or 'inout' parameters.",
fnCall->functionName());
return;
}
} }
} }
if (qual == EvqOut || qual == EvqInOut) if (qual == EvqOut || qual == EvqInOut)
...@@ -1878,8 +1914,8 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location, ...@@ -1878,8 +1914,8 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
else if ((mGeometryShaderInputPrimitiveType != EptUndefined) && else if ((mGeometryShaderInputPrimitiveType != EptUndefined) &&
(variableType.getQualifier() == EvqPerVertexIn)) (variableType.getQualifier() == EvqPerVertexIn))
{ {
ASSERT(mGlInVariableWithArraySize != nullptr); ASSERT(symbolTable.getGlInVariableWithArraySize() != nullptr);
node = new TIntermSymbol(mGlInVariableWithArraySize); node = new TIntermSymbol(symbolTable.getGlInVariableWithArraySize());
} }
else else
{ {
...@@ -1994,6 +2030,7 @@ bool TParseContext::executeInitializer(const TSourceLoc &line, ...@@ -1994,6 +2030,7 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
} }
*initNode = new TIntermBinary(EOpInitialize, intermSymbol, initializer); *initNode = new TIntermBinary(EOpInitialize, intermSymbol, initializer);
markStaticReadIfSymbol(initializer);
(*initNode)->setLine(line); (*initNode)->setLine(line);
return true; return true;
} }
...@@ -2036,8 +2073,19 @@ TIntermNode *TParseContext::addLoop(TLoopType type, ...@@ -2036,8 +2073,19 @@ TIntermNode *TParseContext::addLoop(TLoopType type,
TIntermTyped *typedCond = nullptr; TIntermTyped *typedCond = nullptr;
if (cond) if (cond)
{ {
markStaticReadIfSymbol(cond);
typedCond = cond->getAsTyped(); typedCond = cond->getAsTyped();
} }
if (expr)
{
markStaticReadIfSymbol(expr);
}
// In case the loop body was not parsed as a block and contains a statement that simply refers
// to a variable, we need to mark it as statically used.
if (body)
{
markStaticReadIfSymbol(body);
}
if (cond == nullptr || typedCond) if (cond == nullptr || typedCond)
{ {
if (type == ELoopDoWhile) if (type == ELoopDoWhile)
...@@ -2084,6 +2132,16 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond, ...@@ -2084,6 +2132,16 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond,
const TSourceLoc &loc) const TSourceLoc &loc)
{ {
bool isScalarBool = checkIsScalarBool(loc, cond); bool isScalarBool = checkIsScalarBool(loc, cond);
// In case the conditional statements were not parsed as blocks and contain a statement that
// simply refers to a variable, we need to mark them as statically used.
if (code.node1)
{
markStaticReadIfSymbol(code.node1);
}
if (code.node2)
{
markStaticReadIfSymbol(code.node2);
}
// For compile time constant conditions, prune the code now. // For compile time constant conditions, prune the code now.
if (isScalarBool && cond->getAsConstantUnion()) if (isScalarBool && cond->getAsConstantUnion())
...@@ -2099,6 +2157,7 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond, ...@@ -2099,6 +2157,7 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond,
} }
TIntermIfElse *node = new TIntermIfElse(cond, EnsureBlock(code.node1), EnsureBlock(code.node2)); TIntermIfElse *node = new TIntermIfElse(cond, EnsureBlock(code.node1), EnsureBlock(code.node2));
markStaticReadIfSymbol(cond);
node->setLine(loc); node->setLine(loc);
return node; return node;
...@@ -2346,9 +2405,9 @@ void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &lo ...@@ -2346,9 +2405,9 @@ void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &lo
// input primitive declaration. // input primitive declaration.
if (mGeometryShaderInputPrimitiveType != EptUndefined) if (mGeometryShaderInputPrimitiveType != EptUndefined)
{ {
ASSERT(mGlInVariableWithArraySize != nullptr); ASSERT(symbolTable.getGlInVariableWithArraySize() != nullptr);
type->sizeOutermostUnsizedArray( type->sizeOutermostUnsizedArray(
mGlInVariableWithArraySize->getType().getOutermostArraySize()); symbolTable.getGlInVariableWithArraySize()->getType().getOutermostArraySize());
} }
else else
{ {
...@@ -2818,17 +2877,7 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier ...@@ -2818,17 +2877,7 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier
void TParseContext::setGeometryShaderInputArraySize(unsigned int inputArraySize, void TParseContext::setGeometryShaderInputArraySize(unsigned int inputArraySize,
const TSourceLoc &line) const TSourceLoc &line)
{ {
if (mGlInVariableWithArraySize == nullptr) if (!symbolTable.setGlInArraySize(inputArraySize))
{
const TSymbol *glPerVertex = symbolTable.findBuiltIn(ImmutableString("gl_PerVertex"), 310);
const TInterfaceBlock *glPerVertexBlock = static_cast<const TInterfaceBlock *>(glPerVertex);
TType *glInType = new TType(glPerVertexBlock, EvqPerVertexIn, TLayoutQualifier::Create());
glInType->makeArray(inputArraySize);
mGlInVariableWithArraySize =
new TVariable(&symbolTable, ImmutableString("gl_in"), glInType, SymbolType::BuiltIn,
TExtension::EXT_geometry_shader);
}
else if (mGlInVariableWithArraySize->getType().getOutermostArraySize() != inputArraySize)
{ {
error(line, error(line,
"Array size or input primitive declaration doesn't match the size of earlier sized " "Array size or input primitive declaration doesn't match the size of earlier sized "
...@@ -4010,6 +4059,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression, ...@@ -4010,6 +4059,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
} }
} }
markStaticReadIfSymbol(indexExpression);
TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression); TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression);
node->setLine(location); node->setLine(location);
// Indirect indexing can never be constant folded. // Indirect indexing can never be constant folded.
...@@ -4793,6 +4843,7 @@ TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init, ...@@ -4793,6 +4843,7 @@ TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
return nullptr; return nullptr;
} }
markStaticReadIfSymbol(init);
TIntermSwitch *node = new TIntermSwitch(init, statementList); TIntermSwitch *node = new TIntermSwitch(init, statementList);
node->setLine(loc); node->setLine(loc);
return node; return node;
...@@ -4889,6 +4940,7 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op, ...@@ -4889,6 +4940,7 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
return nullptr; return nullptr;
} }
markStaticReadIfSymbol(child);
TIntermUnary *node = new TIntermUnary(op, child); TIntermUnary *node = new TIntermUnary(op, child);
node->setLine(loc); node->setLine(loc);
...@@ -5292,6 +5344,9 @@ TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op, ...@@ -5292,6 +5344,9 @@ TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op,
} }
TIntermBinary *node = new TIntermBinary(op, left, right); TIntermBinary *node = new TIntermBinary(op, left, right);
ASSERT(op != EOpAssign);
markStaticReadIfSymbol(left);
markStaticReadIfSymbol(right);
node->setLine(loc); node->setLine(loc);
return expressionOrFoldedResult(node); return expressionOrFoldedResult(node);
} }
...@@ -5354,6 +5409,11 @@ TIntermTyped *TParseContext::addAssign(TOperator op, ...@@ -5354,6 +5409,11 @@ TIntermTyped *TParseContext::addAssign(TOperator op,
assignError(loc, "assign", left->getCompleteString(), right->getCompleteString()); assignError(loc, "assign", left->getCompleteString(), right->getCompleteString());
return left; return left;
} }
if (op != EOpAssign)
{
markStaticReadIfSymbol(left);
}
markStaticReadIfSymbol(right);
node->setLine(loc); node->setLine(loc);
return node; return node;
} }
...@@ -5375,6 +5435,9 @@ TIntermTyped *TParseContext::addComma(TIntermTyped *left, ...@@ -5375,6 +5435,9 @@ TIntermTyped *TParseContext::addComma(TIntermTyped *left,
} }
TIntermBinary *commaNode = TIntermBinary::CreateComma(left, right, mShaderVersion); TIntermBinary *commaNode = TIntermBinary::CreateComma(left, right, mShaderVersion);
markStaticReadIfSymbol(left);
markStaticReadIfSymbol(right);
commaNode->setLine(loc);
return expressionOrFoldedResult(commaNode); return expressionOrFoldedResult(commaNode);
} }
...@@ -5420,6 +5483,7 @@ TIntermBranch *TParseContext::addBranch(TOperator op, ...@@ -5420,6 +5483,7 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
{ {
if (expression != nullptr) if (expression != nullptr)
{ {
markStaticReadIfSymbol(expression);
ASSERT(op == EOpReturn); ASSERT(op == EOpReturn);
mFunctionReturnsValue = true; mFunctionReturnsValue = true;
if (mCurrentFunctionType->getBasicType() == EbtVoid) if (mCurrentFunctionType->getBasicType() == EbtVoid)
...@@ -5436,6 +5500,15 @@ TIntermBranch *TParseContext::addBranch(TOperator op, ...@@ -5436,6 +5500,15 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
return node; return node;
} }
void TParseContext::appendStatement(TIntermBlock *block, TIntermNode *statement)
{
if (statement != nullptr)
{
markStaticReadIfSymbol(statement);
block->appendStatement(statement);
}
}
void TParseContext::checkTextureGather(TIntermAggregate *functionCall) void TParseContext::checkTextureGather(TIntermAggregate *functionCall)
{ {
ASSERT(functionCall->getOp() == EOpCallBuiltInFunction); ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
...@@ -5895,6 +5968,9 @@ TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond, ...@@ -5895,6 +5968,9 @@ TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
} }
TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression); TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
markStaticReadIfSymbol(cond);
markStaticReadIfSymbol(trueExpression);
markStaticReadIfSymbol(falseExpression);
node->setLine(loc); node->setLine(loc);
return expressionOrFoldedResult(node); return expressionOrFoldedResult(node);
} }
......
...@@ -419,6 +419,8 @@ class TParseContext : angle::NonCopyable ...@@ -419,6 +419,8 @@ class TParseContext : angle::NonCopyable
TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc); TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc);
void appendStatement(TIntermBlock *block, TIntermNode *statement);
void checkTextureGather(TIntermAggregate *functionCall); void checkTextureGather(TIntermAggregate *functionCall);
void checkTextureOffsetConst(TIntermAggregate *functionCall); void checkTextureOffsetConst(TIntermAggregate *functionCall);
void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall); void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
...@@ -464,6 +466,8 @@ class TParseContext : angle::NonCopyable ...@@ -464,6 +466,8 @@ class TParseContext : angle::NonCopyable
// Note that there may be tests in AtomicCounter_test that will need to be updated as well. // Note that there may be tests in AtomicCounter_test that will need to be updated as well.
constexpr static size_t kAtomicCounterArrayStride = 4; constexpr static size_t kAtomicCounterArrayStride = 4;
void markStaticReadIfSymbol(TIntermNode *node);
// Returns a clamped index. If it prints out an error message, the token is "[]". // Returns a clamped index. If it prints out an error message, the token is "[]".
int checkIndexLessThan(bool outOfRangeIndexIsError, int checkIndexLessThan(bool outOfRangeIndexIsError,
const TSourceLoc &location, const TSourceLoc &location,
...@@ -638,10 +642,6 @@ class TParseContext : angle::NonCopyable ...@@ -638,10 +642,6 @@ class TParseContext : angle::NonCopyable
int mGeometryShaderMaxVertices; int mGeometryShaderMaxVertices;
int mMaxGeometryShaderInvocations; int mMaxGeometryShaderInvocations;
int mMaxGeometryShaderMaxVertices; int mMaxGeometryShaderMaxVertices;
// Store gl_in variable with its array size once the array size can be determined. The array
// size can also be checked against latter input primitive type declaration.
const TVariable *mGlInVariableWithArraySize;
}; };
int PaParseStrings(size_t count, int PaParseStrings(size_t count,
......
...@@ -79,7 +79,8 @@ TSymbol *TSymbolTable::TSymbolTableLevel::find(const ImmutableString &name) cons ...@@ -79,7 +79,8 @@ TSymbol *TSymbolTable::TSymbolTableLevel::find(const ImmutableString &name) cons
return (*it).second; return (*it).second;
} }
TSymbolTable::TSymbolTable() : mUniqueIdCounter(0), mShaderType(GL_FRAGMENT_SHADER) TSymbolTable::TSymbolTable()
: mUniqueIdCounter(0), mShaderType(GL_FRAGMENT_SHADER), mGlInVariableWithArraySize(nullptr)
{ {
} }
...@@ -137,6 +138,56 @@ const TFunction *TSymbolTable::setFunctionParameterNamesFromDefinition(const TFu ...@@ -137,6 +138,56 @@ const TFunction *TSymbolTable::setFunctionParameterNamesFromDefinition(const TFu
return firstDeclaration; return firstDeclaration;
} }
bool TSymbolTable::setGlInArraySize(unsigned int inputArraySize)
{
if (mGlInVariableWithArraySize)
{
return mGlInVariableWithArraySize->getType().getOutermostArraySize() == inputArraySize;
}
const TInterfaceBlock *glPerVertex = mVar_gl_PerVertex;
TType *glInType = new TType(glPerVertex, EvqPerVertexIn, TLayoutQualifier::Create());
glInType->makeArray(inputArraySize);
mGlInVariableWithArraySize =
new TVariable(this, ImmutableString("gl_in"), glInType, SymbolType::BuiltIn,
TExtension::EXT_geometry_shader);
return true;
}
TVariable *TSymbolTable::getGlInVariableWithArraySize() const
{
return mGlInVariableWithArraySize;
}
void TSymbolTable::markStaticWrite(const TVariable &variable)
{
int id = variable.uniqueId().get();
auto iter = mVariableMetadata.find(id);
if (iter == mVariableMetadata.end())
{
iter = mVariableMetadata.insert(std::make_pair(id, VariableMetadata())).first;
}
iter->second.staticWrite = true;
}
void TSymbolTable::markStaticRead(const TVariable &variable)
{
int id = variable.uniqueId().get();
auto iter = mVariableMetadata.find(id);
if (iter == mVariableMetadata.end())
{
iter = mVariableMetadata.insert(std::make_pair(id, VariableMetadata())).first;
}
iter->second.staticRead = true;
}
bool TSymbolTable::isStaticallyUsed(const TVariable &variable) const
{
ASSERT(!variable.getConstPointer());
int id = variable.uniqueId().get();
auto iter = mVariableMetadata.find(id);
return iter != mVariableMetadata.end();
}
const TSymbol *TSymbolTable::find(const ImmutableString &name, int shaderVersion) const const TSymbol *TSymbolTable::find(const ImmutableString &name, int shaderVersion) const
{ {
int userDefinedLevel = static_cast<int>(mTable.size()) - 1; int userDefinedLevel = static_cast<int>(mTable.size()) - 1;
...@@ -238,6 +289,8 @@ void TSymbolTable::setGlobalInvariant(bool invariant) ...@@ -238,6 +289,8 @@ void TSymbolTable::setGlobalInvariant(bool invariant)
void TSymbolTable::clearCompilationResults() void TSymbolTable::clearCompilationResults()
{ {
mUniqueIdCounter = kLastBuiltInId + 1; mUniqueIdCounter = kLastBuiltInId + 1;
mVariableMetadata.clear();
mGlInVariableWithArraySize = nullptr;
// User-defined scopes should have already been cleared when the compilation finished. // User-defined scopes should have already been cleared when the compilation finished.
ASSERT(mTable.size() == 0u); ASSERT(mTable.size() == 0u);
...@@ -297,4 +350,8 @@ void TSymbolTable::initSamplerDefaultPrecision(TBasicType samplerType) ...@@ -297,4 +350,8 @@ void TSymbolTable::initSamplerDefaultPrecision(TBasicType samplerType)
setDefaultPrecision(samplerType, EbpLow); setDefaultPrecision(samplerType, EbpLow);
} }
TSymbolTable::VariableMetadata::VariableMetadata() : staticRead(false), staticWrite(false)
{
}
} // namespace sh } // namespace sh
...@@ -90,6 +90,16 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase ...@@ -90,6 +90,16 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
const TFunction *setFunctionParameterNamesFromDefinition(const TFunction *function, const TFunction *setFunctionParameterNamesFromDefinition(const TFunction *function,
bool *wasDefinedOut); bool *wasDefinedOut);
// Return false if the gl_in array size has already been initialized with a mismatching value.
bool setGlInArraySize(unsigned int inputArraySize);
TVariable *getGlInVariableWithArraySize() const;
void markStaticRead(const TVariable &variable);
void markStaticWrite(const TVariable &variable);
// Note: Should not call this for constant variables.
bool isStaticallyUsed(const TVariable &variable) const;
// find() is guaranteed not to retain a reference to the ImmutableString, so an ImmutableString // find() is guaranteed not to retain a reference to the ImmutableString, so an ImmutableString
// with a reference to a short-lived char * is fine to pass here. // with a reference to a short-lived char * is fine to pass here.
const TSymbol *find(const ImmutableString &name, int shaderVersion) const; const TSymbol *find(const ImmutableString &name, int shaderVersion) const;
...@@ -154,6 +164,20 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase ...@@ -154,6 +164,20 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
sh::GLenum mShaderType; sh::GLenum mShaderType;
ShBuiltInResources mResources; ShBuiltInResources mResources;
struct VariableMetadata
{
VariableMetadata();
bool staticRead;
bool staticWrite;
};
// Indexed by unique id. Map instead of vector since the variables are fairly sparse.
std::map<int, VariableMetadata> mVariableMetadata;
// Store gl_in variable with its array size once the array size can be determined. The array
// size can also be checked against latter input primitive type declaration.
TVariable *mGlInVariableWithArraySize;
}; };
} // namespace sh } // namespace sh
......
...@@ -1304,11 +1304,11 @@ compound_statement_no_new_scope ...@@ -1304,11 +1304,11 @@ compound_statement_no_new_scope
statement_list statement_list
: statement { : statement {
$$ = new TIntermBlock(); $$ = new TIntermBlock();
$$->appendStatement($1); context->appendStatement($$, $1);
} }
| statement_list statement { | statement_list statement {
$$ = $1; $$ = $1;
$$->appendStatement($2); context->appendStatement($$, $2);
} }
; ;
......
...@@ -4630,7 +4630,7 @@ yyreduce: ...@@ -4630,7 +4630,7 @@ yyreduce:
{ {
(yyval.interm.intermBlock) = new TIntermBlock(); (yyval.interm.intermBlock) = new TIntermBlock();
(yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode)); context->appendStatement((yyval.interm.intermBlock), (yyvsp[0].interm.intermNode));
} }
break; break;
...@@ -4639,7 +4639,7 @@ yyreduce: ...@@ -4639,7 +4639,7 @@ yyreduce:
{ {
(yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock); (yyval.interm.intermBlock) = (yyvsp[-1].interm.intermBlock);
(yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode)); context->appendStatement((yyval.interm.intermBlock), (yyvsp[0].interm.intermNode));
} }
break; break;
......
...@@ -292,13 +292,13 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog, ...@@ -292,13 +292,13 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const sh::Varying *output = ref.second.fragment; const sh::Varying *output = ref.second.fragment;
// Only pack statically used varyings that have a matched input or output, plus special // Only pack statically used varyings that have a matched input or output, plus special
// builtins. Note that we pack all statically used varyings even if they are not active. // builtins. Note that we pack all statically used user-defined varyings even if they are
// GLES specs are a bit vague on whether it's allowed to only pack active varyings, though // not active. GLES specs are a bit vague on whether it's allowed to only pack active
// GLES 3.1 spec section 11.1.2.1 says that "device-dependent optimizations" may be used to // varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
// make vertex shader outputs fit. // optimizations" may be used to make vertex shader outputs fit.
if ((input && output && output->staticUse) || if ((input && output && output->staticUse) ||
(input && input->isBuiltIn() && input->staticUse) || (input && input->isBuiltIn() && input->active) ||
(output && output->isBuiltIn() && output->staticUse)) (output && output->isBuiltIn() && output->active))
{ {
const sh::Varying *varying = output ? output : input; const sh::Varying *varying = output ? output : input;
......
...@@ -793,8 +793,8 @@ void DynamicHLSL::generateShaderLinkHLSL(const gl::Context *context, ...@@ -793,8 +793,8 @@ void DynamicHLSL::generateShaderLinkHLSL(const gl::Context *context,
ASSERT(!varying.isBuiltIn() && !varying.isStruct()); ASSERT(!varying.isBuiltIn() && !varying.isStruct());
// Don't reference VS-only transform feedback varyings in the PS. Note that we're relying on // Don't reference VS-only transform feedback varyings in the PS. Note that we're relying on
// that the staticUse flag is set according to usage in the fragment shader. // that the active flag is set according to usage in the fragment shader.
if (packedVarying.vertexOnly || !varying.staticUse) if (packedVarying.vertexOnly || !varying.active)
continue; continue;
pixelStream << " "; pixelStream << " ";
......
...@@ -113,6 +113,7 @@ class CollectVariablesTest : public testing::Test ...@@ -113,6 +113,7 @@ class CollectVariablesTest : public testing::Test
const OutputVariable &outputVariable = outputVariables[varIndex]; const OutputVariable &outputVariable = outputVariables[varIndex];
EXPECT_EQ(-1, outputVariable.location); EXPECT_EQ(-1, outputVariable.location);
EXPECT_TRUE(outputVariable.staticUse); EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_EQ(varName, outputVariable.name); EXPECT_EQ(varName, outputVariable.name);
*outResult = &outputVariable; *outResult = &outputVariable;
} }
...@@ -125,6 +126,17 @@ class CollectVariablesTest : public testing::Test ...@@ -125,6 +126,17 @@ class CollectVariablesTest : public testing::Test
void compile(const std::string &shaderString) { compile(shaderString, 0u); } void compile(const std::string &shaderString) { compile(shaderString, 0u); }
void checkUniformStaticallyUsedButNotActive(const char *name)
{
const auto &uniforms = mTranslator->getUniforms();
ASSERT_EQ(1u, uniforms.size());
const Uniform &uniform = uniforms[0];
EXPECT_EQ(name, uniform.name);
EXPECT_TRUE(uniform.staticUse);
EXPECT_FALSE(uniform.active);
}
::GLenum mShaderType; ::GLenum mShaderType;
std::unique_ptr<TranslatorGLSL> mTranslator; std::unique_ptr<TranslatorGLSL> mTranslator;
}; };
...@@ -247,6 +259,7 @@ TEST_F(CollectFragmentVariablesTest, SimpleOutputVar) ...@@ -247,6 +259,7 @@ TEST_F(CollectFragmentVariablesTest, SimpleOutputVar)
EXPECT_EQ(-1, outputVariable.location); EXPECT_EQ(-1, outputVariable.location);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
EXPECT_TRUE(outputVariable.staticUse); EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
EXPECT_EQ("out_fragColor", outputVariable.name); EXPECT_EQ("out_fragColor", outputVariable.name);
} }
...@@ -272,6 +285,7 @@ TEST_F(CollectFragmentVariablesTest, LocationOutputVar) ...@@ -272,6 +285,7 @@ TEST_F(CollectFragmentVariablesTest, LocationOutputVar)
EXPECT_EQ(5, outputVariable.location); EXPECT_EQ(5, outputVariable.location);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
EXPECT_TRUE(outputVariable.staticUse); EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
EXPECT_EQ("out_fragColor", outputVariable.name); EXPECT_EQ("out_fragColor", outputVariable.name);
} }
...@@ -296,6 +310,7 @@ TEST_F(CollectVertexVariablesTest, LocationAttribute) ...@@ -296,6 +310,7 @@ TEST_F(CollectVertexVariablesTest, LocationAttribute)
EXPECT_EQ(5, attribute.location); EXPECT_EQ(5, attribute.location);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, attribute.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, attribute.precision);
EXPECT_TRUE(attribute.staticUse); EXPECT_TRUE(attribute.staticUse);
EXPECT_TRUE(attribute.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, attribute.type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, attribute.type);
EXPECT_EQ("in_Position", attribute.name); EXPECT_EQ("in_Position", attribute.name);
} }
...@@ -322,6 +337,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock) ...@@ -322,6 +337,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout); EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout);
EXPECT_EQ("b", interfaceBlock.name); EXPECT_EQ("b", interfaceBlock.name);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
...@@ -329,6 +345,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock) ...@@ -329,6 +345,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type); EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("f", field.name); EXPECT_EQ("f", field.name);
EXPECT_FALSE(field.isRowMajorLayout); EXPECT_FALSE(field.isRowMajorLayout);
...@@ -358,6 +375,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) ...@@ -358,6 +375,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name); EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ("blockInstance", interfaceBlock.instanceName); EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
...@@ -365,6 +383,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock) ...@@ -365,6 +383,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type); EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("f", field.name); EXPECT_EQ("f", field.name);
EXPECT_FALSE(field.isRowMajorLayout); EXPECT_FALSE(field.isRowMajorLayout);
...@@ -395,25 +414,27 @@ TEST_F(CollectVertexVariablesTest, StructInterfaceBlock) ...@@ -395,25 +414,27 @@ TEST_F(CollectVertexVariablesTest, StructInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name); EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName); EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0]; const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct()); EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(blockField.staticUse);
EXPECT_EQ("s", field.name); EXPECT_TRUE(blockField.active);
EXPECT_EQ(DecorateName("s"), field.mappedName); EXPECT_EQ("s", blockField.name);
EXPECT_FALSE(field.isRowMajorLayout); EXPECT_EQ(DecorateName("s"), blockField.mappedName);
EXPECT_FALSE(blockField.isRowMajorLayout);
const ShaderVariable &member = field.fields[0]; const ShaderVariable &structField = blockField.fields[0];
// NOTE: we don't currently mark struct members as statically used or not // NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_FALSE(member.isStruct()); EXPECT_FALSE(structField.isStruct());
EXPECT_EQ("f", member.name); EXPECT_EQ("f", structField.name);
EXPECT_EQ(DecorateName("f"), member.mappedName); EXPECT_EQ(DecorateName("f"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, member.type); EXPECT_GLENUM_EQ(GL_FLOAT, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
} }
TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock)
...@@ -441,25 +462,27 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock) ...@@ -441,25 +462,27 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock)
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName); EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_EQ("instanceName", interfaceBlock.instanceName); EXPECT_EQ("instanceName", interfaceBlock.instanceName);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0]; const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct()); EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(blockField.staticUse);
EXPECT_EQ("s", field.name); EXPECT_TRUE(blockField.active);
EXPECT_EQ(DecorateName("s"), field.mappedName); EXPECT_EQ("s", blockField.name);
EXPECT_FALSE(field.isRowMajorLayout); EXPECT_EQ(DecorateName("s"), blockField.mappedName);
EXPECT_FALSE(blockField.isRowMajorLayout);
const ShaderVariable &member = field.fields[0]; const ShaderVariable &structField = blockField.fields[0];
// NOTE: we don't currently mark struct members as statically used or not // NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_FALSE(member.isStruct()); EXPECT_FALSE(structField.isStruct());
EXPECT_EQ("f", member.name); EXPECT_EQ("f", structField.name);
EXPECT_EQ(DecorateName("f"), member.mappedName); EXPECT_EQ(DecorateName("f"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, member.type); EXPECT_GLENUM_EQ(GL_FLOAT, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
} }
TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock) TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock)
...@@ -486,25 +509,27 @@ TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock) ...@@ -486,25 +509,27 @@ TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name); EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName); EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0]; const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct()); EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(blockField.staticUse);
EXPECT_EQ("s", field.name); EXPECT_TRUE(blockField.active);
EXPECT_EQ(DecorateName("s"), field.mappedName); EXPECT_EQ("s", blockField.name);
EXPECT_TRUE(field.isRowMajorLayout); EXPECT_EQ(DecorateName("s"), blockField.mappedName);
EXPECT_TRUE(blockField.isRowMajorLayout);
const ShaderVariable &member = field.fields[0];
const ShaderVariable &structField = blockField.fields[0];
// NOTE: we don't currently mark struct members as statically used or not
EXPECT_FALSE(member.isStruct()); // NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_EQ("m", member.name); EXPECT_FALSE(structField.isStruct());
EXPECT_EQ(DecorateName("m"), member.mappedName); EXPECT_EQ("m", structField.name);
EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, member.type); EXPECT_EQ(DecorateName("m"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision); EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
} }
TEST_F(CollectVertexVariablesTest, VaryingInterpolation) TEST_F(CollectVertexVariablesTest, VaryingInterpolation)
...@@ -533,6 +558,7 @@ TEST_F(CollectVertexVariablesTest, VaryingInterpolation) ...@@ -533,6 +558,7 @@ TEST_F(CollectVertexVariablesTest, VaryingInterpolation)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, varying->precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, varying->precision);
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_GLENUM_EQ(GL_FLOAT, varying->type); EXPECT_GLENUM_EQ(GL_FLOAT, varying->type);
EXPECT_EQ("vary", varying->name); EXPECT_EQ("vary", varying->name);
EXPECT_EQ(DecorateName("vary"), varying->mappedName); EXPECT_EQ(DecorateName("vary"), varying->mappedName);
...@@ -795,6 +821,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock) ...@@ -795,6 +821,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock)
EXPECT_EQ("blockInstance", interfaceBlock.instanceName); EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
EXPECT_EQ("webgl_9", interfaceBlock.mappedName); EXPECT_EQ("webgl_9", interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse); EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size()); ASSERT_EQ(1u, interfaceBlock.fields.size());
...@@ -802,6 +829,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock) ...@@ -802,6 +829,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse); EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type); EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("field", field.name); EXPECT_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName); EXPECT_EQ("webgl_5", field.mappedName);
...@@ -837,13 +865,17 @@ TEST_F(CollectHashedVertexVariablesTest, StructUniform) ...@@ -837,13 +865,17 @@ TEST_F(CollectHashedVertexVariablesTest, StructUniform)
EXPECT_EQ("webgl_1", uniform.mappedName); EXPECT_EQ("webgl_1", uniform.mappedName);
EXPECT_EQ("sType", uniform.structName); EXPECT_EQ("sType", uniform.structName);
EXPECT_TRUE(uniform.staticUse); EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
ASSERT_EQ(1u, uniform.fields.size()); ASSERT_EQ(1u, uniform.fields.size());
const ShaderVariable &field = uniform.fields[0]; const ShaderVariable &field = uniform.fields[0];
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
// EXPECT_TRUE(field.staticUse); // we don't yet support struct static use // We don't yet support tracking static use per field, but fields are marked statically used in
// case the struct is.
EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type); EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("field", field.name); EXPECT_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName); EXPECT_EQ("webgl_5", field.mappedName);
...@@ -877,13 +909,17 @@ TEST_F(CollectHashedVertexVariablesTest, NamelessStructUniform) ...@@ -877,13 +909,17 @@ TEST_F(CollectHashedVertexVariablesTest, NamelessStructUniform)
EXPECT_EQ("webgl_1", uniform.mappedName); EXPECT_EQ("webgl_1", uniform.mappedName);
EXPECT_EQ("", uniform.structName); EXPECT_EQ("", uniform.structName);
EXPECT_TRUE(uniform.staticUse); EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
ASSERT_EQ(1u, uniform.fields.size()); ASSERT_EQ(1u, uniform.fields.size());
const ShaderVariable &field = uniform.fields[0]; const ShaderVariable &field = uniform.fields[0];
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
// EXPECT_TRUE(field.staticUse); // we don't yet support struct static use // We don't yet support tracking static use per field, but fields are marked statically used in
// case the struct is.
EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type); EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("field", field.name); EXPECT_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName); EXPECT_EQ("webgl_5", field.mappedName);
...@@ -913,6 +949,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration) ...@@ -913,6 +949,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration)
EXPECT_FALSE(uniform.isArray()); EXPECT_FALSE(uniform.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniform.precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniform.precision);
EXPECT_TRUE(uniform.staticUse); EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniform.type); EXPECT_GLENUM_EQ(GL_FLOAT, uniform.type);
EXPECT_EQ("uA", uniform.name); EXPECT_EQ("uA", uniform.name);
...@@ -920,6 +957,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration) ...@@ -920,6 +957,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration)
EXPECT_FALSE(uniformB.isArray()); EXPECT_FALSE(uniformB.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision);
EXPECT_TRUE(uniformB.staticUse); EXPECT_TRUE(uniformB.staticUse);
EXPECT_TRUE(uniformB.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type); EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type);
EXPECT_EQ("uB", uniformB.name); EXPECT_EQ("uB", uniformB.name);
} }
...@@ -946,6 +984,7 @@ TEST_F(CollectFragmentVariablesTest, EmptyDeclarator) ...@@ -946,6 +984,7 @@ TEST_F(CollectFragmentVariablesTest, EmptyDeclarator)
EXPECT_FALSE(uniformB.isArray()); EXPECT_FALSE(uniformB.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision); EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision);
EXPECT_TRUE(uniformB.staticUse); EXPECT_TRUE(uniformB.staticUse);
EXPECT_TRUE(uniformB.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type); EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type);
EXPECT_EQ("uB", uniformB.name); EXPECT_EQ("uB", uniformB.name);
} }
...@@ -1008,6 +1047,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields) ...@@ -1008,6 +1047,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields)
EXPECT_EQ("gl_PerVertex", inBlock->name); EXPECT_EQ("gl_PerVertex", inBlock->name);
EXPECT_EQ("gl_in", inBlock->instanceName); EXPECT_EQ("gl_in", inBlock->instanceName);
EXPECT_TRUE(inBlock->staticUse); EXPECT_TRUE(inBlock->staticUse);
EXPECT_TRUE(inBlock->active);
EXPECT_TRUE(inBlock->isBuiltIn()); EXPECT_TRUE(inBlock->isBuiltIn());
ASSERT_EQ(1u, inBlock->fields.size()); ASSERT_EQ(1u, inBlock->fields.size());
...@@ -1017,6 +1057,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields) ...@@ -1017,6 +1057,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields)
EXPECT_FALSE(glPositionField.isArray()); EXPECT_FALSE(glPositionField.isArray());
EXPECT_FALSE(glPositionField.isStruct()); EXPECT_FALSE(glPositionField.isStruct());
EXPECT_TRUE(glPositionField.staticUse); EXPECT_TRUE(glPositionField.staticUse);
EXPECT_TRUE(glPositionField.active);
EXPECT_TRUE(glPositionField.isBuiltIn()); EXPECT_TRUE(glPositionField.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, glPositionField.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, glPositionField.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, glPositionField.type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, glPositionField.type);
...@@ -1076,6 +1117,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveIDIn) ...@@ -1076,6 +1117,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveIDIn)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1108,6 +1150,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInvocationID) ...@@ -1108,6 +1150,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInvocationID)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1169,6 +1212,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPosition) ...@@ -1169,6 +1212,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPosition)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying->type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying->type);
...@@ -1200,6 +1244,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveID) ...@@ -1200,6 +1244,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveID)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1231,6 +1276,7 @@ TEST_F(CollectGeometryVariablesTest, CollectLayer) ...@@ -1231,6 +1276,7 @@ TEST_F(CollectGeometryVariablesTest, CollectLayer)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1262,6 +1308,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectPrimitiveID) ...@@ -1262,6 +1308,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectPrimitiveID)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1293,6 +1340,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectLayer) ...@@ -1293,6 +1340,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectLayer)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct()); EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn()); EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type); EXPECT_GLENUM_EQ(GL_INT, varying->type);
...@@ -1385,6 +1433,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInputs) ...@@ -1385,6 +1433,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInputs)
EXPECT_TRUE(varying.isArray()); EXPECT_TRUE(varying.isArray());
EXPECT_FALSE(varying.isStruct()); EXPECT_FALSE(varying.isStruct());
EXPECT_TRUE(varying.staticUse); EXPECT_TRUE(varying.staticUse);
EXPECT_TRUE(varying.active);
EXPECT_FALSE(varying.isBuiltIn()); EXPECT_FALSE(varying.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying.precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying.type); EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying.type);
...@@ -1548,8 +1597,442 @@ TEST_F(CollectFragmentVariablesTest, VaryingUsedInsideFoldedTernary) ...@@ -1548,8 +1597,442 @@ TEST_F(CollectFragmentVariablesTest, VaryingUsedInsideFoldedTernary)
EXPECT_FALSE(varying->isArray()); EXPECT_FALSE(varying->isArray());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision); EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision);
EXPECT_TRUE(varying->staticUse); EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_GLENUM_EQ(GL_FLOAT, varying->type); EXPECT_GLENUM_EQ(GL_FLOAT, varying->type);
EXPECT_EQ("vary", varying->name); EXPECT_EQ("vary", varying->name);
EXPECT_EQ(DecorateName("vary"), varying->mappedName); EXPECT_EQ(DecorateName("vary"), varying->mappedName);
EXPECT_EQ(INTERPOLATION_CENTROID, varying->interpolation); EXPECT_EQ(INTERPOLATION_CENTROID, varying->interpolation);
} }
// Test a variable that is statically used but not active. The variable is used in a branch of a
// ternary op that is not evaluated.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveInTernaryOp)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform float u;
void main()
{
out_fragColor = vec4(true ? 0.0 : u);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a return value in an
// unused function.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsReturnValue)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform float u;
float f() {
return u;
}
void main()
{
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is an if statement condition
// inside a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsIfCondition)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform bool u;
void main()
{
if (false) {
if (u) {
out_fragColor = vec4(1.0);
}
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a constructor argument in
// a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsConstructorArgument)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform float u;
void main()
{
if (false) {
out_fragColor = vec4(u);
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a binary operator operand
// in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsBinaryOpOperand)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform vec4 u;
void main()
{
if (false) {
out_fragColor = u + 1.0;
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a comparison operator
// operand in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsComparisonOpOperand)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform vec4 u;
void main()
{
if (false) {
if (u == vec4(1.0))
{
out_fragColor = vec4(1.0);
}
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is an unary operator operand
// in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsUnaryOpOperand)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform vec4 u;
void main()
{
if (false) {
out_fragColor = -u;
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is an rvalue in an assigment
// in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsAssignmentRValue)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform vec4 u;
void main()
{
if (false) {
out_fragColor = u;
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a comma operator operand
// in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsCommaOperand)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform vec4 u;
void main()
{
if (false) {
out_fragColor = u, vec4(1.0);
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a switch init statement
// in a block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsSwitchInitStatement)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform int u;
void main()
{
if (false)
{
switch (u)
{
case 1:
out_fragColor = vec4(2.0);
default:
out_fragColor = vec4(1.0);
}
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a loop condition in a
// block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsLoopCondition)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform bool u;
void main()
{
int counter = 0;
if (false)
{
while (u)
{
if (++counter > 2)
{
break;
}
}
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a loop expression in a
// block that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsLoopExpression)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform bool u;
void main()
{
if (false)
{
for (int i = 0; i < 3; u)
{
++i;
}
}
out_fragColor = vec4(0.0);
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is a vector index in a block
// that is not executed.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveAsVectorIndex)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform int u;
void main()
{
vec4 color = vec4(0.0);
if (false)
{
color[u] = 1.0;
}
out_fragColor = color;
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is referenced in a block
// that's not executed. This is a bit of a corner case with some room for interpretation, but we
// treat the variable as statically used.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveJustAReference)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform int u;
void main()
{
vec4 color = vec4(0.0);
if (false)
{
u;
}
out_fragColor = color;
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is statically used but not active. The variable is referenced in a block
// without braces that's not executed. This is a bit of a corner case with some room for
// interpretation, but we treat the variable as statically used.
TEST_F(CollectFragmentVariablesTest, StaticallyUsedButNotActiveJustAReferenceNoBracesIf)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform int u;
void main()
{
vec4 color = vec4(0.0);
if (false)
u;
out_fragColor = color;
})";
compile(shaderString);
checkUniformStaticallyUsedButNotActive("u");
}
// Test a variable that is referenced in a loop body without braces.
TEST_F(CollectFragmentVariablesTest, JustAVariableReferenceInNoBracesLoop)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 out_fragColor;
uniform int u;
void main()
{
vec4 color = vec4(0.0);
while (false)
u;
out_fragColor = color;
})";
compile(shaderString);
const auto &uniforms = mTranslator->getUniforms();
ASSERT_EQ(1u, uniforms.size());
const Uniform &uniform = uniforms[0];
EXPECT_EQ("u", uniform.name);
EXPECT_TRUE(uniform.staticUse);
// Note that we don't check the active flag here - the usage of the uniform is not currently
// being optimized away.
}
// Test an interface block member variable that is statically used but not active.
TEST_F(CollectVertexVariablesTest, StaticallyUsedButNotActiveSimpleInterfaceBlock)
{
const std::string &shaderString =
R"(#version 300 es
uniform b
{
float f;
};
void main() {
gl_Position = vec4(true ? 0.0 : f);
})";
compile(shaderString);
const std::vector<InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
ASSERT_EQ(1u, interfaceBlocks.size());
const InterfaceBlock &interfaceBlock = interfaceBlocks[0];
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_FALSE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0];
EXPECT_EQ("f", field.name);
EXPECT_TRUE(field.staticUse);
EXPECT_FALSE(field.active);
}
// Test an interface block instance variable that is statically used but not active.
TEST_F(CollectVertexVariablesTest, StaticallyUsedButNotActiveInstancedInterfaceBlock)
{
const std::string &shaderString =
R"(#version 300 es
uniform b
{
float f;
} blockInstance;
void main() {
gl_Position = vec4(true ? 0.0 : blockInstance.f);
})";
compile(shaderString);
const std::vector<InterfaceBlock> &interfaceBlocks = mTranslator->getInterfaceBlocks();
ASSERT_EQ(1u, interfaceBlocks.size());
const InterfaceBlock &interfaceBlock = interfaceBlocks[0];
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_FALSE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0];
EXPECT_EQ("f", field.name);
// See TODO in CollectVariables.cpp about tracking instanced interface block field static use.
// EXPECT_TRUE(field.staticUse);
EXPECT_FALSE(field.active);
}
\ No newline at end of file
...@@ -5884,3 +5884,38 @@ TEST_F(VertexShaderValidationTest, LValueSwizzleDuplicateComponents) ...@@ -5884,3 +5884,38 @@ TEST_F(VertexShaderValidationTest, LValueSwizzleDuplicateComponents)
FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog; FAIL() << "Shader compilation succeeded, expecting failure:\n" << mInfoLog;
} }
} }
// Test that a fragment shader with nested if statements without braces compiles successfully.
TEST_F(FragmentShaderValidationTest, HandleIfInnerIfStatementAlwaysTriviallyPruned)
{
const std::string &shaderString =
R"(precision mediump float;
void main()
{
if (true)
if (false)
gl_FragColor = vec4(0.0);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
// Test that a fragment shader with an if statement nested in a loop without braces compiles
// successfully.
TEST_F(FragmentShaderValidationTest, HandleLoopInnerIfStatementAlwaysTriviallyPruned)
{
const std::string &shaderString =
R"(precision mediump float;
void main()
{
while (false)
if (false)
gl_FragColor = vec4(0.0);
})";
if (!compile(shaderString))
{
FAIL() << "Shader compilation failed, expecting success:\n" << mInfoLog;
}
}
...@@ -4148,6 +4148,66 @@ TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockStructFieldMismatch) ...@@ -4148,6 +4148,66 @@ TEST_P(GLSLTest_ES3, ErrorMessageOfLinkInterfaceBlockStructFieldMismatch)
"interface block 'S' member 'S.val1.t2'"); "interface block 'S' member 'S.val1.t2'");
} }
// Test a vertex shader that doesn't declare any varyings with a fragment shader that statically
// uses a varying, but in a statement that gets trivially optimized out by the compiler.
TEST_P(GLSLTest_ES3, FragmentShaderStaticallyUsesVaryingMissingFromVertex)
{
const std::string &vertexShader =
R"(#version 300 es
precision mediump float;
void main()
{
gl_Position = vec4(0, 1, 0, 1);
})";
const std::string &fragmentShader =
R"(#version 300 es
precision mediump float;
in float foo;
out vec4 my_FragColor;
void main()
{
if (false)
{
float unreferenced = foo;
}
my_FragColor = vec4(0, 1, 0, 1);
})";
validateComponentsInErrorMessage(vertexShader, fragmentShader, "does not match any", "foo");
}
// Test a varying that is statically used but not active in the fragment shader.
TEST_P(GLSLTest_ES3, VaryingStaticallyUsedButNotActiveInFragmentShader)
{
const std::string &vertexShader =
R"(#version 300 es
precision mediump float;
in vec4 iv;
out vec4 v;
void main()
{
gl_Position = iv;
v = iv;
})";
const std::string &fragmentShader =
R"(#version 300 es
precision mediump float;
in vec4 v;
out vec4 color;
void main()
{
color = true ? vec4(0.0) : v;
})";
ANGLE_GL_PROGRAM(program, vertexShader, fragmentShader);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest, ANGLE_INSTANTIATE_TEST(GLSLTest,
ES2_D3D9(), ES2_D3D9(),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment