Commit 9733ceef by Olli Etuaho Committed by Commit Bot

Initialize uninitialized locals in GLSL output

Guarantee that local variables are initialized before they are used in GLSL output. In HLSL output all variables were already being initialized. Locals are initialized using an AST transform. The local variable init can only be run after some simplification of the AST, so that it is able to handle complex cases like: for (int i[2], j = i[0]; i[0] < 3; ++i[0]) { } If we're dealing with ESSL 1.00 which lacks array constructors, in this kind of case the uninitialized array initialization code needs to be hoisted out of the loop init statement, and the code also needs to make sure that j's initializer is run after i is initialized. Another complex case involves nameless structs. This can be an issue also in ESSL 3.00 and above: for (struct { float f; } s; s.f < 1.0; ++s.f) { } Since the struct doesn't have a name, its constructor can not be used. We solve this by initializing the struct members individually, similarly to how arrays are initialized in ESSL 1.00. Initializing local variables is disabled on Mac and Android for now. On Mac, invalid behavior was exposed in the WebGL 2.0 tests when enabling it. On Android, the dEQP test runs failed for an unknown reason. Bugs have been opened to resolve these issues later. BUG=angleproject:1966 TEST=angle_end2end_tests, WebGL conformance tests Change-Id: Ic06927f5b6cc9619bc82c647ee966605cd80bab2 Reviewed-on: https://chromium-review.googlesource.com/504728 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent bdc1e2a2
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 174 #define ANGLE_SH_VERSION 175
enum ShShaderSpec enum ShShaderSpec
{ {
...@@ -218,6 +218,10 @@ const ShCompileOptions SH_EMULATE_ATAN2_FLOAT_FUNCTION = UINT64_C(1) << 30; ...@@ -218,6 +218,10 @@ const ShCompileOptions SH_EMULATE_ATAN2_FLOAT_FUNCTION = UINT64_C(1) << 30;
// "uniform highp uint webgl_angle_ViewID_OVR". // "uniform highp uint webgl_angle_ViewID_OVR".
const ShCompileOptions SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM = UINT64_C(1) << 31; const ShCompileOptions SH_TRANSLATE_VIEWID_OVR_TO_UNIFORM = UINT64_C(1) << 31;
// Set to initialize uninitialized local variables. Should only be used with GLSL output. In HLSL
// output variables are initialized regardless of if this flag is set.
const ShCompileOptions SH_INITIALIZE_UNINITIALIZED_LOCALS = UINT64_C(1) << 32;
// Defines alternate strategies for implementing array index clamping. // Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy enum ShArrayIndexClampingStrategy
{ {
......
...@@ -144,24 +144,29 @@ int main(int argc, char *argv[]) ...@@ -144,24 +144,29 @@ int main(int argc, char *argv[])
{ {
switch (argv[0][3]) switch (argv[0][3])
{ {
case 'e': output = SH_ESSL_OUTPUT; break; case 'e':
case 'g': output = SH_ESSL_OUTPUT;
if (!ParseGLSLOutputVersion(&argv[0][sizeof("-b=g") - 1], &output)) compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
{ break;
failCode = EFailUsage; case 'g':
} if (!ParseGLSLOutputVersion(&argv[0][sizeof("-b=g") - 1], &output))
break; {
case 'h': failCode = EFailUsage;
if (argv[0][4] == '1' && argv[0][5] == '1') }
{ compileOptions |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
output = SH_HLSL_4_1_OUTPUT; break;
} case 'h':
else if (argv[0][4] == '1' && argv[0][5] == '1')
{ {
output = SH_HLSL_3_0_OUTPUT; output = SH_HLSL_4_1_OUTPUT;
} }
break; else
default: failCode = EFailUsage; {
output = SH_HLSL_3_0_OUTPUT;
}
break;
default:
failCode = EFailUsage;
} }
} }
else else
......
...@@ -70,6 +70,8 @@ ...@@ -70,6 +70,8 @@
'compiler/translator/InitializeVariables.h', 'compiler/translator/InitializeVariables.h',
'compiler/translator/IntermNode.h', 'compiler/translator/IntermNode.h',
'compiler/translator/IntermNode.cpp', 'compiler/translator/IntermNode.cpp',
'compiler/translator/IntermNodePatternMatcher.cpp',
'compiler/translator/IntermNodePatternMatcher.h',
'compiler/translator/IntermTraverse.cpp', 'compiler/translator/IntermTraverse.cpp',
'compiler/translator/Intermediate.h', 'compiler/translator/Intermediate.h',
'compiler/translator/Intermediate.cpp', 'compiler/translator/Intermediate.cpp',
...@@ -107,9 +109,13 @@ ...@@ -107,9 +109,13 @@
'compiler/translator/ScalarizeVecAndMatConstructorArgs.h', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.h',
'compiler/translator/SearchSymbol.cpp', 'compiler/translator/SearchSymbol.cpp',
'compiler/translator/SearchSymbol.h', 'compiler/translator/SearchSymbol.h',
'compiler/translator/SeparateDeclarations.cpp',
'compiler/translator/SeparateDeclarations.h',
'compiler/translator/Severity.h', 'compiler/translator/Severity.h',
'compiler/translator/ShaderLang.cpp', 'compiler/translator/ShaderLang.cpp',
'compiler/translator/ShaderVars.cpp', 'compiler/translator/ShaderVars.cpp',
'compiler/translator/SimplifyLoopConditions.cpp',
'compiler/translator/SimplifyLoopConditions.h',
'compiler/translator/SymbolTable.cpp', 'compiler/translator/SymbolTable.cpp',
'compiler/translator/SymbolTable.h', 'compiler/translator/SymbolTable.h',
'compiler/translator/Types.cpp', 'compiler/translator/Types.cpp',
...@@ -183,8 +189,6 @@ ...@@ -183,8 +189,6 @@
'compiler/translator/blocklayoutHLSL.h', 'compiler/translator/blocklayoutHLSL.h',
'compiler/translator/BuiltInFunctionEmulatorHLSL.cpp', 'compiler/translator/BuiltInFunctionEmulatorHLSL.cpp',
'compiler/translator/BuiltInFunctionEmulatorHLSL.h', 'compiler/translator/BuiltInFunctionEmulatorHLSL.h',
'compiler/translator/IntermNodePatternMatcher.cpp',
'compiler/translator/IntermNodePatternMatcher.h',
'compiler/translator/OutputHLSL.cpp', 'compiler/translator/OutputHLSL.cpp',
'compiler/translator/OutputHLSL.h', 'compiler/translator/OutputHLSL.h',
'compiler/translator/RemoveDynamicIndexing.cpp', 'compiler/translator/RemoveDynamicIndexing.cpp',
...@@ -195,12 +199,8 @@ ...@@ -195,12 +199,8 @@
'compiler/translator/RewriteElseBlocks.h', 'compiler/translator/RewriteElseBlocks.h',
'compiler/translator/SeparateArrayInitialization.cpp', 'compiler/translator/SeparateArrayInitialization.cpp',
'compiler/translator/SeparateArrayInitialization.h', 'compiler/translator/SeparateArrayInitialization.h',
'compiler/translator/SeparateDeclarations.cpp',
'compiler/translator/SeparateDeclarations.h',
'compiler/translator/SeparateExpressionsReturningArrays.cpp', 'compiler/translator/SeparateExpressionsReturningArrays.cpp',
'compiler/translator/SeparateExpressionsReturningArrays.h', 'compiler/translator/SeparateExpressionsReturningArrays.h',
'compiler/translator/SimplifyLoopConditions.cpp',
'compiler/translator/SimplifyLoopConditions.h',
'compiler/translator/SplitSequenceOperator.cpp', 'compiler/translator/SplitSequenceOperator.cpp',
'compiler/translator/SplitSequenceOperator.h', 'compiler/translator/SplitSequenceOperator.h',
'compiler/translator/StructureHLSL.cpp', 'compiler/translator/StructureHLSL.cpp',
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include "compiler/translator/EmulatePrecision.h" #include "compiler/translator/EmulatePrecision.h"
#include "compiler/translator/Initialize.h" #include "compiler/translator/Initialize.h"
#include "compiler/translator/InitializeVariables.h" #include "compiler/translator/InitializeVariables.h"
#include "compiler/translator/IntermNodePatternMatcher.h"
#include "compiler/translator/ParseContext.h" #include "compiler/translator/ParseContext.h"
#include "compiler/translator/PruneEmptyDeclarations.h" #include "compiler/translator/PruneEmptyDeclarations.h"
#include "compiler/translator/RegenerateStructNames.h" #include "compiler/translator/RegenerateStructNames.h"
...@@ -25,6 +26,8 @@ ...@@ -25,6 +26,8 @@
#include "compiler/translator/RemovePow.h" #include "compiler/translator/RemovePow.h"
#include "compiler/translator/RewriteDoWhile.h" #include "compiler/translator/RewriteDoWhile.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
#include "compiler/translator/SeparateDeclarations.h"
#include "compiler/translator/SimplifyLoopConditions.h"
#include "compiler/translator/UnfoldShortCircuitAST.h" #include "compiler/translator/UnfoldShortCircuitAST.h"
#include "compiler/translator/UseInterfaceBlockFields.h" #include "compiler/translator/UseInterfaceBlockFields.h"
#include "compiler/translator/ValidateLimitations.h" #include "compiler/translator/ValidateLimitations.h"
...@@ -474,6 +477,29 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -474,6 +477,29 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
{ {
DeferGlobalInitializers(root); DeferGlobalInitializers(root);
} }
if (success && (compileOptions & SH_INITIALIZE_UNINITIALIZED_LOCALS) && getOutputType())
{
// Initialize uninitialized local variables.
// In some cases initializing can generate extra statements in the parent block, such as
// when initializing nameless structs or initializing arrays in ESSL 1.00. In that case
// we need to first simplify loop conditions and separate declarations. If we don't
// follow the Appendix A limitations, loop init statements can declare arrays or
// nameless structs and have multiple declarations.
if (!shouldRunLoopAndIndexingValidation(compileOptions))
{
SimplifyLoopConditions(root,
IntermNodePatternMatcher::kMultiDeclaration |
IntermNodePatternMatcher::kArrayDeclaration |
IntermNodePatternMatcher::kNamelessStructDeclaration,
getTemporaryIndex(), getSymbolTable(), getShaderVersion());
}
// We only really need to separate array declarations and nameless struct declarations,
// but it's simpler to just use the regular SeparateDeclarations.
SeparateDeclarations(root);
InitializeUninitializedLocals(root, getShaderVersion());
}
} }
if (success) if (success)
......
...@@ -19,7 +19,70 @@ namespace sh ...@@ -19,7 +19,70 @@ namespace sh
namespace namespace
{ {
void InsertInitCode(TIntermSequence *sequence, 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 = TIntermTyped::CreateZero(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(), TIntermTyped::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(),
TIntermTyped::CreateIndexNode(i));
if (element->getType().isStructureContainingArrays())
{
AddStructZeroInitSequence(element, initSequenceOut);
}
else
{
initSequenceOut->push_back(CreateZeroInitAssignment(element));
}
}
}
void InsertInitCode(TIntermSequence *mainBody,
const InitVariableList &variables, const InitVariableList &variables,
const TSymbolTable &symbolTable) const TSymbolTable &symbolTable)
{ {
...@@ -27,62 +90,118 @@ void InsertInitCode(TIntermSequence *sequence, ...@@ -27,62 +90,118 @@ void InsertInitCode(TIntermSequence *sequence,
{ {
TString name = TString(var.name.c_str()); TString name = TString(var.name.c_str());
TIntermSymbol *initializedSymbol = nullptr;
if (var.isArray()) if (var.isArray())
{ {
// Assign the array elements one by one to keep the AST compatible with ESSL 1.00 which
// doesn't have array assignment.
size_t pos = name.find_last_of('['); size_t pos = name.find_last_of('[');
if (pos != TString::npos) if (pos != TString::npos)
{ {
name = name.substr(0, pos); name = name.substr(0, pos);
} }
TType elementType = sh::GetShaderVariableBasicType(var); TType arrayType = sh::GetShaderVariableBasicType(var);
TType arrayType = elementType;
arrayType.setArraySize(var.elementCount()); arrayType.setArraySize(var.elementCount());
initializedSymbol = new TIntermSymbol(0, name, arrayType);
// Workaround for http://crbug.com/709317
// This loop is reversed to initialize elements in increasing
// order [0 1 2 ...]. Otherwise, they're initialized in
// decreasing order [... 2 1 0], due to
// `sequence->insert(sequence->begin(), ...)` below.
for (unsigned int i = var.arraySize; i > 0; --i)
{
unsigned int index = i - 1;
TIntermSymbol *arraySymbol = new TIntermSymbol(0, name, arrayType);
TIntermBinary *element = new TIntermBinary(EOpIndexDirect, arraySymbol,
TIntermTyped::CreateIndexNode(index));
TIntermTyped *zero = TIntermTyped::CreateZero(elementType);
TIntermBinary *assignment = new TIntermBinary(EOpAssign, element, zero);
sequence->insert(sequence->begin(), assignment);
}
} }
else if (var.isStruct()) else if (var.isStruct())
{ {
TVariable *structInfo = reinterpret_cast<TVariable *>(symbolTable.findGlobal(name)); TVariable *structInfo = reinterpret_cast<TVariable *>(symbolTable.findGlobal(name));
ASSERT(structInfo); ASSERT(structInfo);
TIntermSymbol *symbol = new TIntermSymbol(0, name, structInfo->getType()); initializedSymbol = new TIntermSymbol(0, name, structInfo->getType());
TIntermTyped *zero = TIntermTyped::CreateZero(structInfo->getType());
TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
sequence->insert(sequence->begin(), assign);
} }
else else
{ {
TType type = sh::GetShaderVariableBasicType(var); TType type = sh::GetShaderVariableBasicType(var);
TIntermSymbol *symbol = new TIntermSymbol(0, name, type); initializedSymbol = new TIntermSymbol(0, name, type);
TIntermTyped *zero = TIntermTyped::CreateZero(type);
TIntermBinary *assign = new TIntermBinary(EOpAssign, symbol, zero);
sequence->insert(sequence->begin(), assign);
} }
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, TIntermTyped::CreateZero(symbol->getType()));
queueReplacementWithParent(node, symbol, init, OriginalNode::BECOMES_CHILD);
}
}
}
return false;
}
private:
int mShaderVersion;
};
} // namespace anonymous } // namespace anonymous
TIntermSequence *CreateInitCode(const TIntermSymbol *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, void InitializeVariables(TIntermBlock *root,
const InitVariableList &vars, const InitVariableList &vars,
const TSymbolTable &symbolTable) const TSymbolTable &symbolTable)
......
...@@ -9,23 +9,31 @@ ...@@ -9,23 +9,31 @@
#include <GLSLANG/ShaderLang.h> #include <GLSLANG/ShaderLang.h>
#include "compiler/translator/IntermNode.h"
namespace sh namespace sh
{ {
class TIntermBlock;
class TSymbolTable; class TSymbolTable;
typedef std::vector<sh::ShaderVariable> InitVariableList; typedef std::vector<sh::ShaderVariable> InitVariableList;
// Currently this function is only capable of initializing variables of basic types, // Return a sequence of assignment operations to initialize "initializedSymbol". initializedSymbol
// array of basic types, or struct of basic types. // may be an array, struct or any combination of these, as long as it contains only basic types.
TIntermSequence *CreateInitCode(const TIntermSymbol *initializedSymbol);
// Initialize all uninitialized local variables, so that undefined behavior is avoided.
void InitializeUninitializedLocals(TIntermBlock *root, int shaderVersion);
// This function can initialize all the types that CreateInitCode is able to initialize. For struct
// typed variables it requires that the struct is found from the symbolTable, which is usually not
// the case for locally defined struct types.
// For now it is used for the following two scenarios: // For now it is used for the following two scenarios:
// 1. initializing gl_Position; // 1. initializing gl_Position;
// 2. initializing ESSL 3.00 shaders' output variables (which might be structs). // 2. initializing ESSL 3.00 shaders' output variables.
// Specifically, it's not feasible to make it work for local variables because if their
// types are structs, we can't look into TSymbolTable to find the TType data.
void InitializeVariables(TIntermBlock *root, void InitializeVariables(TIntermBlock *root,
const InitVariableList &vars, const InitVariableList &vars,
const TSymbolTable &symbolTable); const TSymbolTable &symbolTable);
} // namespace sh } // namespace sh
#endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_ #endif // COMPILER_TRANSLATOR_INITIALIZEVARIABLES_H_
...@@ -109,7 +109,35 @@ bool IntermNodePatternMatcher::match(TIntermDeclaration *node) ...@@ -109,7 +109,35 @@ bool IntermNodePatternMatcher::match(TIntermDeclaration *node)
{ {
if ((mMask & kMultiDeclaration) != 0) if ((mMask & kMultiDeclaration) != 0)
{ {
return node->getSequence()->size() > 1; if (node->getSequence()->size() > 1)
{
return true;
}
}
if ((mMask & kArrayDeclaration) != 0)
{
if (node->getSequence()->front()->getAsTyped()->getType().isStructureContainingArrays())
{
return true;
}
// Need to check from all declarators whether they are arrays since that may vary between
// declarators.
for (TIntermNode *declarator : *node->getSequence())
{
if (declarator->getAsTyped()->isArray())
{
return true;
}
}
}
if ((mMask & kNamelessStructDeclaration) != 0)
{
TIntermTyped *declarator = node->getSequence()->front()->getAsTyped();
if (declarator->getBasicType() == EbtStruct &&
declarator->getType().getStruct()->name() == "")
{
return true;
}
} }
return false; return false;
} }
......
...@@ -32,13 +32,19 @@ class IntermNodePatternMatcher ...@@ -32,13 +32,19 @@ class IntermNodePatternMatcher
// Matches expressions that return arrays with the exception of simple statements where a // Matches expressions that return arrays with the exception of simple statements where a
// constructor or function call result is assigned. // constructor or function call result is assigned.
kExpressionReturningArray = 0x0002, kExpressionReturningArray = 0x0001 << 1,
// Matches dynamic indexing of vectors or matrices in l-values. // Matches dynamic indexing of vectors or matrices in l-values.
kDynamicIndexingOfVectorOrMatrixInLValue = 0x0004, kDynamicIndexingOfVectorOrMatrixInLValue = 0x0001 << 2,
// Matches declarations with more than one declared variables // Matches declarations with more than one declared variables.
kMultiDeclaration = 0x0008, kMultiDeclaration = 0x0001 << 3,
// Matches declarations of arrays.
kArrayDeclaration = 0x0001 << 4,
// Matches declarations of structs where the struct type does not have a name.
kNamelessStructDeclaration = 0x0001 << 5
}; };
IntermNodePatternMatcher(const unsigned int mask); IntermNodePatternMatcher(const unsigned int mask);
......
...@@ -95,6 +95,11 @@ ShCompileOptions ShaderGL::prepareSourceAndReturnOptions(std::stringstream *sour ...@@ -95,6 +95,11 @@ ShCompileOptions ShaderGL::prepareSourceAndReturnOptions(std::stringstream *sour
options |= SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR; options |= SH_REWRITE_FLOAT_UNARY_MINUS_OPERATOR;
} }
if (!mWorkarounds.dontInitializeUninitializedLocals)
{
options |= SH_INITIALIZE_UNINITIALIZED_LOCALS;
}
return options; return options;
} }
......
...@@ -125,6 +125,10 @@ struct WorkaroundsGL ...@@ -125,6 +125,10 @@ struct WorkaroundsGL
// This only seems to affect AMD OpenGL drivers. // This only seems to affect AMD OpenGL drivers.
// Tracking bug: http://anglebug.com/1936 // Tracking bug: http://anglebug.com/1936
bool emulateMaxVertexAttribStride = false; bool emulateMaxVertexAttribStride = false;
// Initializing uninitialized locals caused odd behavior on Mac in a few WebGL 2 tests.
// Tracking bug: http://anglebug/2041
bool dontInitializeUninitializedLocals = false;
}; };
} // namespace rx } // namespace rx
......
...@@ -962,6 +962,11 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround ...@@ -962,6 +962,11 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
workarounds->doWhileGLSLCausesGPUHang = true; workarounds->doWhileGLSLCausesGPUHang = true;
workarounds->useUnusedBlocksWithStandardOrSharedLayout = true; workarounds->useUnusedBlocksWithStandardOrSharedLayout = true;
workarounds->rewriteFloatUnaryMinusOperator = IsIntel(vendor); workarounds->rewriteFloatUnaryMinusOperator = IsIntel(vendor);
workarounds->dontInitializeUninitializedLocals = true;
#endif
#if defined(ANGLE_PLATFORM_ANDROID)
workarounds->dontInitializeUninitializedLocals = true;
#endif #endif
workarounds->finishDoesNotCauseQueriesToBeAvailable = workarounds->finishDoesNotCauseQueriesToBeAvailable =
......
...@@ -2823,6 +2823,142 @@ TEST_P(GLSLTest_ES3, ConstantStatementAsLoopInit) ...@@ -2823,6 +2823,142 @@ TEST_P(GLSLTest_ES3, ConstantStatementAsLoopInit)
glDeleteShader(shader); glDeleteShader(shader);
} }
// Test that uninitialized local variables are initialized to 0.
TEST_P(GLSLTest_ES3, InitUninitializedLocals)
{
if (IsAndroid() && IsOpenGLES())
{
// http://anglebug.com/2046
std::cout
<< "Test skipped on Android GLES because local variable initialization is disabled."
<< std::endl;
return;
}
if (IsOSX() && IsOpenGL())
{
// http://anglebug.com/2041
std::cout << "Test skipped on Mac OpenGL because local variable initialization is disabled."
<< std::endl;
return;
}
const std::string &fragmentShader =
"#version 300 es\n"
"precision mediump float;\n"
"out vec4 my_FragColor;\n"
"int result = 0;\n"
"void main()\n"
"{\n"
" int u;\n"
" result += u;\n"
" int k = 0;\n"
" for (int i[2], j = i[0] + 1; k < 2; ++k)\n"
" {\n"
" result += j;\n"
" }\n"
" if (result == 2)\n"
" {\n"
" my_FragColor = vec4(0, 1, 0, 1);\n"
" }\n"
" else\n"
" {\n"
" my_FragColor = vec4(1, 0, 0, 1);\n"
" }\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that uninitialized structs containing arrays of structs are initialized to 0. This
// specifically tests with two different struct variables declared in the same block.
TEST_P(GLSLTest, InitUninitializedStructContainingArrays)
{
if (IsAndroid() && IsOpenGLES())
{
// http://anglebug.com/2046
std::cout
<< "Test skipped on Android GLES because local variable initialization is disabled."
<< std::endl;
return;
}
if (IsOSX() && IsOpenGL())
{
// http://anglebug.com/2041
std::cout << "Test skipped on Mac OpenGL because local variable initialization is disabled."
<< std::endl;
return;
}
const std::string &fragmentShader =
"precision mediump float;\n"
"struct T\n"
"{\n"
" int a[2];\n"
"};\n"
"struct S\n"
"{\n"
" T t[2];\n"
"};\n"
"void main()\n"
"{\n"
" S s;\n"
" S s2;\n"
" if (s.t[1].a[1] == 0 && s2.t[1].a[1] == 0)\n"
" {\n"
" gl_FragColor = vec4(0, 1, 0, 1);\n"
" }\n"
" else\n"
" {\n"
" gl_FragColor = vec4(1, 0, 0, 1);\n"
" }\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test that an uninitialized nameless struct inside a for loop init statement works.
TEST_P(GLSLTest_ES3, UninitializedNamelessStructInForInitStatement)
{
if (IsAndroid() && IsOpenGLES())
{
// http://anglebug.com/2046
std::cout
<< "Test skipped on Android GLES because local variable initialization is disabled."
<< std::endl;
return;
}
if (IsOSX() && IsOpenGL())
{
// http://anglebug.com/2041
std::cout << "Test skipped on Mac OpenGL because local variable initialization is disabled."
<< std::endl;
return;
}
const std::string &fragmentShader =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 my_FragColor;\n"
"void main()\n"
"{\n"
" my_FragColor = vec4(1, 0, 0, 1);\n"
" for (struct { float q; } b; b.q < 2.0; b.q++) {\n"
" my_FragColor = vec4(0, 1, 0, 1);\n"
" }\n"
"}\n";
ANGLE_GL_PROGRAM(program, mSimpleVSSource, fragmentShader);
drawQuad(program.get(), "inputAttribute", 0.5f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against. // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
ANGLE_INSTANTIATE_TEST(GLSLTest, ANGLE_INSTANTIATE_TEST(GLSLTest,
ES2_D3D9(), ES2_D3D9(),
......
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