Defer global initializers when necessary
Move global variable initializers that are not constant expressions to
a function that gets called at the start of main(). This is done
with an AST transformation. This needs to be done because global
variable initializers must be constant in native GL, but ANGLE is more
lenient with what can be put into ESSL 1.00 global initializers to
remain compatible with legacy WebGL content.
Non-constant global variable initializers also caused issues in HLSL
output, since in HLSL output some types of expressions get unfolded
into multiple statements. These include short-circuiting operators and
array initialization. To make sure that these cases are covered, any
initializers that can't be constant folded are deferred, even if they
have the const qualifier.
The old deferring mechanism in OutputHLSL is removed in favor of this
new AST transformation based approach.
BUG=angleproject:819
BUG=angleproject:1205
BUG=angleproject:1350
BUG=596616
TEST=WebGL conformance test
conformance/glsl/misc/global-variable-init.html
Change-Id: I039cc05d6b8c284baeefbdf7f10062cae4bc5716
Reviewed-on: https://chromium-review.googlesource.com/338291
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 225f154..cd95b02 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -259,10 +259,6 @@
mInfoSinkStack.pop();
mInfoSinkStack.push(&mFooter);
- if (!mDeferredGlobalInitializers.empty())
- {
- writeDeferredGlobalInitializers(mFooter);
- }
mInfoSinkStack.pop();
mInfoSinkStack.push(&mHeader);
@@ -1784,20 +1780,11 @@
ASSERT(symbolNode);
TIntermTyped *expression = node->getRight();
- // TODO (jmadill): do a 'deep' scan to know if an expression is statically const
- if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst)
- {
- // For variables which are not constant, defer their real initialization until
- // after we initialize uniforms.
- TIntermBinary *deferredInit = new TIntermBinary(EOpAssign);
- deferredInit->setLeft(node->getLeft());
- deferredInit->setRight(node->getRight());
- deferredInit->setType(node->getType());
- mDeferredGlobalInitializers.push_back(deferredInit);
- const TString &initString = initializer(node->getType());
- node->setRight(new TIntermRaw(node->getType(), initString));
- }
- else if (writeSameSymbolInitializer(out, symbolNode, expression))
+ // Global initializers must be constant at this point.
+ ASSERT(symbolNode->getQualifier() != EvqGlobal ||
+ (expression->getQualifier() == EvqConst &&
+ expression->getAsConstantUnion() != nullptr));
+ if (writeSameSymbolInitializer(out, symbolNode, expression))
{
// Skip initializing the rest of the expression
return false;
@@ -2994,13 +2981,7 @@
TInfoSinkBase &out = getInfoSink();
ASSERT(!node->usesTernaryOperator());
-
- if (!mInsideFunction)
- {
- // This is part of unfolded global initialization.
- mDeferredGlobalInitializers.push_back(node);
- return false;
- }
+ ASSERT(mInsideFunction);
// D3D errors when there is a gradient operation in a loop in an unflattened if.
if (mShaderType == GL_FRAGMENT_SHADER && mCurrentFunctionMetadata->hasGradientLoop(node))
@@ -3731,47 +3712,6 @@
return false;
}
-void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out)
-{
- out << "#define ANGLE_USES_DEFERRED_INIT\n"
- << "\n"
- << "void initializeDeferredGlobals()\n"
- << "{\n";
-
- for (const auto &deferredGlobal : mDeferredGlobalInitializers)
- {
- TIntermBinary *binary = deferredGlobal->getAsBinaryNode();
- TIntermSelection *selection = deferredGlobal->getAsSelectionNode();
- if (binary != nullptr)
- {
- TIntermSymbol *symbol = binary->getLeft()->getAsSymbolNode();
- TIntermTyped *expression = binary->getRight();
- ASSERT(symbol);
- ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst);
-
- out << " " << Decorate(symbol->getSymbol()) << " = ";
-
- if (!writeSameSymbolInitializer(out, symbol, expression))
- {
- ASSERT(mInfoSinkStack.top() == &out);
- expression->traverse(this);
- }
- out << ";\n";
- }
- else if (selection != nullptr)
- {
- writeSelection(out, selection);
- }
- else
- {
- UNREACHABLE();
- }
- }
-
- out << "}\n"
- << "\n";
-}
-
TString OutputHLSL::addStructEqualityFunction(const TStructure &structure)
{
const TFieldList &fields = structure.fields();