| // |
| // Copyright (c) 2002-2013 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. |
| // |
| |
| #include "compiler/translator/InitializeVariables.h" |
| |
| #include "angle_gl.h" |
| #include "common/debug.h" |
| #include "compiler/translator/FindMain.h" |
| #include "compiler/translator/IntermNode_util.h" |
| #include "compiler/translator/IntermTraverse.h" |
| #include "compiler/translator/SymbolTable.h" |
| #include "compiler/translator/util.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| bool IsNamelessStruct(const TIntermTyped *node) |
| { |
| return (node->getBasicType() == EbtStruct && node->getType().getStruct()->name() == ""); |
| } |
| |
| void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, |
| TIntermSequence *initSequenceOut); |
| |
| TIntermBinary *CreateZeroInitAssignment(const TIntermTyped *initializedNode) |
| { |
| TIntermTyped *zero = CreateZeroNode(initializedNode->getType()); |
| return new TIntermBinary(EOpAssign, initializedNode->deepCopy(), zero); |
| } |
| |
| void AddStructZeroInitSequence(const TIntermTyped *initializedNode, |
| TIntermSequence *initSequenceOut) |
| { |
| ASSERT(initializedNode->getBasicType() == EbtStruct); |
| TStructure *structType = initializedNode->getType().getStruct(); |
| for (int i = 0; i < static_cast<int>(structType->fields().size()); ++i) |
| { |
| TIntermBinary *element = new TIntermBinary(EOpIndexDirectStruct, |
| initializedNode->deepCopy(), CreateIndexNode(i)); |
| if (element->isArray()) |
| { |
| AddArrayZeroInitSequence(element, initSequenceOut); |
| } |
| else if (element->getType().isStructureContainingArrays()) |
| { |
| AddStructZeroInitSequence(element, initSequenceOut); |
| } |
| else |
| { |
| // Structs can't be defined inside structs, so the type of a struct field can't be a |
| // nameless struct. |
| ASSERT(!IsNamelessStruct(element)); |
| initSequenceOut->push_back(CreateZeroInitAssignment(element)); |
| } |
| } |
| } |
| |
| void AddArrayZeroInitSequence(const TIntermTyped *initializedNode, TIntermSequence *initSequenceOut) |
| { |
| ASSERT(initializedNode->isArray()); |
| // Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which |
| // doesn't have array assignment. |
| // Note that it is important to have the array init in the right order to workaround |
| // http://crbug.com/709317 |
| for (unsigned int i = 0; i < initializedNode->getArraySize(); ++i) |
| { |
| TIntermBinary *element = |
| new TIntermBinary(EOpIndexDirect, initializedNode->deepCopy(), CreateIndexNode(i)); |
| if (element->getType().isStructureContainingArrays()) |
| { |
| AddStructZeroInitSequence(element, initSequenceOut); |
| } |
| else |
| { |
| initSequenceOut->push_back(CreateZeroInitAssignment(element)); |
| } |
| } |
| } |
| |
| void InsertInitCode(TIntermSequence *mainBody, |
| const InitVariableList &variables, |
| const TSymbolTable &symbolTable, |
| int shaderVersion, |
| const TExtensionBehavior &extensionBehavior) |
| { |
| for (const auto &var : variables) |
| { |
| TString name = TString(var.name.c_str()); |
| size_t pos = name.find_last_of('['); |
| if (pos != TString::npos) |
| { |
| name = name.substr(0, pos); |
| } |
| |
| TIntermTyped *initializedSymbol = nullptr; |
| if (var.isBuiltIn()) |
| { |
| initializedSymbol = ReferenceBuiltInVariable(name, symbolTable, shaderVersion); |
| if (initializedSymbol->getQualifier() == EvqFragData && |
| !IsExtensionEnabled(extensionBehavior, "GL_EXT_draw_buffers")) |
| { |
| // If GL_EXT_draw_buffers is disabled, only the 0th index of gl_FragData can be |
| // written to. |
| // TODO(oetuaho): This is a bit hacky and would be better to remove, if we came up |
| // with a good way to do it. Right now "gl_FragData" in symbol table is initialized |
| // to have the array size of MaxDrawBuffers, and the initialization happens before |
| // the shader sets the extensions it is using. |
| initializedSymbol = |
| new TIntermBinary(EOpIndexDirect, initializedSymbol, CreateIndexNode(0)); |
| } |
| } |
| else |
| { |
| initializedSymbol = ReferenceGlobalVariable(name, symbolTable); |
| } |
| ASSERT(initializedSymbol != nullptr); |
| |
| TIntermSequence *initCode = CreateInitCode(initializedSymbol); |
| mainBody->insert(mainBody->begin(), initCode->begin(), initCode->end()); |
| } |
| } |
| |
| class InitializeLocalsTraverser : public TIntermTraverser |
| { |
| public: |
| InitializeLocalsTraverser(int shaderVersion) |
| : TIntermTraverser(true, false, false), mShaderVersion(shaderVersion) |
| { |
| } |
| |
| protected: |
| bool visitDeclaration(Visit visit, TIntermDeclaration *node) override |
| { |
| for (TIntermNode *declarator : *node->getSequence()) |
| { |
| if (!mInGlobalScope && !declarator->getAsBinaryNode()) |
| { |
| TIntermSymbol *symbol = declarator->getAsSymbolNode(); |
| ASSERT(symbol); |
| if (symbol->getSymbol() == "") |
| { |
| continue; |
| } |
| |
| // Arrays may need to be initialized one element at a time, since ESSL 1.00 does not |
| // support array constructors or assigning arrays. |
| bool arrayConstructorUnavailable = |
| (symbol->isArray() || symbol->getType().isStructureContainingArrays()) && |
| mShaderVersion == 100; |
| // Nameless struct constructors can't be referred to, so they also need to be |
| // initialized one element at a time. |
| if (arrayConstructorUnavailable || IsNamelessStruct(symbol)) |
| { |
| // SimplifyLoopConditions should have been run so the parent node of this node |
| // should not be a loop. |
| ASSERT(getParentNode()->getAsLoopNode() == nullptr); |
| // SeparateDeclarations should have already been run, so we don't need to worry |
| // about further declarators in this declaration depending on the effects of |
| // this declarator. |
| ASSERT(node->getSequence()->size() == 1); |
| insertStatementsInParentBlock(TIntermSequence(), *CreateInitCode(symbol)); |
| } |
| else |
| { |
| TIntermBinary *init = |
| new TIntermBinary(EOpInitialize, symbol, CreateZeroNode(symbol->getType())); |
| queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD); |
| } |
| } |
| } |
| return false; |
| } |
| |
| private: |
| int mShaderVersion; |
| }; |
| |
| } // namespace anonymous |
| |
| TIntermSequence *CreateInitCode(const TIntermTyped *initializedSymbol) |
| { |
| TIntermSequence *initCode = new TIntermSequence(); |
| if (initializedSymbol->isArray()) |
| { |
| AddArrayZeroInitSequence(initializedSymbol, initCode); |
| } |
| else if (initializedSymbol->getType().isStructureContainingArrays() || |
| IsNamelessStruct(initializedSymbol)) |
| { |
| AddStructZeroInitSequence(initializedSymbol, initCode); |
| } |
| else |
| { |
| initCode->push_back(CreateZeroInitAssignment(initializedSymbol)); |
| } |
| return initCode; |
| } |
| |
| void InitializeUninitializedLocals(TIntermBlock *root, int shaderVersion) |
| { |
| InitializeLocalsTraverser traverser(shaderVersion); |
| root->traverse(&traverser); |
| traverser.updateTree(); |
| } |
| |
| void InitializeVariables(TIntermBlock *root, |
| const InitVariableList &vars, |
| const TSymbolTable &symbolTable, |
| int shaderVersion, |
| const TExtensionBehavior &extensionBehavior) |
| { |
| TIntermBlock *body = FindMainBody(root); |
| InsertInitCode(body->getSequence(), vars, symbolTable, shaderVersion, extensionBehavior); |
| } |
| |
| } // namespace sh |