1 | // |
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 separate |
7 | // function that is called in the beginning of main(). This enables initialization of globals with |
8 | // uniforms or non-constant globals, as allowed by the WebGL spec. Some initializers referencing |
9 | // non-constants may need to be unfolded into if statements in HLSL - this kind of steps should be |
10 | // done after DeferGlobalInitializers is run. Note that it's important that the function definition |
11 | // is at the end of the shader, as some globals may be declared after main(). |
12 | // |
13 | // It can also initialize all uninitialized globals. |
14 | // |
15 | |
16 | #include "compiler/translator/tree_ops/DeferGlobalInitializers.h" |
17 | |
18 | #include <vector> |
19 | |
20 | #include "compiler/translator/IntermNode.h" |
21 | #include "compiler/translator/StaticType.h" |
22 | #include "compiler/translator/SymbolTable.h" |
23 | #include "compiler/translator/tree_ops/InitializeVariables.h" |
24 | #include "compiler/translator/tree_util/FindMain.h" |
25 | #include "compiler/translator/tree_util/IntermNode_util.h" |
26 | #include "compiler/translator/tree_util/ReplaceVariable.h" |
27 | |
28 | namespace sh |
29 | { |
30 | |
31 | namespace |
32 | { |
33 | |
34 | constexpr const ImmutableString kInitGlobalsString("initGlobals" ); |
35 | |
36 | void GetDeferredInitializers(TIntermDeclaration *declaration, |
37 | bool initializeUninitializedGlobals, |
38 | bool canUseLoopsToInitialize, |
39 | bool highPrecisionSupported, |
40 | TIntermSequence *deferredInitializersOut, |
41 | std::vector<const TVariable *> *variablesToReplaceOut, |
42 | TSymbolTable *symbolTable) |
43 | { |
44 | // SeparateDeclarations should have already been run. |
45 | ASSERT(declaration->getSequence()->size() == 1); |
46 | |
47 | TIntermNode *declarator = declaration->getSequence()->back(); |
48 | TIntermBinary *init = declarator->getAsBinaryNode(); |
49 | if (init) |
50 | { |
51 | TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode(); |
52 | ASSERT(symbolNode); |
53 | TIntermTyped *expression = init->getRight(); |
54 | |
55 | if (expression->getQualifier() != EvqConst || !expression->hasConstantValue()) |
56 | { |
57 | // For variables which are not constant, defer their real initialization until |
58 | // after we initialize uniforms. |
59 | // Deferral is done also in any cases where the variable can not be converted to a |
60 | // constant union, since otherwise there's a chance that HLSL output will generate extra |
61 | // statements from the initializer expression. |
62 | |
63 | // Change const global to a regular global if its initialization is deferred. |
64 | // This can happen if ANGLE has not been able to fold the constant expression used |
65 | // as an initializer. |
66 | ASSERT(symbolNode->getQualifier() == EvqConst || |
67 | symbolNode->getQualifier() == EvqGlobal); |
68 | if (symbolNode->getQualifier() == EvqConst) |
69 | { |
70 | variablesToReplaceOut->push_back(&symbolNode->variable()); |
71 | } |
72 | |
73 | TIntermBinary *deferredInit = |
74 | new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight()); |
75 | deferredInitializersOut->push_back(deferredInit); |
76 | |
77 | // Remove the initializer from the global scope and just declare the global instead. |
78 | declaration->replaceChildNode(init, symbolNode); |
79 | } |
80 | } |
81 | else if (initializeUninitializedGlobals) |
82 | { |
83 | TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); |
84 | ASSERT(symbolNode); |
85 | |
86 | // Ignore ANGLE internal variables and nameless declarations. |
87 | if (symbolNode->variable().symbolType() == SymbolType::AngleInternal || |
88 | symbolNode->variable().symbolType() == SymbolType::Empty) |
89 | return; |
90 | |
91 | if (symbolNode->getQualifier() == EvqGlobal) |
92 | { |
93 | TIntermSequence *initCode = CreateInitCode(symbolNode, canUseLoopsToInitialize, |
94 | highPrecisionSupported, symbolTable); |
95 | deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(), |
96 | initCode->end()); |
97 | } |
98 | } |
99 | } |
100 | |
101 | void InsertInitCallToMain(TIntermBlock *root, |
102 | TIntermSequence *deferredInitializers, |
103 | TSymbolTable *symbolTable) |
104 | { |
105 | TIntermBlock *initGlobalsBlock = new TIntermBlock(); |
106 | initGlobalsBlock->getSequence()->swap(*deferredInitializers); |
107 | |
108 | TFunction *initGlobalsFunction = |
109 | new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal, |
110 | StaticType::GetBasic<EbtVoid>(), false); |
111 | |
112 | TIntermFunctionPrototype *initGlobalsFunctionPrototype = |
113 | CreateInternalFunctionPrototypeNode(*initGlobalsFunction); |
114 | root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype); |
115 | TIntermFunctionDefinition *initGlobalsFunctionDefinition = |
116 | CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock); |
117 | root->appendStatement(initGlobalsFunctionDefinition); |
118 | |
119 | TIntermAggregate *initGlobalsCall = |
120 | TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, new TIntermSequence()); |
121 | |
122 | TIntermBlock *mainBody = FindMainBody(root); |
123 | mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall); |
124 | } |
125 | |
126 | } // namespace |
127 | |
128 | void DeferGlobalInitializers(TIntermBlock *root, |
129 | bool initializeUninitializedGlobals, |
130 | bool canUseLoopsToInitialize, |
131 | bool highPrecisionSupported, |
132 | TSymbolTable *symbolTable) |
133 | { |
134 | TIntermSequence *deferredInitializers = new TIntermSequence(); |
135 | std::vector<const TVariable *> variablesToReplace; |
136 | |
137 | // Loop over all global statements and process the declarations. This is simpler than using a |
138 | // traverser. |
139 | for (TIntermNode *statement : *root->getSequence()) |
140 | { |
141 | TIntermDeclaration *declaration = statement->getAsDeclarationNode(); |
142 | if (declaration) |
143 | { |
144 | GetDeferredInitializers(declaration, initializeUninitializedGlobals, |
145 | canUseLoopsToInitialize, highPrecisionSupported, |
146 | deferredInitializers, &variablesToReplace, symbolTable); |
147 | } |
148 | } |
149 | |
150 | // Add the function with initialization and the call to that. |
151 | if (!deferredInitializers->empty()) |
152 | { |
153 | InsertInitCallToMain(root, deferredInitializers, symbolTable); |
154 | } |
155 | |
156 | // Replace constant variables with non-constant global variables. |
157 | for (const TVariable *var : variablesToReplace) |
158 | { |
159 | TType *replacementType = new TType(var->getType()); |
160 | replacementType->setQualifier(EvqGlobal); |
161 | TVariable *replacement = |
162 | new TVariable(symbolTable, var->name(), replacementType, var->symbolType()); |
163 | ReplaceVariable(root, var, replacement); |
164 | } |
165 | } |
166 | |
167 | } // namespace sh |
168 | |