blob: 1cef1a5eba983dc697b2b25958909d4d74cf3d69 [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//
Olli Etuahoa16a84f2017-09-12 13:49:18 +03006// 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().
Olli Etuaho3d932d82016-04-12 11:10:30 +030012//
Olli Etuaho0ffc4412017-05-19 14:18:55 +030013// It can also initialize all uninitialized globals.
14//
Olli Etuaho3d932d82016-04-12 11:10:30 +030015
16#include "compiler/translator/DeferGlobalInitializers.h"
17
Olli Etuaho9cbc07c2017-05-10 18:22:01 +030018#include "compiler/translator/FindMain.h"
Olli Etuaho0ffc4412017-05-19 14:18:55 +030019#include "compiler/translator/InitializeVariables.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030020#include "compiler/translator/IntermNode.h"
Olli Etuahoa16a84f2017-09-12 13:49:18 +030021#include "compiler/translator/IntermNode_util.h"
Olli Etuaho3d932d82016-04-12 11:10:30 +030022#include "compiler/translator/SymbolTable.h"
23
Jamie Madill45bcc782016-11-07 13:58:48 -050024namespace sh
25{
26
Olli Etuaho3d932d82016-04-12 11:10:30 +030027namespace
28{
29
Olli Etuaho1dd07a62017-05-12 17:01:22 +030030void GetDeferredInitializers(TIntermDeclaration *declaration,
Olli Etuaho0ffc4412017-05-19 14:18:55 +030031 bool initializeUninitializedGlobals,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030032 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +020033 bool highPrecisionSupported,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +030034 TIntermSequence *deferredInitializersOut,
35 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +030036{
Olli Etuahob5601eb2017-11-15 18:08:04 +020037 // SeparateDeclarations should have already been run.
38 ASSERT(declaration->getSequence()->size() == 1);
39
40 TIntermNode *declarator = declaration->getSequence()->back();
41 TIntermBinary *init = declarator->getAsBinaryNode();
42 if (init)
Olli Etuaho3d932d82016-04-12 11:10:30 +030043 {
Olli Etuahob5601eb2017-11-15 18:08:04 +020044 TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
45 ASSERT(symbolNode);
46 TIntermTyped *expression = init->getRight();
47
48 if ((expression->getQualifier() != EvqConst ||
49 (expression->getAsConstantUnion() == nullptr &&
50 !expression->isConstructorWithOnlyConstantUnionParameters())))
Olli Etuaho3d932d82016-04-12 11:10:30 +030051 {
Olli Etuahob5601eb2017-11-15 18:08:04 +020052 // For variables which are not constant, defer their real initialization until
53 // after we initialize uniforms.
54 // Deferral is done also in any cases where the variable has not been constant
55 // folded, since otherwise there's a chance that HLSL output will generate extra
56 // statements from the initializer expression.
Olli Etuaho3d932d82016-04-12 11:10:30 +030057
Olli Etuahob5601eb2017-11-15 18:08:04 +020058 // Change const global to a regular global if its initialization is deferred.
59 // This can happen if ANGLE has not been able to fold the constant expression used
60 // as an initializer.
61 ASSERT(symbolNode->getQualifier() == EvqConst ||
62 symbolNode->getQualifier() == EvqGlobal);
63 if (symbolNode->getQualifier() == EvqConst)
Olli Etuahod4f4c112016-04-15 15:11:24 +030064 {
Olli Etuahob5601eb2017-11-15 18:08:04 +020065 symbolNode->getTypePointer()->setQualifier(EvqGlobal);
Olli Etuahod4f4c112016-04-15 15:11:24 +030066 }
Olli Etuahob5601eb2017-11-15 18:08:04 +020067
68 TIntermBinary *deferredInit =
69 new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
70 deferredInitializersOut->push_back(deferredInit);
71
72 // Remove the initializer from the global scope and just declare the global instead.
73 declaration->replaceChildNode(init, symbolNode);
Olli Etuaho3d932d82016-04-12 11:10:30 +030074 }
Olli Etuahob5601eb2017-11-15 18:08:04 +020075 }
76 else if (initializeUninitializedGlobals)
77 {
78 TIntermSymbol *symbolNode = declarator->getAsSymbolNode();
79 ASSERT(symbolNode);
80
81 // Ignore ANGLE internal variables.
82 if (symbolNode->getName().isInternal())
83 return;
84
85 if (symbolNode->getQualifier() == EvqGlobal && symbolNode->getSymbol() != "")
Olli Etuaho0ffc4412017-05-19 14:18:55 +030086 {
Olli Etuaho87cc90d2017-12-12 15:28:06 +020087 TIntermSequence *initCode = CreateInitCode(symbolNode, canUseLoopsToInitialize,
88 highPrecisionSupported, symbolTable);
Olli Etuahob5601eb2017-11-15 18:08:04 +020089 deferredInitializersOut->insert(deferredInitializersOut->end(), initCode->begin(),
90 initCode->end());
Olli Etuaho0ffc4412017-05-19 14:18:55 +030091 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030092 }
Olli Etuaho3d932d82016-04-12 11:10:30 +030093}
94
Olli Etuahoa16a84f2017-09-12 13:49:18 +030095void InsertInitCallToMain(TIntermBlock *root,
96 TIntermSequence *deferredInitializers,
97 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +030098{
Olli Etuaho83498432017-05-19 14:57:23 +030099 TIntermBlock *initGlobalsBlock = new TIntermBlock();
100 initGlobalsBlock->getSequence()->swap(*deferredInitializers);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300101
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300102 TSymbolUniqueId initGlobalsFunctionId(symbolTable);
103
104 const char *kInitGlobalsFunctionName = "initGlobals";
105
106 TIntermFunctionPrototype *initGlobalsFunctionPrototype =
107 CreateInternalFunctionPrototypeNode(TType(), kInitGlobalsFunctionName, initGlobalsFunctionId);
108 root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype);
109 TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(
110 TType(), kInitGlobalsFunctionName, initGlobalsBlock, initGlobalsFunctionId);
111 root->appendStatement(initGlobalsFunctionDefinition);
112
113 TIntermAggregate *initGlobalsCall = CreateInternalFunctionCallNode(
114 TType(), kInitGlobalsFunctionName, initGlobalsFunctionId, new TIntermSequence());
115
Martin Radevb50ccd32017-07-06 17:09:58 +0300116 TIntermBlock *mainBody = FindMainBody(root);
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300117 mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall);
Olli Etuaho3d932d82016-04-12 11:10:30 +0300118}
119
120} // namespace
121
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300122void DeferGlobalInitializers(TIntermBlock *root,
123 bool initializeUninitializedGlobals,
Olli Etuaho2c7f34c2017-10-09 17:18:02 +0300124 bool canUseLoopsToInitialize,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200125 bool highPrecisionSupported,
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300126 TSymbolTable *symbolTable)
Olli Etuaho3d932d82016-04-12 11:10:30 +0300127{
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300128 TIntermSequence *deferredInitializers = new TIntermSequence();
Olli Etuaho3d932d82016-04-12 11:10:30 +0300129
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300130 // Loop over all global statements and process the declarations. This is simpler than using a
131 // traverser.
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300132 for (TIntermNode *statement : *root->getSequence())
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300133 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300134 TIntermDeclaration *declaration = statement->getAsDeclarationNode();
135 if (declaration)
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300136 {
Olli Etuaho0ffc4412017-05-19 14:18:55 +0300137 GetDeferredInitializers(declaration, initializeUninitializedGlobals,
Olli Etuaho87cc90d2017-12-12 15:28:06 +0200138 canUseLoopsToInitialize, highPrecisionSupported,
139 deferredInitializers, symbolTable);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300140 }
141 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300142
143 // Add the function with initialization and the call to that.
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300144 if (!deferredInitializers->empty())
145 {
Olli Etuahoa16a84f2017-09-12 13:49:18 +0300146 InsertInitCallToMain(root, deferredInitializers, symbolTable);
Olli Etuaho1dd07a62017-05-12 17:01:22 +0300147 }
Olli Etuaho3d932d82016-04-12 11:10:30 +0300148}
Jamie Madill45bcc782016-11-07 13:58:48 -0500149
150} // namespace sh