// // Copyright 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 separate // function that is called 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. Note that it's important that the function definition // is at the end of the shader, as some globals may be declared after main(). // // It can also initialize all uninitialized globals. // #include "compiler/translator/tree_ops/DeferGlobalInitializers.h" #include #include "compiler/translator/Compiler.h" #include "compiler/translator/IntermNode.h" #include "compiler/translator/StaticType.h" #include "compiler/translator/SymbolTable.h" #include "compiler/translator/tree_ops/InitializeVariables.h" #include "compiler/translator/tree_util/FindMain.h" #include "compiler/translator/tree_util/IntermNode_util.h" #include "compiler/translator/tree_util/ReplaceVariable.h" namespace sh { namespace { constexpr const ImmutableString kInitGlobalsString("initGlobals"); void GetDeferredInitializers(TIntermDeclaration *declaration, bool initializeUninitializedGlobals, bool canUseLoopsToInitialize, bool highPrecisionSupported, bool forceDeferGlobalInitializers, TIntermSequence *deferredInitializersOut, std::vector *variablesToReplaceOut, TSymbolTable *symbolTable) { // SeparateDeclarations should have already been run. ASSERT(declaration->getSequence()->size() == 1); TIntermNode *declarator = declaration->getSequence()->back(); TIntermBinary *init = declarator->getAsBinaryNode(); if (init) { TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode(); ASSERT(symbolNode); TIntermTyped *expression = init->getRight(); if (expression->getQualifier() != EvqConst || !expression->hasConstantValue() || forceDeferGlobalInitializers) { // 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 can not be converted to a // constant union, since otherwise there's a chance that HLSL output will generate extra // statements from the initializer expression. // 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) { variablesToReplaceOut->push_back(&symbolNode->variable()); } TIntermBinary *deferredInit = new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight()); deferredInitializersOut->push_back(deferredInit); // Remove the initializer from the global scope and just declare the global instead. declaration->replaceChildNode(init, symbolNode); } } else if (initializeUninitializedGlobals) { TIntermSymbol *symbolNode = declarator->getAsSymbolNode(); ASSERT(symbolNode); // Ignore ANGLE internal variables and nameless declarations. if (symbolNode->variable().symbolType() == SymbolType::AngleInternal || symbolNode->variable().symbolType() == SymbolType::Empty) return; if (symbolNode->getQualifier() == EvqGlobal) { TIntermSequence initCode; CreateInitCode(symbolNode, canUseLoopsToInitialize, highPrecisionSupported, &initCode, symbolTable); deferredInitializersOut->insert(deferredInitializersOut->end(), initCode.begin(), initCode.end()); } } } void InsertInitCallToMain(TIntermBlock *root, TIntermSequence *deferredInitializers, TSymbolTable *symbolTable) { TIntermBlock *initGlobalsBlock = new TIntermBlock(); initGlobalsBlock->getSequence()->swap(*deferredInitializers); TFunction *initGlobalsFunction = new TFunction(symbolTable, kInitGlobalsString, SymbolType::AngleInternal, StaticType::GetBasic(), false); TIntermFunctionPrototype *initGlobalsFunctionPrototype = CreateInternalFunctionPrototypeNode(*initGlobalsFunction); root->getSequence()->insert(root->getSequence()->begin(), initGlobalsFunctionPrototype); TIntermFunctionDefinition *initGlobalsFunctionDefinition = CreateInternalFunctionDefinitionNode(*initGlobalsFunction, initGlobalsBlock); root->appendStatement(initGlobalsFunctionDefinition); TIntermSequence emptySequence; TIntermAggregate *initGlobalsCall = TIntermAggregate::CreateFunctionCall(*initGlobalsFunction, &emptySequence); TIntermBlock *mainBody = FindMainBody(root); mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsCall); } } // namespace bool DeferGlobalInitializers(TCompiler *compiler, TIntermBlock *root, bool initializeUninitializedGlobals, bool canUseLoopsToInitialize, bool highPrecisionSupported, bool forceDeferGlobalInitializers, TSymbolTable *symbolTable) { TIntermSequence deferredInitializers; std::vector variablesToReplace; // Loop over all global statements and process the declarations. This is simpler than using a // traverser. for (TIntermNode *statement : *root->getSequence()) { TIntermDeclaration *declaration = statement->getAsDeclarationNode(); if (declaration) { GetDeferredInitializers(declaration, initializeUninitializedGlobals, canUseLoopsToInitialize, highPrecisionSupported, forceDeferGlobalInitializers, &deferredInitializers, &variablesToReplace, symbolTable); } } // Add the function with initialization and the call to that. if (!deferredInitializers.empty()) { InsertInitCallToMain(root, &deferredInitializers, symbolTable); } // Replace constant variables with non-constant global variables. for (const TVariable *var : variablesToReplace) { TType *replacementType = new TType(var->getType()); replacementType->setQualifier(EvqGlobal); TVariable *replacement = new TVariable(symbolTable, var->name(), replacementType, var->symbolType()); if (!ReplaceVariable(compiler, root, var, replacement)) { return false; } } return true; } } // namespace sh