blob: 6b015e2f4481cc7747339d930b020fc101538e7b [file] [log] [blame]
//
// 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