Commit daeac238 by Shahbaz Youssefi Committed by Commit Bot

Translator: Ensure structs and blocks are uniquely defined

A new AST validation is added to ensure that the same TStructure or TInterfaceBlock is not redundantly defined. This helps with SPIR-V generation by allowing the id to be used as key in a hash map that looks up the corresponding SPIR-V type id. A bug is fixed where the Vulkan driver uniform declaration created two identical declarations for ANGLEDepthRangeParams. A number of other bugs are also fixed in this change, where if a variable declaration is eliminated (for example due to constant folding, or inactive interface variable removal) and it contained a struct specifier, the struct declaration was also removed. OutputGLSLBase had a hack where structs were declared on first encounter, which was incorrect as the scope of the declaration could change. Those bugs are fixed and this hack is removed. Bug: angleproject:2733 Bug: angleproject:4889 Bug: angleproject:5936 Change-Id: I8e13748c0bf552ae8b052249282769a1f0775603 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2881942Reviewed-by: 's avatarCharlie Lao <cclao@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 84d22197
...@@ -395,8 +395,8 @@ void TOutputGLSLBase::writeVariableType(const TType &type, ...@@ -395,8 +395,8 @@ void TOutputGLSLBase::writeVariableType(const TType &type,
out << getMemoryQualifiers(type); out << getMemoryQualifiers(type);
} }
// Declare the struct if we have not done so already. // Declare the struct.
if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) if (type.isStructSpecifier())
{ {
const TStructure *structure = type.getStruct(); const TStructure *structure = type.getStruct();
...@@ -1279,17 +1279,6 @@ ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func) ...@@ -1279,17 +1279,6 @@ ImmutableString TOutputGLSLBase::hashFunctionNameIfNeeded(const TFunction *func)
} }
} }
bool TOutputGLSLBase::structDeclared(const TStructure *structure) const
{
ASSERT(structure);
if (structure->symbolType() == SymbolType::Empty)
{
return false;
}
return (mDeclaredStructs.count(structure->uniqueId().get()) > 0);
}
void TOutputGLSLBase::declareStruct(const TStructure *structure) void TOutputGLSLBase::declareStruct(const TStructure *structure)
{ {
TInfoSinkBase &out = objSink(); TInfoSinkBase &out = objSink();
...@@ -1313,11 +1302,6 @@ void TOutputGLSLBase::declareStruct(const TStructure *structure) ...@@ -1313,11 +1302,6 @@ void TOutputGLSLBase::declareStruct(const TStructure *structure)
out << ";\n"; out << ";\n";
} }
out << "}"; out << "}";
if (structure->symbolType() != SymbolType::Empty)
{
mDeclaredStructs.insert(structure->uniqueId().get());
}
} }
void TOutputGLSLBase::declareInterfaceBlockLayout(const TType &type) void TOutputGLSLBase::declareInterfaceBlockLayout(const TType &type)
......
...@@ -89,7 +89,6 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -89,7 +89,6 @@ class TOutputGLSLBase : public TIntermTraverser
void declareStruct(const TStructure *structure); void declareStruct(const TStructure *structure);
void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol); void writeQualifier(TQualifier qualifier, const TType &type, const TSymbol *symbol);
bool structDeclared(const TStructure *structure) const;
const char *mapQualifierToString(TQualifier qualifier); const char *mapQualifierToString(TQualifier qualifier);
...@@ -104,9 +103,6 @@ class TOutputGLSLBase : public TIntermTraverser ...@@ -104,9 +103,6 @@ class TOutputGLSLBase : public TIntermTraverser
TInfoSinkBase &mObjSink; TInfoSinkBase &mObjSink;
bool mDeclaringVariable; bool mDeclaringVariable;
// This set contains all the ids of the structs from every scope.
std::set<int> mDeclaredStructs;
ShArrayIndexClampingStrategy mClampingStrategy; ShArrayIndexClampingStrategy mClampingStrategy;
// name hashing. // name hashing.
......
...@@ -174,15 +174,6 @@ void TOutputVulkanGLSL::writeVariableType(const TType &type, ...@@ -174,15 +174,6 @@ void TOutputVulkanGLSL::writeVariableType(const TType &type,
TOutputGLSL::writeVariableType(overrideType, symbol, isFunctionArgument); TOutputGLSL::writeVariableType(overrideType, symbol, isFunctionArgument);
} }
void TOutputVulkanGLSL::writeStructType(const TStructure *structure)
{
if (!structDeclared(structure))
{
declareStruct(structure);
objSink() << ";\n";
}
}
bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision) bool TOutputVulkanGLSL::writeVariablePrecision(TPrecision precision)
{ {
if ((precision == EbpUndefined) || !mEnablePrecision) if ((precision == EbpUndefined) || !mEnablePrecision)
......
...@@ -32,8 +32,6 @@ class TOutputVulkanGLSL : public TOutputGLSL ...@@ -32,8 +32,6 @@ class TOutputVulkanGLSL : public TOutputGLSL
bool enablePrecision, bool enablePrecision,
ShCompileOptions compileOptions); ShCompileOptions compileOptions);
void writeStructType(const TStructure *structure);
uint32_t nextUnusedBinding() { return mNextUnusedBinding++; } uint32_t nextUnusedBinding() { return mNextUnusedBinding++; }
uint32_t nextUnusedInputLocation(uint32_t consumedCount) uint32_t nextUnusedInputLocation(uint32_t consumedCount)
{ {
......
...@@ -2997,6 +2997,15 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType ...@@ -2997,6 +2997,15 @@ TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType
{ {
declaration->appendDeclarator(initNode); declaration->appendDeclarator(initNode);
} }
else if (publicType.isStructSpecifier())
{
// The initialization got constant folded. If it's a struct, declare the struct anyway.
TVariable *emptyVariable =
new TVariable(&symbolTable, kEmptyImmutableString, type, SymbolType::Empty);
TIntermSymbol *symbol = new TIntermSymbol(emptyVariable);
symbol->setLine(publicType.getLine());
declaration->appendDeclarator(symbol);
}
} }
return declaration; return declaration;
} }
......
...@@ -116,7 +116,7 @@ ANGLE_NO_DISCARD bool InitializeUnusedOutputs(TIntermBlock *root, ...@@ -116,7 +116,7 @@ ANGLE_NO_DISCARD bool InitializeUnusedOutputs(TIntermBlock *root,
} // anonymous namespace } // anonymous namespace
// class DriverUniformMetal // class DriverUniformMetal
TFieldList *DriverUniformMetal::createUniformFields(TSymbolTable *symbolTable) const TFieldList *DriverUniformMetal::createUniformFields(TSymbolTable *symbolTable)
{ {
TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable); TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable);
......
...@@ -48,7 +48,7 @@ class DriverUniformMetal : public DriverUniform ...@@ -48,7 +48,7 @@ class DriverUniformMetal : public DriverUniform
TIntermBinary *getCoverageMaskFieldRef() const; TIntermBinary *getCoverageMaskFieldRef() const;
protected: protected:
TFieldList *createUniformFields(TSymbolTable *symbolTable) const override; TFieldList *createUniformFields(TSymbolTable *symbolTable) override;
}; };
class TranslatorMetal : public TranslatorVulkan class TranslatorMetal : public TranslatorVulkan
......
...@@ -814,8 +814,8 @@ bool TranslatorVulkan::translateImpl(TInfoSinkBase &sink, ...@@ -814,8 +814,8 @@ bool TranslatorVulkan::translateImpl(TInfoSinkBase &sink,
// inactive samplers is not yet supported. Note also that currently, CollectVariables marks // inactive samplers is not yet supported. Note also that currently, CollectVariables marks
// every field of an active uniform that's of struct type as active, i.e. no extracted sampler // every field of an active uniform that's of struct type as active, i.e. no extracted sampler
// is inactive. // is inactive.
if (!RemoveInactiveInterfaceVariables(this, root, getAttributes(), getInputVaryings(), if (!RemoveInactiveInterfaceVariables(this, root, &getSymbolTable(), getAttributes(),
getOutputVariables(), getUniforms(), getInputVaryings(), getOutputVariables(), getUniforms(),
getInterfaceBlocks())) getInterfaceBlocks()))
{ {
return false; return false;
......
...@@ -49,10 +49,15 @@ class ValidateAST : public TIntermTraverser ...@@ -49,10 +49,15 @@ class ValidateAST : public TIntermTraverser
// Visit as a generic node // Visit as a generic node
void visitNode(Visit visit, TIntermNode *node); void visitNode(Visit visit, TIntermNode *node);
// Visit a structure or interface block, and recursively visit its fields of structure type.
void visitStructOrInterfaceBlockDeclaration(const TType &type, const TSourceLoc &location);
void visitStructInDeclarationUsage(const TType &type, const TSourceLoc &location);
void scope(Visit visit); void scope(Visit visit);
bool isVariableDeclared(const TVariable *variable); bool isVariableDeclared(const TVariable *variable);
bool variableNeedsDeclaration(const TVariable *variable); bool variableNeedsDeclaration(const TVariable *variable);
const TFieldListCollection *getStructOrInterfaceBlock(const TType &type,
ImmutableString *typeNameOut);
void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count); void expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count);
...@@ -73,6 +78,10 @@ class ValidateAST : public TIntermTraverser ...@@ -73,6 +78,10 @@ class ValidateAST : public TIntermTraverser
// For validateNullNodes: // For validateNullNodes:
bool mNullNodesFailed = false; bool mNullNodesFailed = false;
// For validateStructUsage:
std::vector<std::map<ImmutableString, const TFieldListCollection *>> mStructsAndBlocksByName;
bool mStructUsageFailed = false;
// For validateMultiDeclarations: // For validateMultiDeclarations:
bool mMultiDeclarationsFailed = false; bool mMultiDeclarationsFailed = false;
}; };
...@@ -130,6 +139,96 @@ void ValidateAST::visitNode(Visit visit, TIntermNode *node) ...@@ -130,6 +139,96 @@ void ValidateAST::visitNode(Visit visit, TIntermNode *node)
} }
} }
void ValidateAST::visitStructOrInterfaceBlockDeclaration(const TType &type,
const TSourceLoc &location)
{
if (type.getStruct() == nullptr && type.getInterfaceBlock() == nullptr)
{
return;
}
// Make sure the structure or interface block is not doubly defined.
ImmutableString typeName("");
const TFieldListCollection *structOrBlock = getStructOrInterfaceBlock(type, &typeName);
if (structOrBlock)
{
ASSERT(!typeName.empty());
if (mStructsAndBlocksByName.back().find(typeName) != mStructsAndBlocksByName.back().end())
{
mDiagnostics->error(location,
"Found redeclaration of struct or interface block with the same "
"name in the same scope <validateStructUsage>",
typeName.data());
mStructUsageFailed = true;
}
else
{
// First encounter.
mStructsAndBlocksByName.back()[typeName] = structOrBlock;
}
}
// Recurse the fields of the structure or interface block and check members of structure type.
// Note that structOrBlock was previously only set for named structures, so make sure nameless
// structs are also recursed.
if (structOrBlock == nullptr)
{
structOrBlock = type.getStruct();
}
ASSERT(structOrBlock != nullptr);
for (const TField *field : structOrBlock->fields())
{
visitStructInDeclarationUsage(*field->type(), field->line());
}
}
void ValidateAST::visitStructInDeclarationUsage(const TType &type, const TSourceLoc &location)
{
if (type.getStruct() == nullptr)
{
return;
}
// Make sure the structure being referenced has the same pointer as the closest (in scope)
// definition.
const TStructure *structure = type.getStruct();
const ImmutableString &typeName = structure->name();
bool foundDeclaration = false;
for (size_t scopeIndex = mStructsAndBlocksByName.size(); scopeIndex > 0; --scopeIndex)
{
const std::map<ImmutableString, const TFieldListCollection *> &scopeDecls =
mStructsAndBlocksByName[scopeIndex - 1];
auto iter = scopeDecls.find(typeName);
if (iter != scopeDecls.end())
{
foundDeclaration = true;
if (iter->second != structure)
{
mDiagnostics->error(location,
"Found reference to struct or interface block with doubly "
"created type <validateStructUsage>",
typeName.data());
mStructUsageFailed = true;
}
}
}
if (!foundDeclaration)
{
mDiagnostics->error(location,
"Found reference to struct or interface block with no declaration "
"<validateStructUsage>",
typeName.data());
mStructUsageFailed = true;
}
}
void ValidateAST::scope(Visit visit) void ValidateAST::scope(Visit visit)
{ {
if (mOptions.validateVariableReferences) if (mOptions.validateVariableReferences)
...@@ -143,6 +242,18 @@ void ValidateAST::scope(Visit visit) ...@@ -143,6 +242,18 @@ void ValidateAST::scope(Visit visit)
mDeclaredVariables.pop_back(); mDeclaredVariables.pop_back();
} }
} }
if (mOptions.validateStructUsage)
{
if (visit == PreVisit)
{
mStructsAndBlocksByName.push_back({});
}
else if (visit == PostVisit)
{
mStructsAndBlocksByName.pop_back();
}
}
} }
bool ValidateAST::isVariableDeclared(const TVariable *variable) bool ValidateAST::isVariableDeclared(const TVariable *variable)
...@@ -179,6 +290,30 @@ bool ValidateAST::variableNeedsDeclaration(const TVariable *variable) ...@@ -179,6 +290,30 @@ bool ValidateAST::variableNeedsDeclaration(const TVariable *variable)
return true; return true;
} }
const TFieldListCollection *ValidateAST::getStructOrInterfaceBlock(const TType &type,
ImmutableString *typeNameOut)
{
const TStructure *structure = type.getStruct();
const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock();
ASSERT(structure != nullptr || interfaceBlock != nullptr);
// Make sure the structure or interface block is not doubly defined.
const TFieldListCollection *structOrBlock = nullptr;
if (structure != nullptr && structure->symbolType() != SymbolType::Empty)
{
structOrBlock = structure;
*typeNameOut = structure->name();
}
else if (interfaceBlock != nullptr)
{
structOrBlock = interfaceBlock;
*typeNameOut = interfaceBlock->name();
}
return structOrBlock;
}
void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count) void ValidateAST::expectNonNullChildren(Visit visit, TIntermNode *node, size_t least_count)
{ {
if (visit == PreVisit && mOptions.validateNullNodes) if (visit == PreVisit && mOptions.validateNullNodes)
...@@ -237,7 +372,10 @@ void ValidateAST::visitSymbol(TIntermSymbol *node) ...@@ -237,7 +372,10 @@ void ValidateAST::visitSymbol(TIntermSymbol *node)
} }
else else
{ {
if (!isVariableDeclared(variable)) const bool isStructDeclaration =
type.isStructSpecifier() && variable->symbolType() == SymbolType::Empty;
if (!isStructDeclaration && !isVariableDeclared(variable))
{ {
mDiagnostics->error(node->getLine(), mDiagnostics->error(node->getLine(),
"Found reference to undeclared or inconsistently redeclared " "Found reference to undeclared or inconsistently redeclared "
...@@ -366,8 +504,10 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) ...@@ -366,8 +504,10 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node)
mMultiDeclarationsFailed = true; mMultiDeclarationsFailed = true;
} }
if (mOptions.validateVariableReferences && visit == PreVisit) if (visit == PreVisit)
{ {
bool validateStructUsage = mOptions.validateStructUsage;
for (TIntermNode *instance : sequence) for (TIntermNode *instance : sequence)
{ {
TIntermSymbol *symbol = instance->getAsSymbolNode(); TIntermSymbol *symbol = instance->getAsSymbolNode();
...@@ -381,6 +521,8 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) ...@@ -381,6 +521,8 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node)
const TVariable *variable = &symbol->variable(); const TVariable *variable = &symbol->variable();
if (mOptions.validateVariableReferences)
{
if (isVariableDeclared(variable)) if (isVariableDeclared(variable))
{ {
mDiagnostics->error( mDiagnostics->error(
...@@ -397,15 +539,26 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node) ...@@ -397,15 +539,26 @@ bool ValidateAST::visitDeclaration(Visit visit, TIntermDeclaration *node)
if (variable->symbolType() == SymbolType::Empty && interfaceBlock != nullptr) if (variable->symbolType() == SymbolType::Empty && interfaceBlock != nullptr)
{ {
// Nameless interface blocks can only be declared at the top level. Their fields // Nameless interface blocks can only be declared at the top level. Their
// are matched by field index, and then verified to match by name. Conflict in // fields are matched by field index, and then verified to match by name.
// names should have already generated a compile error. // Conflict in names should have already generated a compile error.
ASSERT(mDeclaredVariables.size() == 1); ASSERT(mDeclaredVariables.size() == 1);
ASSERT(mNamelessInterfaceBlocks.count(interfaceBlock) == 0); ASSERT(mNamelessInterfaceBlocks.count(interfaceBlock) == 0);
mNamelessInterfaceBlocks.insert(interfaceBlock); mNamelessInterfaceBlocks.insert(interfaceBlock);
} }
} }
if (validateStructUsage)
{
// Only declare the struct once.
validateStructUsage = false;
const TType &type = variable->getType();
if (type.isStructSpecifier() || type.isInterfaceBlock())
visitStructOrInterfaceBlockDeclaration(type, node->getLine());
}
}
} }
return true; return true;
...@@ -431,7 +584,7 @@ void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node) ...@@ -431,7 +584,7 @@ void ValidateAST::visitPreprocessorDirective(TIntermPreprocessorDirective *node)
bool ValidateAST::validateInternal() bool ValidateAST::validateInternal()
{ {
return !mSingleParentFailed && !mVariableReferencesFailed && !mNullNodesFailed && return !mSingleParentFailed && !mVariableReferencesFailed && !mNullNodesFailed &&
!mMultiDeclarationsFailed; !mStructUsageFailed && !mMultiDeclarationsFailed;
} }
} // anonymous namespace } // anonymous namespace
......
...@@ -42,9 +42,11 @@ struct ValidateASTOptions ...@@ -42,9 +42,11 @@ struct ValidateASTOptions
// Check that there is only one TFunction with each function name referenced in the nodes (no // Check that there is only one TFunction with each function name referenced in the nodes (no
// two TFunctions with the same name, taking internal/non-internal namespaces into account). // two TFunctions with the same name, taking internal/non-internal namespaces into account).
bool validateUniqueFunctions = true; // TODO bool validateUniqueFunctions = true; // TODO
// Check that references to user-defined structs are matched with the corresponding struct // Check that references to structs are matched with the corresponding struct declaration. This
// is only done for references to structs inside other struct or interface blocks declarations,
// as validateVariableReferences already ensures other references to the struct match the
// declaration. // declaration.
bool validateStructUsage = true; // TODO bool validateStructUsage = true;
// Check that expression nodes have the correct type considering their operand(s). // Check that expression nodes have the correct type considering their operand(s).
bool validateExpressionTypes = true; // TODO bool validateExpressionTypes = true; // TODO
// If SeparateDeclarations has been run, check for the absence of multi declarations as well. // If SeparateDeclarations has been run, check for the absence of multi declarations as well.
......
...@@ -24,29 +24,13 @@ class TPrecisionTraverser : public TIntermTraverser ...@@ -24,29 +24,13 @@ class TPrecisionTraverser : public TIntermTraverser
protected: protected:
bool visitDeclaration(Visit visit, TIntermDeclaration *node) override; bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
bool structDeclared(const TStructure *structure) const;
void overwriteVariablePrecision(TType *type) const; void overwriteVariablePrecision(TType *type) const;
private:
// This set contains all the ids of the structs from every scope.
std::set<int> mDeclaredStructs;
}; };
TPrecisionTraverser::TPrecisionTraverser(TSymbolTable *symbolTable) TPrecisionTraverser::TPrecisionTraverser(TSymbolTable *symbolTable)
: TIntermTraverser(true, true, true, symbolTable) : TIntermTraverser(true, true, true, symbolTable)
{} {}
bool TPrecisionTraverser::structDeclared(const TStructure *structure) const
{
ASSERT(structure);
if (structure->symbolType() == SymbolType::Empty)
{
return false;
}
return (mDeclaredStructs.count(structure->uniqueId().get()) > 0);
}
void TPrecisionTraverser::overwriteVariablePrecision(TType *type) const void TPrecisionTraverser::overwriteVariablePrecision(TType *type) const
{ {
if (type->getPrecision() == EbpHigh) if (type->getPrecision() == EbpHigh)
...@@ -71,8 +55,8 @@ bool TPrecisionTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node ...@@ -71,8 +55,8 @@ bool TPrecisionTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node
return true; return true;
} }
// Visit the struct if we have not done so already. // Visit the struct.
if (type.getBasicType() == EbtStruct && !structDeclared(type.getStruct())) if (type.isStructSpecifier())
{ {
const TStructure *structure = type.getStruct(); const TStructure *structure = type.getStruct();
const TFieldList &fields = structure->fields(); const TFieldList &fields = structure->fields();
...@@ -82,7 +66,6 @@ bool TPrecisionTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node ...@@ -82,7 +66,6 @@ bool TPrecisionTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node
const TType *fieldType = field->type(); const TType *fieldType = field->type();
overwriteVariablePrecision((TType *)fieldType); overwriteVariablePrecision((TType *)fieldType);
} }
mDeclaredStructs.insert(structure->uniqueId().get());
} }
else if (type.getBasicType() == EbtInterfaceBlock) else if (type.getBasicType() == EbtInterfaceBlock)
{ {
......
...@@ -24,6 +24,7 @@ class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser ...@@ -24,6 +24,7 @@ class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser
{ {
public: public:
RemoveInactiveInterfaceVariablesTraverser( RemoveInactiveInterfaceVariablesTraverser(
TSymbolTable *symbolTable,
const std::vector<sh::ShaderVariable> &attributes, const std::vector<sh::ShaderVariable> &attributes,
const std::vector<sh::ShaderVariable> &inputVaryings, const std::vector<sh::ShaderVariable> &inputVaryings,
const std::vector<sh::ShaderVariable> &outputVariables, const std::vector<sh::ShaderVariable> &outputVariables,
...@@ -41,12 +42,13 @@ class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser ...@@ -41,12 +42,13 @@ class RemoveInactiveInterfaceVariablesTraverser : public TIntermTraverser
}; };
RemoveInactiveInterfaceVariablesTraverser::RemoveInactiveInterfaceVariablesTraverser( RemoveInactiveInterfaceVariablesTraverser::RemoveInactiveInterfaceVariablesTraverser(
TSymbolTable *symbolTable,
const std::vector<sh::ShaderVariable> &attributes, const std::vector<sh::ShaderVariable> &attributes,
const std::vector<sh::ShaderVariable> &inputVaryings, const std::vector<sh::ShaderVariable> &inputVaryings,
const std::vector<sh::ShaderVariable> &outputVariables, const std::vector<sh::ShaderVariable> &outputVariables,
const std::vector<sh::ShaderVariable> &uniforms, const std::vector<sh::ShaderVariable> &uniforms,
const std::vector<sh::InterfaceBlock> &interfaceBlocks) const std::vector<sh::InterfaceBlock> &interfaceBlocks)
: TIntermTraverser(true, false, false), : TIntermTraverser(true, false, false, symbolTable),
mAttributes(attributes), mAttributes(attributes),
mInputVaryings(inputVaryings), mInputVaryings(inputVaryings),
mOutputVariables(outputVariables), mOutputVariables(outputVariables),
...@@ -128,9 +130,21 @@ bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit, ...@@ -128,9 +130,21 @@ bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit,
if (removeDeclaration) if (removeDeclaration)
{ {
TIntermSequence emptySequence; TIntermSequence replacement;
// If the declaration was of a struct, keep the struct declaration itself.
if (type.isStructSpecifier())
{
TType *structSpecifierType = new TType(type.getStruct(), true);
TVariable *emptyVariable = new TVariable(mSymbolTable, kEmptyImmutableString,
structSpecifierType, SymbolType::Empty);
TIntermDeclaration *declaration = new TIntermDeclaration();
declaration->appendDeclarator(new TIntermSymbol(emptyVariable));
replacement.push_back(declaration);
}
mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node, mMultiReplacements.emplace_back(getParentNode()->getAsBlock(), node,
std::move(emptySequence)); std::move(replacement));
} }
return false; return false;
...@@ -140,14 +154,15 @@ bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit, ...@@ -140,14 +154,15 @@ bool RemoveInactiveInterfaceVariablesTraverser::visitDeclaration(Visit visit,
bool RemoveInactiveInterfaceVariables(TCompiler *compiler, bool RemoveInactiveInterfaceVariables(TCompiler *compiler,
TIntermBlock *root, TIntermBlock *root,
TSymbolTable *symbolTable,
const std::vector<sh::ShaderVariable> &attributes, const std::vector<sh::ShaderVariable> &attributes,
const std::vector<sh::ShaderVariable> &inputVaryings, const std::vector<sh::ShaderVariable> &inputVaryings,
const std::vector<sh::ShaderVariable> &outputVariables, const std::vector<sh::ShaderVariable> &outputVariables,
const std::vector<sh::ShaderVariable> &uniforms, const std::vector<sh::ShaderVariable> &uniforms,
const std::vector<sh::InterfaceBlock> &interfaceBlocks) const std::vector<sh::InterfaceBlock> &interfaceBlocks)
{ {
RemoveInactiveInterfaceVariablesTraverser traverser(attributes, inputVaryings, outputVariables, RemoveInactiveInterfaceVariablesTraverser traverser(symbolTable, attributes, inputVaryings,
uniforms, interfaceBlocks); outputVariables, uniforms, interfaceBlocks);
root->traverse(&traverser); root->traverse(&traverser);
return traverser.updateTree(compiler, root); return traverser.updateTree(compiler, root);
} }
......
...@@ -29,6 +29,7 @@ class TSymbolTable; ...@@ -29,6 +29,7 @@ class TSymbolTable;
ANGLE_NO_DISCARD bool RemoveInactiveInterfaceVariables( ANGLE_NO_DISCARD bool RemoveInactiveInterfaceVariables(
TCompiler *compiler, TCompiler *compiler,
TIntermBlock *root, TIntermBlock *root,
TSymbolTable *symbolTable,
const std::vector<sh::ShaderVariable> &attributes, const std::vector<sh::ShaderVariable> &attributes,
const std::vector<sh::ShaderVariable> &inputVaryings, const std::vector<sh::ShaderVariable> &inputVaryings,
const std::vector<sh::ShaderVariable> &outputVariables, const std::vector<sh::ShaderVariable> &outputVariables,
......
...@@ -71,7 +71,7 @@ bool DriverUniform::addComputeDriverUniformsToShader(TIntermBlock *root, TSymbol ...@@ -71,7 +71,7 @@ bool DriverUniform::addComputeDriverUniformsToShader(TIntermBlock *root, TSymbol
return mDriverUniforms != nullptr; return mDriverUniforms != nullptr;
} }
TFieldList *DriverUniform::createUniformFields(TSymbolTable *symbolTable) const TFieldList *DriverUniform::createUniformFields(TSymbolTable *symbolTable)
{ {
constexpr size_t kNumGraphicsDriverUniforms = 8; constexpr size_t kNumGraphicsDriverUniforms = 8;
constexpr std::array<const char *, kNumGraphicsDriverUniforms> kGraphicsDriverUniformNames = { constexpr std::array<const char *, kNumGraphicsDriverUniforms> kGraphicsDriverUniformNames = {
...@@ -104,9 +104,15 @@ TFieldList *DriverUniform::createUniformFields(TSymbolTable *symbolTable) const ...@@ -104,9 +104,15 @@ TFieldList *DriverUniform::createUniformFields(TSymbolTable *symbolTable) const
return driverFieldList; return driverFieldList;
} }
TType *DriverUniform::createEmulatedDepthRangeType(TSymbolTable *symbolTable) const TType *DriverUniform::createEmulatedDepthRangeType(TSymbolTable *symbolTable)
{ {
// Init the depth range type. // If already defined, return it immediately.
if (mEmulatedDepthRangeType != nullptr)
{
return mEmulatedDepthRangeType;
}
// Create the depth range type.
TFieldList *depthRangeParamsFields = new TFieldList(); TFieldList *depthRangeParamsFields = new TFieldList();
depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1), depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1),
ImmutableString("near"), TSourceLoc(), ImmutableString("near"), TSourceLoc(),
...@@ -125,9 +131,11 @@ TType *DriverUniform::createEmulatedDepthRangeType(TSymbolTable *symbolTable) co ...@@ -125,9 +131,11 @@ TType *DriverUniform::createEmulatedDepthRangeType(TSymbolTable *symbolTable) co
TStructure *emulatedDepthRangeParams = new TStructure( TStructure *emulatedDepthRangeParams = new TStructure(
symbolTable, kEmulatedDepthRangeParams, depthRangeParamsFields, SymbolType::AngleInternal); symbolTable, kEmulatedDepthRangeParams, depthRangeParamsFields, SymbolType::AngleInternal);
TType *emulatedDepthRangeType = new TType(emulatedDepthRangeParams, false); mEmulatedDepthRangeType = new TType(emulatedDepthRangeParams, false);
return emulatedDepthRangeType; // Note: this should really return a const TType *, but one of its uses is with TField who takes
// a non-const TType. See comment on that class.
return mEmulatedDepthRangeType;
} }
// The Add*DriverUniformsToShader operation adds an internal uniform block to a shader. The driver // The Add*DriverUniformsToShader operation adds an internal uniform block to a shader. The driver
...@@ -139,11 +147,13 @@ bool DriverUniform::addGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbo ...@@ -139,11 +147,13 @@ bool DriverUniform::addGraphicsDriverUniformsToShader(TIntermBlock *root, TSymbo
{ {
ASSERT(!mDriverUniforms); ASSERT(!mDriverUniforms);
// Declare the depth range struct type.
TType *emulatedDepthRangeType = createEmulatedDepthRangeType(symbolTable); TType *emulatedDepthRangeType = createEmulatedDepthRangeType(symbolTable);
// Declare a global depth range variable. TType *emulatedDepthRangeDeclType = new TType(emulatedDepthRangeType->getStruct(), true);
TVariable *depthRangeVar = TVariable *depthRangeVar =
new TVariable(symbolTable->nextUniqueId(), kEmptyImmutableString, SymbolType::Empty, new TVariable(symbolTable->nextUniqueId(), kEmptyImmutableString, SymbolType::Empty,
TExtension::UNDEFINED, emulatedDepthRangeType); TExtension::UNDEFINED, emulatedDepthRangeDeclType);
DeclareGlobalVariable(root, depthRangeVar); DeclareGlobalVariable(root, depthRangeVar);
...@@ -220,7 +230,7 @@ TIntermBinary *DriverUniform::getNumSamplesRef() const ...@@ -220,7 +230,7 @@ TIntermBinary *DriverUniform::getNumSamplesRef() const
// //
// Class DriverUniformExtended // Class DriverUniformExtended
// //
TFieldList *DriverUniformExtended::createUniformFields(TSymbolTable *symbolTable) const TFieldList *DriverUniformExtended::createUniformFields(TSymbolTable *symbolTable)
{ {
TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable); TFieldList *driverFieldList = DriverUniform::createUniformFields(symbolTable);
......
...@@ -26,7 +26,7 @@ class TIntermBinary; ...@@ -26,7 +26,7 @@ class TIntermBinary;
class DriverUniform class DriverUniform
{ {
public: public:
DriverUniform() : mDriverUniforms(nullptr) {} DriverUniform() : mDriverUniforms(nullptr), mEmulatedDepthRangeType(nullptr) {}
virtual ~DriverUniform() = default; virtual ~DriverUniform() = default;
bool addComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable); bool addComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable);
...@@ -50,10 +50,11 @@ class DriverUniform ...@@ -50,10 +50,11 @@ class DriverUniform
protected: protected:
TIntermBinary *createDriverUniformRef(const char *fieldName) const; TIntermBinary *createDriverUniformRef(const char *fieldName) const;
virtual TFieldList *createUniformFields(TSymbolTable *symbolTable) const; virtual TFieldList *createUniformFields(TSymbolTable *symbolTable);
TType *createEmulatedDepthRangeType(TSymbolTable *symbolTable) const; TType *createEmulatedDepthRangeType(TSymbolTable *symbolTable);
const TVariable *mDriverUniforms; const TVariable *mDriverUniforms;
TType *mEmulatedDepthRangeType;
}; };
class DriverUniformExtended : public DriverUniform class DriverUniformExtended : public DriverUniform
...@@ -69,7 +70,7 @@ class DriverUniformExtended : public DriverUniform ...@@ -69,7 +70,7 @@ class DriverUniformExtended : public DriverUniform
TIntermSwizzle *getNegFlipYRef() const override; TIntermSwizzle *getNegFlipYRef() const override;
protected: protected:
virtual TFieldList *createUniformFields(TSymbolTable *symbolTable) const override; virtual TFieldList *createUniformFields(TSymbolTable *symbolTable) override;
}; };
} // namespace sh } // namespace sh
......
...@@ -673,12 +673,6 @@ void main() ...@@ -673,12 +673,6 @@ void main()
TEST_P(GLSLTest, ScopedStructsOrderBug) TEST_P(GLSLTest, ScopedStructsOrderBug)
{ {
// TODO(geofflang): Find out why this doesn't compile on Apple OpenGL drivers
// (http://anglebug.com/1292)
// TODO(geofflang): Find out why this doesn't compile on AMD OpenGL drivers
// (http://anglebug.com/1291)
ANGLE_SKIP_TEST_IF(IsDesktopOpenGL() && (IsOSX() || !IsNVIDIA()));
constexpr char kFS[] = R"(precision mediump float; constexpr char kFS[] = R"(precision mediump float;
struct T struct T
...@@ -705,6 +699,132 @@ void main() ...@@ -705,6 +699,132 @@ void main()
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
} }
// Test that defining a struct together with an inactive uniform, then using it in a scope that has
// another struct with the same name declared works.
TEST_P(GLSLTest, ScopedStructsOrderBug2)
{
constexpr char kFS[] = R"(precision mediump float;
uniform struct T
{
float f;
} x;
void main()
{
T a;
struct T
{
float q;
};
T b;
gl_FragColor = vec4(1, 0, 0, 1);
gl_FragColor.a += a.f;
gl_FragColor.a += b.q;
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS);
}
// Regression test based on WebGL's conformance/glsl/misc/empty-declaration.html
TEST_P(GLSLTest, StructEmptyDeclaratorBug)
{
constexpr char kVS[] = R"(
struct S {
float member;
}, a;
void main() {
a.member = 0.0;
gl_Position = vec4(a.member);
})";
constexpr char kFS[] = R"(precision mediump float;
precision mediump float;
void main()
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
}
// Regression test based on WebGL's conformance/ogles/GL/build/build_001_to_008.html
TEST_P(GLSLTest, StructConstantFoldingBug)
{
constexpr char kVS[] = R"(
void main()
{
const struct s2 {
int i;
vec3 v3;
bvec4 bv4;
} s22 = s2(8, vec3(9, 10, 11), bvec4(true, false, true, false));
struct s4 {
int ii;
vec4 v4;
};
const struct s1 {
s2 ss;
int i;
float f;
mat4 m;
s4 s44;
} s11 = s1(s22, 2, 4.0, mat4(5), s4(6, vec4(7, 8, 9, 10))) ;
const int field3 = s11.i * s11.ss.i; // constant folding (int * int)
const vec4 field4 = s11.s44.v4 * s11.s44.v4; // constant folding (vec4 * vec4)
// 49, 64, 81, 100
const vec4 v4 = vec4(s11.ss.v3.y, s11.m[3][3], field3, field4[2]); // 10.0, 5.0, 16.0, 81.0
gl_Position = v4;
})";
constexpr char kFS[] = R"(precision mediump float;
precision mediump float;
void main()
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
}
// Test that constant folding doesn't remove struct declaration.
TEST_P(GLSLTest, StructConstantFoldingBug2)
{
constexpr char kVS[] = R"(
uniform vec4 u;
void main()
{
const struct s2 {
int i;
vec3 v3;
bvec4 bv4;
} s22 = s2(8, vec3(9, 10, 11), bvec4(true, false, true, false));
s2 x;
x.v3 = u.xyz;
gl_Position = vec4(x.v3, float(s22.i));
})";
constexpr char kFS[] = R"(precision mediump float;
precision mediump float;
void main()
{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
}
TEST_P(GLSLTest, ScopedStructsBug) TEST_P(GLSLTest, ScopedStructsBug)
{ {
constexpr char kFS[] = R"(precision mediump float; constexpr char kFS[] = R"(precision mediump float;
......
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