Commit d81ed841 by Olli Etuaho

Defer executing if statements in the global scope

Unfolding of short-circuiting operators (ternary and logical operators) may create if statements in the global scope, which is not valid HLSL. Use existing deferred global initialization function to defer execution of if statements in the global scope. TEST=WebGL conformance tests BUG=angleproject:819 Change-Id: I2b0afcc6824dab6bb87eb6abed609e75b1384dab Reviewed-on: https://chromium-review.googlesource.com/270461Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent a6f22096
......@@ -1502,8 +1502,12 @@ bool OutputHLSL::visitBinary(Visit visit, TIntermBinary *node)
if (symbolNode->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst)
{
// For variables which are not constant, defer their real initialization until
// after we initialize other globals: uniforms, attributes and varyings.
mDeferredGlobalInitializers.push_back(std::make_pair(symbolNode, expression));
// 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));
}
......@@ -2272,24 +2276,10 @@ bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
return true;
}
bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
void OutputHLSL::writeSelection(TIntermSelection *node)
{
TInfoSinkBase &out = getInfoSink();
ASSERT(!node->usesTernaryOperator());
// D3D errors when there is a gradient operation in a loop in an unflattened if.
// We check for null mCurrentFunctionMetadata to prevent crashing in the case that the translator has generated if
// statements in the global scope when unfolding global initializers. This is a bug that should be addressed by
// moving the unfolded global initializers into a function.
if (mShaderType == GL_FRAGMENT_SHADER
&& mCurrentFunctionMetadata != nullptr
&& mCurrentFunctionMetadata->hasDiscontinuousLoop(node)
&& mCurrentFunctionMetadata->hasGradientInCallGraph(node))
{
out << "FLATTEN ";
}
out << "if (";
node->getCondition()->traverse(this);
......@@ -2334,6 +2324,30 @@ bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
{
mUsesDiscardRewriting = true;
}
}
bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
{
TInfoSinkBase &out = getInfoSink();
ASSERT(!node->usesTernaryOperator());
if (!mInsideFunction)
{
// This is part of unfolded global initialization.
mDeferredGlobalInitializers.push_back(node);
return false;
}
// D3D errors when there is a gradient operation in a loop in an unflattened if.
if (mShaderType == GL_FRAGMENT_SHADER &&
mCurrentFunctionMetadata->hasDiscontinuousLoop(node) &&
mCurrentFunctionMetadata->hasGradientInCallGraph(node))
{
out << "FLATTEN ";
}
writeSelection(node);
return false;
}
......@@ -2954,20 +2968,33 @@ void OutputHLSL::writeDeferredGlobalInitializers(TInfoSinkBase &out)
for (const auto &deferredGlobal : mDeferredGlobalInitializers)
{
TIntermSymbol *symbol = deferredGlobal.first;
TIntermTyped *expression = deferredGlobal.second;
ASSERT(symbol);
ASSERT(symbol->getQualifier() == EvqGlobal && expression->getQualifier() != EvqConst);
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()) << " = ";
out << " " << Decorate(symbol->getSymbol()) << " = ";
if (!writeSameSymbolInitializer(out, symbol, expression))
if (!writeSameSymbolInitializer(out, symbol, expression))
{
ASSERT(mInfoSinkStack.top() == &out);
expression->traverse(this);
}
out << ";\n";
}
else if (selection != nullptr)
{
ASSERT(mInfoSinkStack.top() == &out);
expression->traverse(this);
writeSelection(selection);
}
else
{
UNREACHABLE();
}
out << ";\n";
}
out << "}\n"
......
......@@ -85,6 +85,7 @@ class OutputHLSL : public TIntermTraverser
// Returns true if it found a 'same symbol' initializer (initializer that references the variable it's initting)
bool writeSameSymbolInitializer(TInfoSinkBase &out, TIntermSymbol *symbolNode, TIntermTyped *expression);
void writeDeferredGlobalInitializers(TInfoSinkBase &out);
void writeSelection(TIntermSelection *node);
// Returns the function name
TString addStructEqualityFunction(const TStructure &structure);
......@@ -185,11 +186,10 @@ class OutputHLSL : public TIntermTraverser
std::map<TIntermTyped*, TString> mFlaggedStructMappedNames;
std::map<TIntermTyped*, TString> mFlaggedStructOriginalNames;
// Some initializers use varyings, uniforms or attributes, thus we can't evaluate some variables
// at global static scope in HLSL. These variables depend on values which we retrieve from the
// shader input structure, which we set in the D3D main function. Instead, we can initialize
// these static globals after we initialize our other globals.
std::vector<std::pair<TIntermSymbol*, TIntermTyped*>> mDeferredGlobalInitializers;
// Some initializers may have been unfolded into if statements, thus we can't evaluate all initializers
// at global static scope in HLSL. Instead, we can initialize these static globals inside a helper function.
// This also enables initialization of globals with uniforms.
TIntermSequence mDeferredGlobalInitializers;
struct HelperFunction
{
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment