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;
......
...@@ -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 << " ";
......
...@@ -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