Hash :
83498433
Author :
Date :
2017-05-19T14:57:23
Put global init code inside main() instead in a function Otherwise we may run into a name conflict issue if the shader code is run through the translator twice, since neither user-defined function names or internal function names are currently being prefixed in GLSL output. This could be fixed by prefixing user-defined variables in GLSL output, but this solution is much simpler. BUG=angleproject:1966 TEST=angle_end2end_tests, WebGL conformance tests Change-Id: I15c8de5a0a5e596fafe9f55e8d370345c790d5cc Reviewed-on: https://chromium-review.googlesource.com/509688 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Corentin Wallez <cwallez@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
//
// Copyright (c) 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 block 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.
//
#include "compiler/translator/DeferGlobalInitializers.h"
#include "compiler/translator/FindMain.h"
#include "compiler/translator/IntermNode.h"
#include "compiler/translator/SymbolTable.h"
namespace sh
{
namespace
{
void GetDeferredInitializers(TIntermDeclaration *declaration,
TIntermSequence *deferredInitializersOut)
{
// We iterate with an index instead of using an iterator since we're replacing the children of
// declaration inside the loop.
for (size_t i = 0; i < declaration->getSequence()->size(); ++i)
{
TIntermNode *declarator = declaration->getSequence()->at(i);
TIntermBinary *init = declarator->getAsBinaryNode();
if (init)
{
TIntermSymbol *symbolNode = init->getLeft()->getAsSymbolNode();
ASSERT(symbolNode);
TIntermTyped *expression = init->getRight();
if ((expression->getQualifier() != EvqConst ||
(expression->getAsConstantUnion() == nullptr &&
!expression->isConstructorWithOnlyConstantUnionParameters())))
{
// 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 has not been constant
// folded, since otherwise there's a chance that HLSL output will generate extra
// statements from the initializer expression.
TIntermBinary *deferredInit =
new TIntermBinary(EOpAssign, symbolNode->deepCopy(), init->getRight());
deferredInitializersOut->push_back(deferredInit);
// 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)
{
// All of the siblings in the same declaration need to have consistent
// qualifiers.
auto *siblings = declaration->getSequence();
for (TIntermNode *siblingNode : *siblings)
{
TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
if (siblingBinary)
{
ASSERT(siblingBinary->getOp() == EOpInitialize);
siblingBinary->getLeft()->getTypePointer()->setQualifier(EvqGlobal);
}
siblingNode->getAsTyped()->getTypePointer()->setQualifier(EvqGlobal);
}
// This node is one of the siblings.
ASSERT(symbolNode->getQualifier() == EvqGlobal);
}
// Remove the initializer from the global scope and just declare the global instead.
declaration->replaceChildNode(init, symbolNode);
}
}
}
}
void InsertInitCodeToMain(TIntermBlock *root, TIntermSequence *deferredInitializers)
{
// Insert init code as a block to the beginning of the main() function.
TIntermBlock *initGlobalsBlock = new TIntermBlock();
initGlobalsBlock->getSequence()->swap(*deferredInitializers);
TIntermFunctionDefinition *main = FindMain(root);
ASSERT(main != nullptr);
TIntermBlock *mainBody = main->getBody();
ASSERT(mainBody != nullptr);
mainBody->getSequence()->insert(mainBody->getSequence()->begin(), initGlobalsBlock);
}
} // namespace
void DeferGlobalInitializers(TIntermBlock *root)
{
TIntermSequence *deferredInitializers = new TIntermSequence();
// Loop over all global statements and process the declarations. This is simpler than using a
// traverser.
for (TIntermNode *declaration : *root->getSequence())
{
TIntermDeclaration *asVariableDeclaration = declaration->getAsDeclarationNode();
if (asVariableDeclaration)
{
GetDeferredInitializers(asVariableDeclaration, deferredInitializers);
}
}
// Add the function with initialization and the call to that.
if (!deferredInitializers->empty())
{
InsertInitCodeToMain(root, deferredInitializers);
}
}
} // namespace sh