summaryrefslogtreecommitdiffstats
path: root/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp')
-rwxr-xr-xgfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp194
1 files changed, 194 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
new file mode 100755
index 000000000..1c58562fc
--- /dev/null
+++ b/gfx/angle/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -0,0 +1,194 @@
+//
+// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// DeferGlobalInitializers is an AST traverser that moves global initializers into a function, and
+// adds a function call to that function in the beginning of main().
+// This enables initialization of globals with uniforms or non-constant globals, as allowed by
+// the WebGL spec. Some initializers referencing non-constants may need to be unfolded into if
+// statements in HLSL - this kind of steps should be done after DeferGlobalInitializers is run.
+//
+
+#include "compiler/translator/DeferGlobalInitializers.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+void SetInternalFunctionName(TFunctionSymbolInfo *functionInfo, const char *name)
+{
+ TString nameStr(name);
+ nameStr = TFunction::mangleName(nameStr);
+ TName nameObj(nameStr);
+ nameObj.setInternal(true);
+ functionInfo->setNameObj(nameObj);
+}
+
+TIntermAggregate *CreateFunctionPrototypeNode(const char *name, const int functionId)
+{
+ TIntermAggregate *functionNode = new TIntermAggregate(EOpPrototype);
+
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ TType returnType(EbtVoid);
+ functionNode->setType(returnType);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+TIntermFunctionDefinition *CreateFunctionDefinitionNode(const char *name,
+ TIntermBlock *functionBody,
+ const int functionId)
+{
+ TType returnType(EbtVoid);
+ TIntermAggregate *paramsNode = new TIntermAggregate(EOpParameters);
+ TIntermFunctionDefinition *functionNode =
+ new TIntermFunctionDefinition(returnType, paramsNode, functionBody);
+
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+TIntermAggregate *CreateFunctionCallNode(const char *name, const int functionId)
+{
+ TIntermAggregate *functionNode = new TIntermAggregate(EOpFunctionCall);
+
+ functionNode->setUserDefined();
+ SetInternalFunctionName(functionNode->getFunctionSymbolInfo(), name);
+ TType returnType(EbtVoid);
+ functionNode->setType(returnType);
+ functionNode->getFunctionSymbolInfo()->setId(functionId);
+ return functionNode;
+}
+
+class DeferGlobalInitializersTraverser : public TIntermTraverser
+{
+ 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)
+ {
+ TIntermSymbol *symbolNode = node->getLeft()->getAsSymbolNode();
+ ASSERT(symbolNode);
+ TIntermTyped *expression = node->getRight();
+
+ if (mInGlobalScope && (expression->getQualifier() != EvqConst ||
+ (expression->getAsConstantUnion() == nullptr &&
+ !expression->isConstructorWithOnlyConstantUnionParameters())))
+ {
+ // 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)
+ {
+ // All of the siblings in the same declaration need to have consistent qualifiers.
+ auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
+ for (TIntermNode *siblingNode : *siblings)
+ {
+ TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
+ if (siblingBinary)
+ {
+ ASSERT(siblingBinary->getOp() == EOpInitialize);
+ siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
+ }
+ siblingNode->getAsTyped()->getTypePointer()->setQualifier(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.
+ queueReplacement(node, symbolNode, OriginalNode::IS_DROPPED);
+ }
+ }
+ return false;
+}
+
+void DeferGlobalInitializersTraverser::insertInitFunction(TIntermBlock *root)
+{
+ if (mDeferredInitializers.empty())
+ {
+ return;
+ }
+ const int initFunctionId = TSymbolTable::nextUniqueId();
+
+ const char *functionName = "initializeDeferredGlobals";
+
+ // Add function prototype to the beginning of the shader
+ TIntermAggregate *functionPrototypeNode =
+ CreateFunctionPrototypeNode(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 =
+ CreateFunctionDefinitionNode(functionName, functionBodyNode, initFunctionId);
+ root->getSequence()->push_back(functionDefinition);
+
+ // Insert call into main function
+ for (TIntermNode *node : *root->getSequence())
+ {
+ TIntermFunctionDefinition *nodeFunction = node->getAsFunctionDefinition();
+ if (nodeFunction != nullptr && nodeFunction->getFunctionSymbolInfo()->isMain())
+ {
+ TIntermAggregate *functionCallNode =
+ CreateFunctionCallNode(functionName, initFunctionId);
+
+ TIntermBlock *mainBody = nodeFunction->getBody();
+ ASSERT(mainBody != nullptr);
+ mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
+ }
+ }
+}
+
+} // namespace
+
+void DeferGlobalInitializers(TIntermBlock *root)
+{
+ DeferGlobalInitializersTraverser traverser;
+ root->traverse(&traverser);
+
+ // Replace the initializers of the global variables.
+ traverser.updateTree();
+
+ // Add the function with initialization and the call to that.
+ traverser.insertInitFunction(root);
+}
+
+} // namespace sh