Commit b12040c4 by Olli Etuaho Committed by Commit Bot

Clean up redundant initialization of gl_Position

In case gl_Position is statically used in the input shader, setting the INIT_OUTPUT_VARIABLES flag will initialize gl_Position. Avoid redundant initialization of gl_Position in this case. Includes cleaning up memory management in InitOutputVariables_test: all the pool-allocated variables will be freed at the end of each test when the memory pool is cleared, so manual memory management is not needed. Also includes making the zero node check used in unit tests stricter so that the tests are more reliable and moving it to ShaderCompileTreeTest.h so that it can be reused in the future. BUG=angleproject:2092 TEST=angle_unittests Change-Id: I323a0a094afa6cea95c8a64e681d9fc485137423 Reviewed-on: https://chromium-review.googlesource.com/549418 Commit-Queue: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent 0fb0864e
...@@ -226,6 +226,7 @@ TShHandleBase::~TShHandleBase() ...@@ -226,6 +226,7 @@ TShHandleBase::~TShHandleBase()
TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output) TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
: variablesCollected(false), : variablesCollected(false),
mGLPositionInitialized(false),
shaderType(type), shaderType(type),
shaderSpec(spec), shaderSpec(spec),
outputType(output), outputType(output),
...@@ -431,12 +432,6 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -431,12 +432,6 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews()); DeclareAndInitBuiltinsForInstancedMultiview(root, getNumViews());
} }
// gl_Position is always written in compatibility output mode
if (success && shaderType == GL_VERTEX_SHADER &&
((compileOptions & SH_INIT_GL_POSITION) ||
(outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
initializeGLPosition(root);
// This pass might emit short circuits so keep it before the short circuit unfolding // This pass might emit short circuits so keep it before the short circuit unfolding
if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS)) if (success && (compileOptions & SH_REWRITE_DO_WHILE_LOOPS))
RewriteDoWhile(root, getTemporaryIndex()); RewriteDoWhile(root, getTemporaryIndex());
...@@ -487,6 +482,17 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[], ...@@ -487,6 +482,17 @@ TIntermBlock *TCompiler::compileTreeImpl(const char *const shaderStrings[],
} }
} }
// gl_Position is always written in compatibility output mode.
// It may have been already initialized among other output variables, in that case we don't
// need to initialize it twice.
if (success && shaderType == GL_VERTEX_SHADER && !mGLPositionInitialized &&
((compileOptions & SH_INIT_GL_POSITION) ||
(outputType == SH_GLSL_COMPATIBILITY_OUTPUT)))
{
initializeGLPosition(root);
mGLPositionInitialized = true;
}
// Removing invariant declarations must be done after collecting variables. // Removing invariant declarations must be done after collecting variables.
// Otherwise, built-in invariant declarations don't apply. // Otherwise, built-in invariant declarations don't apply.
if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions)) if (success && RemoveInvariant(shaderType, shaderVersion, outputType, compileOptions))
...@@ -727,6 +733,7 @@ void TCompiler::clearResults() ...@@ -727,6 +733,7 @@ void TCompiler::clearResults()
varyings.clear(); varyings.clear();
interfaceBlocks.clear(); interfaceBlocks.clear();
variablesCollected = false; variablesCollected = false;
mGLPositionInitialized = false;
mNumViews = -1; mNumViews = -1;
...@@ -964,6 +971,11 @@ void TCompiler::initializeOutputVariables(TIntermBlock *root) ...@@ -964,6 +971,11 @@ void TCompiler::initializeOutputVariables(TIntermBlock *root)
for (auto var : varyings) for (auto var : varyings)
{ {
list.push_back(var); list.push_back(var);
if (var.name == "gl_Position")
{
ASSERT(!mGLPositionInitialized);
mGLPositionInitialized = true;
}
} }
} }
else else
......
...@@ -187,6 +187,8 @@ class TCompiler : public TShHandleBase ...@@ -187,6 +187,8 @@ class TCompiler : public TShHandleBase
bool variablesCollected; bool variablesCollected;
bool mGLPositionInitialized;
// Removes unused function declarations and prototypes from the AST // Removes unused function declarations and prototypes from the AST
class UnusedPredicate; class UnusedPredicate;
bool pruneUnusedFunctions(TIntermBlock *root); bool pruneUnusedFunctions(TIntermBlock *root);
......
...@@ -82,65 +82,6 @@ ExpectedLValues CreateIndexedLValueNodeList(const TString &lValueName, ...@@ -82,65 +82,6 @@ ExpectedLValues CreateIndexedLValueNodeList(const TString &lValueName,
return expected; return expected;
} }
void ReleaseExpectedLValuesMemory(ExpectedLValues &expectedLValues)
{
for (size_t i = 0u; i < expectedLValues.size(); ++i)
{
delete expectedLValues[i];
}
expectedLValues.clear();
}
class ConstantsAreZerosTraverser final : public TIntermTraverser
{
public:
ConstantsAreZerosTraverser() : TIntermTraverser(true, false, false), mAllConstantsAreZeros(true)
{
}
void visitConstantUnion(TIntermConstantUnion *node)
{
if (!mAllConstantsAreZeros)
{
return;
}
const TType &type = node->getType();
size_t objectSize = type.getObjectSize();
for (size_t i = 0u; i < objectSize && mAllConstantsAreZeros; ++i)
{
switch (type.getBasicType())
{
case EbtFloat:
mAllConstantsAreZeros = (node->getFConst(i) == 0.0f);
break;
case EbtInt:
mAllConstantsAreZeros = (node->getIConst(i) == 0);
break;
case EbtUInt:
mAllConstantsAreZeros = (node->getUConst(i) == 0u);
break;
default:
// Cannot handle.
mAllConstantsAreZeros = false;
}
}
}
bool areAllConstantsZeros() const { return mAllConstantsAreZeros; }
private:
bool mAllConstantsAreZeros;
};
// Returns true if all visited constants in subtree are zeros.
bool AreAllConstantsInSubtreeZeros(TIntermNode *subtree)
{
ConstantsAreZerosTraverser traverser;
subtree->traverse(&traverser);
return traverser.areAllConstantsZeros();
}
// VerifyOutputVariableInitializers traverses the subtree covering main and collects the lvalues in // VerifyOutputVariableInitializers traverses the subtree covering main and collects the lvalues in
// assignments for which the rvalue is an expression containing only zero constants. // assignments for which the rvalue is an expression containing only zero constants.
class VerifyOutputVariableInitializers final : public TIntermTraverser class VerifyOutputVariableInitializers final : public TIntermTraverser
...@@ -159,7 +100,7 @@ class VerifyOutputVariableInitializers final : public TIntermTraverser ...@@ -159,7 +100,7 @@ class VerifyOutputVariableInitializers final : public TIntermTraverser
bool visitBinary(Visit visit, TIntermBinary *node) override bool visitBinary(Visit visit, TIntermBinary *node) override
{ {
if (node->getOp() == EOpAssign && AreAllConstantsInSubtreeZeros(node->getRight())) if (node->getOp() == EOpAssign && IsZero(node->getRight()))
{ {
mCandidateLValues.push_back(node->getLeft()); mCandidateLValues.push_back(node->getLeft());
return false; return false;
...@@ -234,10 +175,15 @@ class FindStructByName final : public TIntermTraverser ...@@ -234,10 +175,15 @@ class FindStructByName final : public TIntermTraverser
class InitOutputVariablesWebGL2Test : public ShaderCompileTreeTest class InitOutputVariablesWebGL2Test : public ShaderCompileTreeTest
{ {
public: public:
InitOutputVariablesWebGL2Test() void SetUp() override
{ {
mExtraCompileOptions |= SH_VARIABLES; mExtraCompileOptions |= SH_VARIABLES;
mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES; mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;
if (getShaderType() == GL_VERTEX_SHADER)
{
mExtraCompileOptions |= SH_INIT_GL_POSITION;
}
ShaderCompileTreeTest::SetUp();
} }
protected: protected:
...@@ -302,7 +248,6 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputAllQualifiers) ...@@ -302,7 +248,6 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputAllQualifiers)
CreateLValueNode("out3", TType(EbtFloat, EbpMedium, EvqCentroidOut)), CreateLValueNode("out3", TType(EbtFloat, EbpMedium, EvqCentroidOut)),
CreateLValueNode("out4", TType(EbtFloat, EbpMedium, EvqSmoothOut))}; CreateLValueNode("out4", TType(EbtFloat, EbpMedium, EvqSmoothOut))};
EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues)); EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues));
ReleaseExpectedLValuesMemory(expectedLValues);
} }
// Test the initialization of an output array in a vertex shader. // Test the initialization of an output array in a vertex shader.
...@@ -320,7 +265,6 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputArray) ...@@ -320,7 +265,6 @@ TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputArray)
ExpectedLValues expectedLValues = ExpectedLValues expectedLValues =
CreateIndexedLValueNodeList("out1", TType(EbtFloat, EbpMedium, EvqVertexOut), 2); CreateIndexedLValueNodeList("out1", TType(EbtFloat, EbpMedium, EvqVertexOut), 2);
EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues)); EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues));
ReleaseExpectedLValuesMemory(expectedLValues);
} }
// Test the initialization of a struct output variable in a vertex shader. // Test the initialization of a struct output variable in a vertex shader.
...@@ -402,7 +346,6 @@ TEST_F(InitOutputVariablesWebGL2FragmentShaderTest, FragData) ...@@ -402,7 +346,6 @@ TEST_F(InitOutputVariablesWebGL2FragmentShaderTest, FragData)
CreateIndexedLValueNodeList("gl_FragData", TType(EbtFloat, EbpMedium, EvqFragData, 4), 1); CreateIndexedLValueNodeList("gl_FragData", TType(EbtFloat, EbpMedium, EvqFragData, 4), 1);
EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0])); EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));
EXPECT_EQ(1u, verifier.getCandidates().size()); EXPECT_EQ(1u, verifier.getCandidates().size());
ReleaseExpectedLValuesMemory(expectedLValues);
} }
// Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader. Only writes to // Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader. Only writes to
...@@ -421,7 +364,6 @@ TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragData) ...@@ -421,7 +364,6 @@ TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragData)
CreateIndexedLValueNodeList("gl_FragData", TType(EbtFloat, EbpMedium, EvqFragData, 4), 1); CreateIndexedLValueNodeList("gl_FragData", TType(EbtFloat, EbpMedium, EvqFragData, 4), 1);
EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0])); EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));
EXPECT_EQ(1u, verifier.getCandidates().size()); EXPECT_EQ(1u, verifier.getCandidates().size());
ReleaseExpectedLValuesMemory(expectedLValues);
} }
// Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader with GL_EXT_draw_buffers // Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader with GL_EXT_draw_buffers
...@@ -442,7 +384,43 @@ TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragDataWithDrawBuffersExtEn ...@@ -442,7 +384,43 @@ TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragDataWithDrawBuffersExtEn
EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0])); EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));
EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[1])); EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[1]));
EXPECT_EQ(2u, verifier.getCandidates().size()); EXPECT_EQ(2u, verifier.getCandidates().size());
ReleaseExpectedLValuesMemory(expectedLValues);
} }
} // namespace sh // Test that gl_Position is initialized once in case it is not statically used and both
\ No newline at end of file // SH_INIT_OUTPUT_VARIABLES and SH_INIT_GL_POSITION flags are set.
TEST_F(InitOutputVariablesWebGL2VertexShaderTest, InitGLPositionWhenNotStaticallyUsed)
{
const std::string &shaderString =
"#version 300 es\n"
"precision highp float;\n"
"void main() {\n"
"}\n";
compileAssumeSuccess(shaderString);
VerifyOutputVariableInitializers verifier(mASTRoot);
TIntermTyped *glPosition =
CreateLValueNode("gl_Position", TType(EbtFloat, EbpHigh, EvqPosition, 4));
EXPECT_TRUE(verifier.isExpectedLValueFound(glPosition));
EXPECT_EQ(1u, verifier.getCandidates().size());
}
// Test that gl_Position is initialized once in case it is statically used and both
// SH_INIT_OUTPUT_VARIABLES and SH_INIT_GL_POSITION flags are set.
TEST_F(InitOutputVariablesWebGL2VertexShaderTest, InitGLPositionOnceWhenStaticallyUsed)
{
const std::string &shaderString =
"#version 300 es\n"
"precision highp float;\n"
"void main() {\n"
" gl_Position = vec4(1.0);\n"
"}\n";
compileAssumeSuccess(shaderString);
VerifyOutputVariableInitializers verifier(mASTRoot);
TIntermTyped *glPosition =
CreateLValueNode("gl_Position", TType(EbtFloat, EbpHigh, EvqPosition, 4));
EXPECT_TRUE(verifier.isExpectedLValueFound(glPosition));
EXPECT_EQ(1u, verifier.getCandidates().size());
}
} // namespace sh
...@@ -11,7 +11,103 @@ ...@@ -11,7 +11,103 @@
#include "compiler/translator/TranslatorESSL.h" #include "compiler/translator/TranslatorESSL.h"
using namespace sh; namespace sh
{
namespace
{
// Checks that the node traversed is a zero node. It can be made out of multiple constructors and
// constant union nodes as long as there's no arithmetic involved and all constants are zero.
class OnlyContainsZeroConstantsTraverser final : public TIntermTraverser
{
public:
OnlyContainsZeroConstantsTraverser()
: TIntermTraverser(true, false, false), mOnlyContainsConstantZeros(true)
{
}
bool visitUnary(Visit, TIntermUnary *node) override
{
mOnlyContainsConstantZeros = false;
return false;
}
bool visitBinary(Visit, TIntermBinary *node) override
{
mOnlyContainsConstantZeros = false;
return false;
}
bool visitTernary(Visit, TIntermTernary *node) override
{
mOnlyContainsConstantZeros = false;
return false;
}
bool visitSwizzle(Visit, TIntermSwizzle *node) override
{
mOnlyContainsConstantZeros = false;
return false;
}
bool visitAggregate(Visit, TIntermAggregate *node) override
{
if (node->getOp() != EOpConstruct)
{
mOnlyContainsConstantZeros = false;
return false;
}
return true;
}
void visitSymbol(TIntermSymbol *node) override { mOnlyContainsConstantZeros = false; }
void visitConstantUnion(TIntermConstantUnion *node) override
{
if (!mOnlyContainsConstantZeros)
{
return;
}
const TType &type = node->getType();
size_t objectSize = type.getObjectSize();
for (size_t i = 0u; i < objectSize && mOnlyContainsConstantZeros; ++i)
{
bool isZero = false;
switch (type.getBasicType())
{
case EbtFloat:
isZero = (node->getFConst(i) == 0.0f);
break;
case EbtInt:
isZero = (node->getIConst(i) == 0);
break;
case EbtUInt:
isZero = (node->getUConst(i) == 0u);
break;
case EbtBool:
isZero = (node->getBConst(i) == false);
break;
default:
// Cannot handle.
break;
}
if (!isZero)
{
mOnlyContainsConstantZeros = false;
return;
}
}
}
bool onlyContainsConstantZeros() const { return mOnlyContainsConstantZeros; }
private:
bool mOnlyContainsConstantZeros;
};
} // anonymous namespace
void ShaderCompileTreeTest::SetUp() void ShaderCompileTreeTest::SetUp()
{ {
...@@ -61,4 +157,17 @@ const std::vector<sh::Uniform> ShaderCompileTreeTest::getUniforms() ...@@ -61,4 +157,17 @@ const std::vector<sh::Uniform> ShaderCompileTreeTest::getUniforms()
{ {
ASSERT(mExtraCompileOptions & SH_VARIABLES); ASSERT(mExtraCompileOptions & SH_VARIABLES);
return mTranslator->getUniforms(); return mTranslator->getUniforms();
} }
\ No newline at end of file
bool IsZero(TIntermNode *node)
{
if (!node->getAsTyped())
{
return false;
}
OnlyContainsZeroConstantsTraverser traverser;
node->traverse(&traverser);
return traverser.onlyContainsConstantZeros();
}
} // namespace sh
...@@ -19,6 +19,7 @@ namespace sh ...@@ -19,6 +19,7 @@ namespace sh
{ {
class TIntermBlock; class TIntermBlock;
class TIntermNode;
class TranslatorESSL; class TranslatorESSL;
class ShaderCompileTreeTest : public testing::Test class ShaderCompileTreeTest : public testing::Test
...@@ -54,6 +55,10 @@ class ShaderCompileTreeTest : public testing::Test ...@@ -54,6 +55,10 @@ class ShaderCompileTreeTest : public testing::Test
TPoolAllocator mAllocator; TPoolAllocator mAllocator;
}; };
// Returns true if the node is some kind of a zero node - either constructor or a constant union
// node.
bool IsZero(TIntermNode *node);
} // namespace sh } // namespace sh
#endif // TESTS_TEST_UTILS_SHADER_COMPILE_TREE_TEST_H_ #endif // TESTS_TEST_UTILS_SHADER_COMPILE_TREE_TEST_H_
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