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)
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.
void MarkStaticallyUsed(ShaderVariable *variable)
void MarkActive(ShaderVariable *variable)
{
if (!variable->staticUse)
if (!variable->active)
{
if (variable->isStruct())
{
// Conservatively assume all fields are statically used as well.
for (auto &field : variable->fields)
{
MarkStaticallyUsed(&field);
MarkActive(&field);
}
}
variable->staticUse = true;
ASSERT(variable->staticUse);
variable->active = true;
}
}
......@@ -126,9 +126,12 @@ class CollectVariablesTraverser : public TIntermTraverser
private:
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,
const ImmutableString &name,
bool staticUse,
ShaderVariable *variableOut) const;
void setCommonVariableProperties(const TType &type,
const TVariable &variable,
......@@ -322,8 +325,6 @@ InterfaceBlock *CollectVariablesTraverser::recordGLInUsed(const TType &glInType)
ASSERT(glInType.getQualifier() == EvqPerVertexIn);
InterfaceBlock info;
recordInterfaceBlock("gl_in", glInType, &info);
info.staticUse = true;
info.active = true;
mPerVertexInAdded = true;
mInBlocks->push_back(info);
......@@ -551,15 +552,18 @@ void CollectVariablesTraverser::visitSymbol(TIntermSymbol *symbol)
}
if (var)
{
MarkStaticallyUsed(var);
MarkActive(var);
}
}
void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
bool staticUse,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
variableOut->staticUse = staticUse;
const TStructure *structure = type.getStruct();
if (!structure)
{
......@@ -582,7 +586,7 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
// Regardless of the variable type (uniform, in/out etc.) its fields are always plain
// ShaderVariable objects.
ShaderVariable fieldVariable;
setFieldProperties(*field->type(), field->name(), &fieldVariable);
setFieldProperties(*field->type(), field->name(), staticUse, &fieldVariable);
variableOut->fields.push_back(fieldVariable);
}
}
......@@ -594,10 +598,11 @@ void CollectVariablesTraverser::setFieldOrVariableProperties(const TType &type,
void CollectVariablesTraverser::setFieldProperties(const TType &type,
const ImmutableString &name,
bool staticUse,
ShaderVariable *variableOut) const
{
ASSERT(variableOut);
setFieldOrVariableProperties(type, variableOut);
setFieldOrVariableProperties(type, staticUse, variableOut);
variableOut->name.assign(name.data(), name.length());
variableOut->mappedName = HashName(name, mHashFunction, nullptr).data();
}
......@@ -608,7 +613,8 @@ void CollectVariablesTraverser::setCommonVariableProperties(const TType &type,
{
ASSERT(variableOut);
setFieldOrVariableProperties(type, variableOut);
variableOut->staticUse = mSymbolTable->isStaticallyUsed(variable);
setFieldOrVariableProperties(type, variableOut->staticUse, variableOut);
ASSERT(variable.symbolType() != SymbolType::Empty);
variableOut->name.assign(variable.name().data(), variable.name().length());
variableOut->mappedName = getMappedName(&variable);
......@@ -684,6 +690,18 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
if (instanceName != nullptr)
{
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
interfaceBlock->arraySize = interfaceBlockType.isArray() ? interfaceBlockType.getOutermostArraySize() : 0;
......@@ -699,16 +717,36 @@ void CollectVariablesTraverser::recordInterfaceBlock(const char *instanceName,
}
// Gather field information
bool anyFieldStaticallyUsed = false;
for (const TField *field : blockType->fields())
{
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;
setFieldProperties(fieldType, field->name(), &fieldVariable);
setFieldProperties(fieldType, field->name(), staticUse, &fieldVariable);
fieldVariable.isRowMajorLayout =
(fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
interfaceBlock->fields.push_back(fieldVariable);
}
if (anyFieldStaticallyUsed)
{
interfaceBlock->staticUse = true;
}
}
Uniform CollectVariablesTraverser::recordUniform(const TIntermSymbol &variable) const
......@@ -826,7 +864,7 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
{
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();
ASSERT(blockNode);
......@@ -860,10 +898,13 @@ bool CollectVariablesTraverser::visitBinary(Visit, TIntermBinary *binaryNode)
namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
}
ASSERT(namedBlock);
namedBlock->staticUse = true;
ASSERT(namedBlock->staticUse);
namedBlock->active = true;
unsigned int fieldIndex = static_cast<unsigned int>(constantUnion->getIConst(0));
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].active = true;
......
......@@ -219,8 +219,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mGeometryShaderInvocations(0),
mGeometryShaderMaxVertices(-1),
mMaxGeometryShaderInvocations(resources.MaxGeometryShaderInvocations),
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices),
mGlInVariableWithArraySize(nullptr)
mMaxGeometryShaderMaxVertices(resources.MaxGeometryOutputVertices)
{
}
......@@ -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
// an l-value that can be operated on this way.
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
TIntermSymbol *symNode = node->getAsSymbolNode();
if (message.empty() && symNode != nullptr)
{
symbolTable.markStaticWrite(symNode->variable());
return true;
}
......@@ -689,6 +719,7 @@ bool TParseContext::checkConstructorArguments(const TSourceLoc &line,
for (TIntermNode *arg : arguments)
{
markStaticReadIfSymbol(arg);
const TIntermTyped *argTyped = arg->getAsTyped();
ASSERT(argTyped != nullptr);
if (type.getBasicType() != EbtStruct && IsOpaqueType(argTyped->getBasicType()))
......@@ -1665,15 +1696,20 @@ void TParseContext::functionCallRValueLValueErrorCheck(const TFunction *fnCandid
{
TQualifier qual = fnCandidate->getParam(i)->getType().getQualifier();
TIntermTyped *argument = (*(fnCall->getSequence()))[i]->getAsTyped();
if (!IsImage(argument->getBasicType()) && (IsQualifierUnspecified(qual) || qual == EvqIn ||
qual == EvqInOut || qual == EvqConstReadOnly))
bool argumentIsRead = (IsQualifierUnspecified(qual) || qual == EvqIn || qual == EvqInOut ||
qual == EvqConstReadOnly);
if (argumentIsRead)
{
if (argument->getMemoryQualifier().writeonly)
markStaticReadIfSymbol(argument);
if (!IsImage(argument->getBasicType()))
{
error(argument->getLine(),
"Writeonly value cannot be passed for 'in' or 'inout' parameters.",
fnCall->functionName());
return;
if (argument->getMemoryQualifier().writeonly)
{
error(argument->getLine(),
"Writeonly value cannot be passed for 'in' or 'inout' parameters.",
fnCall->functionName());
return;
}
}
}
if (qual == EvqOut || qual == EvqInOut)
......@@ -1878,8 +1914,8 @@ TIntermTyped *TParseContext::parseVariableIdentifier(const TSourceLoc &location,
else if ((mGeometryShaderInputPrimitiveType != EptUndefined) &&
(variableType.getQualifier() == EvqPerVertexIn))
{
ASSERT(mGlInVariableWithArraySize != nullptr);
node = new TIntermSymbol(mGlInVariableWithArraySize);
ASSERT(symbolTable.getGlInVariableWithArraySize() != nullptr);
node = new TIntermSymbol(symbolTable.getGlInVariableWithArraySize());
}
else
{
......@@ -1994,6 +2030,7 @@ bool TParseContext::executeInitializer(const TSourceLoc &line,
}
*initNode = new TIntermBinary(EOpInitialize, intermSymbol, initializer);
markStaticReadIfSymbol(initializer);
(*initNode)->setLine(line);
return true;
}
......@@ -2036,8 +2073,19 @@ TIntermNode *TParseContext::addLoop(TLoopType type,
TIntermTyped *typedCond = nullptr;
if (cond)
{
markStaticReadIfSymbol(cond);
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 (type == ELoopDoWhile)
......@@ -2084,6 +2132,16 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond,
const TSourceLoc &loc)
{
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.
if (isScalarBool && cond->getAsConstantUnion())
......@@ -2099,6 +2157,7 @@ TIntermNode *TParseContext::addIfElse(TIntermTyped *cond,
}
TIntermIfElse *node = new TIntermIfElse(cond, EnsureBlock(code.node1), EnsureBlock(code.node2));
markStaticReadIfSymbol(cond);
node->setLine(loc);
return node;
......@@ -2346,9 +2405,9 @@ void TParseContext::checkGeometryShaderInputAndSetArraySize(const TSourceLoc &lo
// input primitive declaration.
if (mGeometryShaderInputPrimitiveType != EptUndefined)
{
ASSERT(mGlInVariableWithArraySize != nullptr);
ASSERT(symbolTable.getGlInVariableWithArraySize() != nullptr);
type->sizeOutermostUnsizedArray(
mGlInVariableWithArraySize->getType().getOutermostArraySize());
symbolTable.getGlInVariableWithArraySize()->getType().getOutermostArraySize());
}
else
{
......@@ -2818,17 +2877,7 @@ bool TParseContext::checkPrimitiveTypeMatchesTypeQualifier(const TTypeQualifier
void TParseContext::setGeometryShaderInputArraySize(unsigned int inputArraySize,
const TSourceLoc &line)
{
if (mGlInVariableWithArraySize == nullptr)
{
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)
if (!symbolTable.setGlInArraySize(inputArraySize))
{
error(line,
"Array size or input primitive declaration doesn't match the size of earlier sized "
......@@ -4010,6 +4059,7 @@ TIntermTyped *TParseContext::addIndexExpression(TIntermTyped *baseExpression,
}
}
markStaticReadIfSymbol(indexExpression);
TIntermBinary *node = new TIntermBinary(EOpIndexIndirect, baseExpression, indexExpression);
node->setLine(location);
// Indirect indexing can never be constant folded.
......@@ -4793,6 +4843,7 @@ TIntermSwitch *TParseContext::addSwitch(TIntermTyped *init,
return nullptr;
}
markStaticReadIfSymbol(init);
TIntermSwitch *node = new TIntermSwitch(init, statementList);
node->setLine(loc);
return node;
......@@ -4889,6 +4940,7 @@ TIntermTyped *TParseContext::createUnaryMath(TOperator op,
return nullptr;
}
markStaticReadIfSymbol(child);
TIntermUnary *node = new TIntermUnary(op, child);
node->setLine(loc);
......@@ -5292,6 +5344,9 @@ TIntermTyped *TParseContext::addBinaryMathInternal(TOperator op,
}
TIntermBinary *node = new TIntermBinary(op, left, right);
ASSERT(op != EOpAssign);
markStaticReadIfSymbol(left);
markStaticReadIfSymbol(right);
node->setLine(loc);
return expressionOrFoldedResult(node);
}
......@@ -5354,6 +5409,11 @@ TIntermTyped *TParseContext::addAssign(TOperator op,
assignError(loc, "assign", left->getCompleteString(), right->getCompleteString());
return left;
}
if (op != EOpAssign)
{
markStaticReadIfSymbol(left);
}
markStaticReadIfSymbol(right);
node->setLine(loc);
return node;
}
......@@ -5375,6 +5435,9 @@ TIntermTyped *TParseContext::addComma(TIntermTyped *left,
}
TIntermBinary *commaNode = TIntermBinary::CreateComma(left, right, mShaderVersion);
markStaticReadIfSymbol(left);
markStaticReadIfSymbol(right);
commaNode->setLine(loc);
return expressionOrFoldedResult(commaNode);
}
......@@ -5420,6 +5483,7 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
{
if (expression != nullptr)
{
markStaticReadIfSymbol(expression);
ASSERT(op == EOpReturn);
mFunctionReturnsValue = true;
if (mCurrentFunctionType->getBasicType() == EbtVoid)
......@@ -5436,6 +5500,15 @@ TIntermBranch *TParseContext::addBranch(TOperator op,
return node;
}
void TParseContext::appendStatement(TIntermBlock *block, TIntermNode *statement)
{
if (statement != nullptr)
{
markStaticReadIfSymbol(statement);
block->appendStatement(statement);
}
}
void TParseContext::checkTextureGather(TIntermAggregate *functionCall)
{
ASSERT(functionCall->getOp() == EOpCallBuiltInFunction);
......@@ -5895,6 +5968,9 @@ TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
}
TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
markStaticReadIfSymbol(cond);
markStaticReadIfSymbol(trueExpression);
markStaticReadIfSymbol(falseExpression);
node->setLine(loc);
return expressionOrFoldedResult(node);
}
......
......@@ -419,6 +419,8 @@ class TParseContext : angle::NonCopyable
TIntermBranch *addBranch(TOperator op, const TSourceLoc &loc);
TIntermBranch *addBranch(TOperator op, TIntermTyped *expression, const TSourceLoc &loc);
void appendStatement(TIntermBlock *block, TIntermNode *statement);
void checkTextureGather(TIntermAggregate *functionCall);
void checkTextureOffsetConst(TIntermAggregate *functionCall);
void checkImageMemoryAccessForBuiltinFunctions(TIntermAggregate *functionCall);
......@@ -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.
constexpr static size_t kAtomicCounterArrayStride = 4;
void markStaticReadIfSymbol(TIntermNode *node);
// Returns a clamped index. If it prints out an error message, the token is "[]".
int checkIndexLessThan(bool outOfRangeIndexIsError,
const TSourceLoc &location,
......@@ -638,10 +642,6 @@ class TParseContext : angle::NonCopyable
int mGeometryShaderMaxVertices;
int mMaxGeometryShaderInvocations;
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,
......
......@@ -79,7 +79,8 @@ TSymbol *TSymbolTable::TSymbolTableLevel::find(const ImmutableString &name) cons
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
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
{
int userDefinedLevel = static_cast<int>(mTable.size()) - 1;
......@@ -238,6 +289,8 @@ void TSymbolTable::setGlobalInvariant(bool invariant)
void TSymbolTable::clearCompilationResults()
{
mUniqueIdCounter = kLastBuiltInId + 1;
mVariableMetadata.clear();
mGlInVariableWithArraySize = nullptr;
// User-defined scopes should have already been cleared when the compilation finished.
ASSERT(mTable.size() == 0u);
......@@ -297,4 +350,8 @@ void TSymbolTable::initSamplerDefaultPrecision(TBasicType samplerType)
setDefaultPrecision(samplerType, EbpLow);
}
TSymbolTable::VariableMetadata::VariableMetadata() : staticRead(false), staticWrite(false)
{
}
} // namespace sh
......@@ -90,6 +90,16 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
const TFunction *setFunctionParameterNamesFromDefinition(const TFunction *function,
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
// with a reference to a short-lived char * is fine to pass here.
const TSymbol *find(const ImmutableString &name, int shaderVersion) const;
......@@ -154,6 +164,20 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
sh::GLenum mShaderType;
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
......
......@@ -1304,11 +1304,11 @@ compound_statement_no_new_scope
statement_list
: statement {
$$ = new TIntermBlock();
$$->appendStatement($1);
context->appendStatement($$, $1);
}
| statement_list statement {
$$ = $1;
$$->appendStatement($2);
context->appendStatement($$, $2);
}
;
......
......@@ -4630,7 +4630,7 @@ yyreduce:
{
(yyval.interm.intermBlock) = new TIntermBlock();
(yyval.interm.intermBlock)->appendStatement((yyvsp[0].interm.intermNode));
context->appendStatement((yyval.interm.intermBlock), (yyvsp[0].interm.intermNode));
}
break;
......@@ -4639,7 +4639,7 @@ yyreduce:
{
(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;
......
......@@ -292,13 +292,13 @@ bool VaryingPacking::collectAndPackUserVaryings(gl::InfoLog &infoLog,
const sh::Varying *output = ref.second.fragment;
// 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.
// GLES specs are a bit vague on whether it's allowed to only pack active varyings, though
// GLES 3.1 spec section 11.1.2.1 says that "device-dependent optimizations" may be used to
// make vertex shader outputs fit.
// builtins. Note that we pack all statically used user-defined varyings even if they are
// not active. GLES specs are a bit vague on whether it's allowed to only pack active
// varyings, though GLES 3.1 spec section 11.1.2.1 says that "device-dependent
// optimizations" may be used to make vertex shader outputs fit.
if ((input && output && output->staticUse) ||
(input && input->isBuiltIn() && input->staticUse) ||
(output && output->isBuiltIn() && output->staticUse))
(input && input->isBuiltIn() && input->active) ||
(output && output->isBuiltIn() && output->active))
{
const sh::Varying *varying = output ? output : input;
......
......@@ -793,8 +793,8 @@ void DynamicHLSL::generateShaderLinkHLSL(const gl::Context *context,
ASSERT(!varying.isBuiltIn() && !varying.isStruct());
// 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.
if (packedVarying.vertexOnly || !varying.staticUse)
// that the active flag is set according to usage in the fragment shader.
if (packedVarying.vertexOnly || !varying.active)
continue;
pixelStream << " ";
......
......@@ -113,6 +113,7 @@ class CollectVariablesTest : public testing::Test
const OutputVariable &outputVariable = outputVariables[varIndex];
EXPECT_EQ(-1, outputVariable.location);
EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_EQ(varName, outputVariable.name);
*outResult = &outputVariable;
}
......@@ -125,6 +126,17 @@ class CollectVariablesTest : public testing::Test
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;
std::unique_ptr<TranslatorGLSL> mTranslator;
};
......@@ -247,6 +259,7 @@ TEST_F(CollectFragmentVariablesTest, SimpleOutputVar)
EXPECT_EQ(-1, outputVariable.location);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
EXPECT_EQ("out_fragColor", outputVariable.name);
}
......@@ -272,6 +285,7 @@ TEST_F(CollectFragmentVariablesTest, LocationOutputVar)
EXPECT_EQ(5, outputVariable.location);
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, outputVariable.precision);
EXPECT_TRUE(outputVariable.staticUse);
EXPECT_TRUE(outputVariable.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, outputVariable.type);
EXPECT_EQ("out_fragColor", outputVariable.name);
}
......@@ -296,6 +310,7 @@ TEST_F(CollectVertexVariablesTest, LocationAttribute)
EXPECT_EQ(5, attribute.location);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, attribute.precision);
EXPECT_TRUE(attribute.staticUse);
EXPECT_TRUE(attribute.active);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, attribute.type);
EXPECT_EQ("in_Position", attribute.name);
}
......@@ -322,6 +337,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
EXPECT_EQ(BLOCKLAYOUT_SHARED, interfaceBlock.layout);
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
......@@ -329,6 +345,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("f", field.name);
EXPECT_FALSE(field.isRowMajorLayout);
......@@ -358,6 +375,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
......@@ -365,6 +383,7 @@ TEST_F(CollectVertexVariablesTest, SimpleInstancedInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("f", field.name);
EXPECT_FALSE(field.isRowMajorLayout);
......@@ -395,25 +414,27 @@ TEST_F(CollectVertexVariablesTest, StructInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0];
const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct());
EXPECT_TRUE(field.staticUse);
EXPECT_EQ("s", field.name);
EXPECT_EQ(DecorateName("s"), field.mappedName);
EXPECT_FALSE(field.isRowMajorLayout);
EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(blockField.staticUse);
EXPECT_TRUE(blockField.active);
EXPECT_EQ("s", blockField.name);
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
EXPECT_FALSE(member.isStruct());
EXPECT_EQ("f", member.name);
EXPECT_EQ(DecorateName("f"), member.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, member.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
// NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_FALSE(structField.isStruct());
EXPECT_EQ("f", structField.name);
EXPECT_EQ(DecorateName("f"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
}
TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock)
......@@ -441,25 +462,27 @@ TEST_F(CollectVertexVariablesTest, StructInstancedInterfaceBlock)
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_EQ("instanceName", interfaceBlock.instanceName);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0];
const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct());
EXPECT_TRUE(field.staticUse);
EXPECT_EQ("s", field.name);
EXPECT_EQ(DecorateName("s"), field.mappedName);
EXPECT_FALSE(field.isRowMajorLayout);
EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(blockField.staticUse);
EXPECT_TRUE(blockField.active);
EXPECT_EQ("s", blockField.name);
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
EXPECT_FALSE(member.isStruct());
EXPECT_EQ("f", member.name);
EXPECT_EQ(DecorateName("f"), member.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, member.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
// NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_FALSE(structField.isStruct());
EXPECT_EQ("f", structField.name);
EXPECT_EQ(DecorateName("f"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
}
TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock)
......@@ -486,25 +509,27 @@ TEST_F(CollectVertexVariablesTest, NestedStructRowMajorInterfaceBlock)
EXPECT_EQ("b", interfaceBlock.name);
EXPECT_EQ(DecorateName("b"), interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
const InterfaceBlockField &field = interfaceBlock.fields[0];
const InterfaceBlockField &blockField = interfaceBlock.fields[0];
EXPECT_TRUE(field.isStruct());
EXPECT_TRUE(field.staticUse);
EXPECT_EQ("s", field.name);
EXPECT_EQ(DecorateName("s"), field.mappedName);
EXPECT_TRUE(field.isRowMajorLayout);
const ShaderVariable &member = field.fields[0];
// NOTE: we don't currently mark struct members as statically used or not
EXPECT_FALSE(member.isStruct());
EXPECT_EQ("m", member.name);
EXPECT_EQ(DecorateName("m"), member.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, member.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, member.precision);
EXPECT_TRUE(blockField.isStruct());
EXPECT_TRUE(blockField.staticUse);
EXPECT_TRUE(blockField.active);
EXPECT_EQ("s", blockField.name);
EXPECT_EQ(DecorateName("s"), blockField.mappedName);
EXPECT_TRUE(blockField.isRowMajorLayout);
const ShaderVariable &structField = blockField.fields[0];
// NOTE: we don't track static use or active at individual struct member granularity.
EXPECT_FALSE(structField.isStruct());
EXPECT_EQ("m", structField.name);
EXPECT_EQ(DecorateName("m"), structField.mappedName);
EXPECT_GLENUM_EQ(GL_FLOAT_MAT2, structField.type);
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, structField.precision);
}
TEST_F(CollectVertexVariablesTest, VaryingInterpolation)
......@@ -533,6 +558,7 @@ TEST_F(CollectVertexVariablesTest, VaryingInterpolation)
EXPECT_FALSE(varying->isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, varying->precision);
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_GLENUM_EQ(GL_FLOAT, varying->type);
EXPECT_EQ("vary", varying->name);
EXPECT_EQ(DecorateName("vary"), varying->mappedName);
......@@ -795,6 +821,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock)
EXPECT_EQ("blockInstance", interfaceBlock.instanceName);
EXPECT_EQ("webgl_9", interfaceBlock.mappedName);
EXPECT_TRUE(interfaceBlock.staticUse);
EXPECT_TRUE(interfaceBlock.active);
ASSERT_EQ(1u, interfaceBlock.fields.size());
......@@ -802,6 +829,7 @@ TEST_F(CollectHashedVertexVariablesTest, InstancedInterfaceBlock)
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
EXPECT_TRUE(field.staticUse);
EXPECT_TRUE(field.active);
EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
EXPECT_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName);
......@@ -837,13 +865,17 @@ TEST_F(CollectHashedVertexVariablesTest, StructUniform)
EXPECT_EQ("webgl_1", uniform.mappedName);
EXPECT_EQ("sType", uniform.structName);
EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
ASSERT_EQ(1u, uniform.fields.size());
const ShaderVariable &field = uniform.fields[0];
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_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName);
......@@ -877,13 +909,17 @@ TEST_F(CollectHashedVertexVariablesTest, NamelessStructUniform)
EXPECT_EQ("webgl_1", uniform.mappedName);
EXPECT_EQ("", uniform.structName);
EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
ASSERT_EQ(1u, uniform.fields.size());
const ShaderVariable &field = uniform.fields[0];
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_EQ("field", field.name);
EXPECT_EQ("webgl_5", field.mappedName);
......@@ -913,6 +949,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration)
EXPECT_FALSE(uniform.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniform.precision);
EXPECT_TRUE(uniform.staticUse);
EXPECT_TRUE(uniform.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniform.type);
EXPECT_EQ("uA", uniform.name);
......@@ -920,6 +957,7 @@ TEST_F(CollectFragmentVariablesTest, MultiDeclaration)
EXPECT_FALSE(uniformB.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision);
EXPECT_TRUE(uniformB.staticUse);
EXPECT_TRUE(uniformB.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type);
EXPECT_EQ("uB", uniformB.name);
}
......@@ -946,6 +984,7 @@ TEST_F(CollectFragmentVariablesTest, EmptyDeclarator)
EXPECT_FALSE(uniformB.isArray());
EXPECT_GLENUM_EQ(GL_MEDIUM_FLOAT, uniformB.precision);
EXPECT_TRUE(uniformB.staticUse);
EXPECT_TRUE(uniformB.active);
EXPECT_GLENUM_EQ(GL_FLOAT, uniformB.type);
EXPECT_EQ("uB", uniformB.name);
}
......@@ -1008,6 +1047,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields)
EXPECT_EQ("gl_PerVertex", inBlock->name);
EXPECT_EQ("gl_in", inBlock->instanceName);
EXPECT_TRUE(inBlock->staticUse);
EXPECT_TRUE(inBlock->active);
EXPECT_TRUE(inBlock->isBuiltIn());
ASSERT_EQ(1u, inBlock->fields.size());
......@@ -1017,6 +1057,7 @@ TEST_F(CollectGeometryVariablesTest, CollectGLInFields)
EXPECT_FALSE(glPositionField.isArray());
EXPECT_FALSE(glPositionField.isStruct());
EXPECT_TRUE(glPositionField.staticUse);
EXPECT_TRUE(glPositionField.active);
EXPECT_TRUE(glPositionField.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, glPositionField.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, glPositionField.type);
......@@ -1076,6 +1117,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveIDIn)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1108,6 +1150,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInvocationID)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1169,6 +1212,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPosition)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying->type);
......@@ -1200,6 +1244,7 @@ TEST_F(CollectGeometryVariablesTest, CollectPrimitiveID)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1231,6 +1276,7 @@ TEST_F(CollectGeometryVariablesTest, CollectLayer)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1262,6 +1308,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectPrimitiveID)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1293,6 +1340,7 @@ TEST_F(CollectFragmentVariablesEXTGeometryShaderTest, CollectLayer)
EXPECT_FALSE(varying->isArray());
EXPECT_FALSE(varying->isStruct());
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_TRUE(varying->isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_INT, varying->precision);
EXPECT_GLENUM_EQ(GL_INT, varying->type);
......@@ -1385,6 +1433,7 @@ TEST_F(CollectGeometryVariablesTest, CollectInputs)
EXPECT_TRUE(varying.isArray());
EXPECT_FALSE(varying.isStruct());
EXPECT_TRUE(varying.staticUse);
EXPECT_TRUE(varying.active);
EXPECT_FALSE(varying.isBuiltIn());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying.precision);
EXPECT_GLENUM_EQ(GL_FLOAT_VEC4, varying.type);
......@@ -1548,8 +1597,442 @@ TEST_F(CollectFragmentVariablesTest, VaryingUsedInsideFoldedTernary)
EXPECT_FALSE(varying->isArray());
EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, varying->precision);
EXPECT_TRUE(varying->staticUse);
EXPECT_TRUE(varying->active);
EXPECT_GLENUM_EQ(GL_FLOAT, varying->type);
EXPECT_EQ("vary", varying->name);
EXPECT_EQ(DecorateName("vary"), varying->mappedName);
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)
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)
"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.
ANGLE_INSTANTIATE_TEST(GLSLTest,
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