blob: cdc8a378a7141c4544782ee4090993a2b5055309 [file] [log] [blame]
Olli Etuaho3d932d82016-04-12 11:10:30 +03001//
2// Copyright (c) 2016 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6// DeferGlobalInitializers is an AST traverser that moves global initializers into a function, and
7// adds a function call to that function in the beginning of main().
8// This enables initialization of globals with uniforms or non-constant globals, as allowed by
9// the WebGL spec. Some initializers referencing non-constants may need to be unfolded into if
10// statements in HLSL - this kind of steps should be done after DeferGlobalInitializers is run.
11//
12
13#include "compiler/translator/DeferGlobalInitializers.h"
14
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030015#include "compiler/translator/FindMain.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030016#include "compiler/translator/IntermNode.h"
17#include "compiler/translator/SymbolTable.h"
18
Jamie Madill45bcc782016-11-07 13:58:48 -050019namespace sh
20{
21
Olli Etuaho3d932d82016-04-12 11:10:30 +030022namespace
23{
24
Olli Etuaho1dd07a62017-05-12 17:01:22 +030025void GetDeferredInitializers(TIntermDeclaration *declaration,
26 TIntermSequence *deferredInitializersOut)
Olli Etuaho3d932d82016-04-12 11:10:30 +030027{
Olli Etuaho1dd07a62017-05-12 17:01:22 +030028 // We iterate with an index instead of using an iterator since we're replacing the children of
29 // declaration inside the loop.
30 for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
Olli Etuaho3d932d82016-04-12 11:10:30 +030031 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030032 TIntermNode *declarator = declaration->getSequence()->at(i);
33 TIntermBinary *init = declarator->getAsBinaryNode();
34 if (init)
Olli Etuaho3d932d82016-04-12 11:10:30 +030035 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030036 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
37 ASSERT(symbolNode);
38 TIntermTyped *expression = init->getRight();
Olli Etuaho3d932d82016-04-12 11:10:30 +030039
Olli Etuaho1dd07a62017-05-12 17:01:22 +030040 if ((expression->getQualifier() != EvqConst ||
41 (expression->getAsConstantUnion() == nullptr &&
42 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuahod4f4c112016-04-15 15:11:24 +030043 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030044 // For variables which are not constant, defer their real initialization until
45 // after we initialize uniforms.
46 // Deferral is done also in any cases where the variable has not been constant
47 // folded, since otherwise there's a chance that HLSL output will generate extra
48 // statements from the initializer expression.
49 TIntermBinary *deferredInit =
50 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
51 deferredInitializersOut->push_back(deferredInit);
52
53 // Change const global to a regular global if its initialization is deferred.
54 // This can happen if ANGLE has not been able to fold the constant expression used
55 // as an initializer.
56 ASSERT(symbolNode->getQualifier() == EvqConst ||
57 symbolNode->getQualifier() == EvqGlobal);
58 if (symbolNode->getQualifier() == EvqConst)
Olli Etuahod4f4c112016-04-15 15:11:24 +030059 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030060 // All of the siblings in the same declaration need to have consistent
61 // qualifiers.
62 auto *siblings = declaration->getSequence();
63 for (TIntermNode *siblingNode : *siblings)
Olli Etuahod4f4c112016-04-15 15:11:24 +030064 {
Olli Etuaho1dd07a62017-05-12 17:01:22 +030065 TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
66 if (siblingBinary)
67 {
68 ASSERT(siblingBinary->getOp() == EOpInitialize);
69 siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
70 }
71 siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030072 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030073 // This node is one of the siblings.
74 ASSERT(symbolNode->getQualifier() == EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030075 }
Olli Etuaho1dd07a62017-05-12 17:01:22 +030076 // Remove the initializer from the global scope and just declare the global instead.
77 declaration->replaceChildNode(init, symbolNode);
Olli Etuahod4f4c112016-04-15 15:11:24 +030078 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030079 }
80 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030081}
82
Olli Etuaho1dd07a62017-05-12 17:01:22 +030083void InsertInitFunction(TIntermBlock *root, TIntermSequence *deferredInitializers)
Olli Etuaho3d932d82016-04-12 11:10:30 +030084{
Olli Etuahofe486322017-03-21 09:30:54 +000085 TSymbolUniqueId initFunctionId;
Olli Etuaho1dd07a62017-05-12 17:01:22 +030086 const char *functionName = "initializeGlobals";
Olli Etuaho3d932d82016-04-12 11:10:30 +030087
88 // Add function prototype to the beginning of the shader
Olli Etuaho16c745a2017-01-16 17:02:27 +000089 TIntermFunctionPrototype *functionPrototypeNode =
Olli Etuaho1dd07a62017-05-12 17:01:22 +030090 TIntermTraverser::CreateInternalFunctionPrototypeNode(TType(EbtVoid), functionName,
91 initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010092 root->getSequence()->insert(root->getSequence()->begin(), functionPrototypeNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +030093
94 // Add function definition to the end of the shader
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010095 TIntermBlock *functionBodyNode = new TIntermBlock();
Olli Etuaho1dd07a62017-05-12 17:01:22 +030096 functionBodyNode->getSequence()->swap(*deferredInitializers);
97 TIntermFunctionDefinition *functionDefinition =
98 TIntermTraverser::CreateInternalFunctionDefinitionNode(TType(EbtVoid), functionName,
99 functionBodyNode, initFunctionId);
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100100 root->getSequence()->push_back(functionDefinition);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300101
102 // Insert call into main function
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300103 TIntermFunctionDefinition *main = FindMain(root);
104 ASSERT(main != nullptr);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300105 TIntermAggregate *functionCallNode = TIntermTraverser::CreateInternalFunctionCallNode(
106 TType(EbtVoid), functionName, initFunctionId, nullptr);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300107
Olli Etuaho9cbc07c2017-05-10 18:22:01 +0300108 TIntermBlock *mainBody = main->getBody();
109 ASSERT(mainBody != nullptr);
110 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), functionCallNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300111}
112
113} // namespace
114
Olli Etuaho6d40bbd2016-09-30 13:49:38 +0100115void DeferGlobalInitializers(TIntermBlock *root)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300116{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300117 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300118
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300119 // Loop over all global statements and process the declarations. This is simpler than using a
120 // traverser.
121 for (TIntermNode *declaration : *root->getSequence())
122 {
123 TIntermDeclaration *asVariableDeclaration = declaration->getAsDeclarationNode();
124 if (asVariableDeclaration)
125 {
126 GetDeferredInitializers(asVariableDeclaration, deferredInitializers);
127 }
128 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300129
130 // Add the function with initialization and the call to that.
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300131 if (!deferredInitializers->empty())
132 {
133 InsertInitFunction(root, deferredInitializers);
134 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300135}
Jamie Madill45bcc782016-11-07 13:58:48 -0500136
137} // namespace sh