Commit 1dd07a64 by Olli Etuaho Committed by Commit Bot

Simplify DeferGlobalInitializers

It doesn't need to use a traverser, it's simpler to just iterate over all statements in the global scope. BUG=angleproject:1966 TEST=angle_unittests, WebGL conformance tests Change-Id: I11200f72842db86be2bcdd11934262da183cc3b4 Reviewed-on: https://chromium-review.googlesource.com/504727 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 231c7f56
......@@ -22,105 +22,88 @@ namespace sh
namespace
{
class DeferGlobalInitializersTraverser : public TIntermTraverser
void GetDeferredInitializers(TIntermDeclaration *declaration,
TIntermSequence *deferredInitializersOut)
{
public:
DeferGlobalInitializersTraverser();
bool visitBinary(Visit visit, TIntermBinary *node) override;
void insertInitFunction(TIntermBlock *root);
private:
TIntermSequence mDeferredInitializers;
};
DeferGlobalInitializersTraverser::DeferGlobalInitializersTraverser()
: TIntermTraverser(true, false, false)
{
}
bool DeferGlobalInitializersTraverser::visitBinary(Visit visit, TIntermBinary *node)
{
if (node->getOp() == EOpInitialize)
// We iterate with an index instead of using an iterator since we're replacing the children of
// declaration inside the loop.
for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
{
TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
ASSERT(symbolNode);
TIntermTyped *expression = node->getRight();
if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
(expression->getAsConstantUnion() == nullptr &&
!expression->isConstructorWithOnlyConstantUnionParameters())))
TIntermNode *declarator = declaration->getSequence()->at(i);
TIntermBinary *init = declarator->getAsBinaryNode();
if (init)
{
// For variables which are not constant, defer their real initialization until
// after we initialize uniforms.
// Deferral is done also in any cases where the variable has not been constant folded,
// since otherwise there's a chance that HLSL output will generate extra statements
// from the initializer expression.
TIntermBinary *deferredInit =
new TIntermBinary(EOpAssign, symbolNode->deepCopy(), node->getRight());
mDeferredInitializers.push_back(deferredInit);
// Change const global to a regular global if its initialization is deferred.
// This can happen if ANGLE has not been able to fold the constant expression used
// as an initializer.
ASSERT(symbolNode->getQualifier() == EvqConst ||
symbolNode->getQualifier() == EvqGlobal);
if (symbolNode->getQualifier() == EvqConst)
TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
ASSERT(symbolNode);
TIntermTyped *expression = init->getRight();
if ((expression->getQualifier() != EvqConst ||
(expression->getAsConstantUnion() == nullptr &&
!expression->isConstructorWithOnlyConstantUnionParameters())))
{
// All of the siblings in the same declaration need to have consistent qualifiers.
auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
for (TIntermNode *siblingNode : *siblings)
// For variables which are not constant, defer their real initialization until
// after we initialize uniforms.
// Deferral is done also in any cases where the variable has not been constant
// folded, since otherwise there's a chance that HLSL output will generate extra
// statements from the initializer expression.
TIntermBinary *deferredInit =
new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
deferredInitializersOut->push_back(deferredInit);
// Change const global to a regular global if its initialization is deferred.
// This can happen if ANGLE has not been able to fold the constant expression used
// as an initializer.
ASSERT(symbolNode->getQualifier() == EvqConst ||
symbolNode->getQualifier() == EvqGlobal);
if (symbolNode->getQualifier() == EvqConst)
{
TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
if (siblingBinary)
// All of the siblings in the same declaration need to have consistent
// qualifiers.
auto *siblings = declaration->getSequence();
for (TIntermNode *siblingNode : *siblings)
{
ASSERT(siblingBinary->getOp() == EOpInitialize);
siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
if (siblingBinary)
{
ASSERT(siblingBinary->getOp() == EOpInitialize);
siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
}
siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
}
siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
// This node is one of the siblings.
ASSERT(symbolNode->getQualifier() == EvqGlobal);
}
// This node is one of the siblings.
ASSERT(symbolNode->getQualifier() == EvqGlobal);
// Remove the initializer from the global scope and just declare the global instead.
declaration->replaceChildNode(init, symbolNode);
}
// Remove the initializer from the global scope and just declare the global instead.
queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
}
}
return false;
}
void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
void InsertInitFunction(TIntermBlock *root, TIntermSequence *deferredInitializers)
{
if (mDeferredInitializers.empty())
{
return;
}
TSymbolUniqueId initFunctionId;
const char *functionName = "initializeDeferredGlobals";
const char *functionName = "initializeGlobals";
// Add function prototype to the beginning of the shader
TIntermFunctionPrototype *functionPrototypeNode =
CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName, initFunctionId);
TIntermTraverser::CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName,
initFunctionId);
root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
// Add function definition to the end of the shader
TIntermBlock *functionBodyNode = new TIntermBlock();
TIntermSequence *functionBody = functionBodyNode->getSequence();
for (const auto &deferredInit : mDeferredInitializers)
{
functionBody->push_back(deferredInit);
}
TIntermFunctionDefinition *functionDefinition = CreateInternalFunctionDefinitionNode(
TType(EbtVoid), functionName, functionBodyNode, initFunctionId);
functionBodyNode->getSequence()->swap(*deferredInitializers);
TIntermFunctionDefinition *functionDefinition =
TIntermTraverser::CreateInternalFunctionDefinitionNode(TType(EbtVoid), functionName,
functionBodyNode, initFunctionId);
root->getSequence()->push_back(functionDefinition);
// Insert call into main function
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermAggregate *functionCallNode =
CreateInternalFunctionCallNode(TType(EbtVoid), functionName, initFunctionId, nullptr);
TIntermAggregate *functionCallNode = TIntermTraverser::CreateInternalFunctionCallNode(
TType(EbtVoid), functionName, initFunctionId, nullptr);
TIntermBlock *mainBody = main->getBody();
ASSERT(mainBody != nullptr);
......@@ -131,14 +114,24 @@ void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
void DeferGlobalInitializers(TIntermBlock *root)
{
DeferGlobalInitializersTraverser traverser;
root->traverse(&traverser);
TIntermSequence *deferredInitializers = new TIntermSequence();
// Replace the initializers of the global variables.
traverser.updateTree();
// Loop over all global statements and process the declarations. This is simpler than using a
// traverser.
for (TIntermNode *declaration : *root->getSequence())
{
TIntermDeclaration *asVariableDeclaration = declaration->getAsDeclarationNode();
if (asVariableDeclaration)
{
GetDeferredInitializers(asVariableDeclaration, deferredInitializers);
}
}
// Add the function with initialization and the call to that.
traverser.insertInitFunction(root);
if (!deferredInitializers->empty())
{
InsertInitFunction(root, deferredInitializers);
}
}
} // namespace sh
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