Path: blob/main_old/src/tests/compiler_tests/InitOutputVariables_test.cpp
1693 views
//1// Copyright 2017 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// InitOutputVariables_test.cpp: Tests correctness of the AST pass enabled through6// SH_INIT_OUTPUT_VARIABLES.7//89#include "common/angleutils.h"1011#include "compiler/translator/SymbolTable.h"12#include "compiler/translator/tree_util/FindMain.h"13#include "compiler/translator/tree_util/IntermNode_util.h"14#include "compiler/translator/tree_util/IntermTraverse.h"15#include "tests/test_utils/ShaderCompileTreeTest.h"1617#include <algorithm>1819namespace sh20{2122namespace23{2425typedef std::vector<TIntermTyped *> ExpectedLValues;2627bool AreSymbolsTheSame(const TIntermSymbol *expected, const TIntermSymbol *candidate)28{29if (expected == nullptr || candidate == nullptr)30{31return false;32}33const TType &expectedType = expected->getType();34const TType &candidateType = candidate->getType();35const bool sameTypes = expectedType == candidateType &&36expectedType.getPrecision() == candidateType.getPrecision() &&37expectedType.getQualifier() == candidateType.getQualifier();38const bool sameSymbols = (expected->variable().symbolType() == SymbolType::Empty &&39candidate->variable().symbolType() == SymbolType::Empty) ||40expected->getName() == candidate->getName();41return sameSymbols && sameTypes;42}4344bool AreLValuesTheSame(TIntermTyped *expected, TIntermTyped *candidate)45{46const TIntermBinary *expectedBinary = expected->getAsBinaryNode();47if (expectedBinary)48{49ASSERT(expectedBinary->getOp() == EOpIndexDirect);50const TIntermBinary *candidateBinary = candidate->getAsBinaryNode();51if (candidateBinary == nullptr || candidateBinary->getOp() != EOpIndexDirect)52{53return false;54}55if (expectedBinary->getRight()->getAsConstantUnion()->getIConst(0) !=56candidateBinary->getRight()->getAsConstantUnion()->getIConst(0))57{58return false;59}60return AreSymbolsTheSame(expectedBinary->getLeft()->getAsSymbolNode(),61candidateBinary->getLeft()->getAsSymbolNode());62}63return AreSymbolsTheSame(expected->getAsSymbolNode(), candidate->getAsSymbolNode());64}6566TIntermTyped *CreateLValueNode(const ImmutableString &lValueName, const TType &type)67{68// We're using a mock symbol table here, don't need to assign proper symbol ids to these nodes.69TSymbolTable symbolTable;70TVariable *variable =71new TVariable(&symbolTable, lValueName, new TType(type), SymbolType::UserDefined);72return new TIntermSymbol(variable);73}7475ExpectedLValues CreateIndexedLValueNodeList(const ImmutableString &lValueName,76const TType &elementType,77unsigned arraySize)78{79ASSERT(elementType.isArray() == false);80TType *arrayType = new TType(elementType);81arrayType->makeArray(arraySize);8283// We're using a mock symbol table here, don't need to assign proper symbol ids to these nodes.84TSymbolTable symbolTable;85TVariable *variable =86new TVariable(&symbolTable, lValueName, arrayType, SymbolType::UserDefined);87TIntermSymbol *arraySymbol = new TIntermSymbol(variable);8889ExpectedLValues expected(arraySize);90for (unsigned index = 0u; index < arraySize; ++index)91{92expected[index] = new TIntermBinary(EOpIndexDirect, arraySymbol->deepCopy(),93CreateIndexNode(static_cast<int>(index)));94}95return expected;96}9798// VerifyOutputVariableInitializers traverses the subtree covering main and collects the lvalues in99// assignments for which the rvalue is an expression containing only zero constants.100class VerifyOutputVariableInitializers final : public TIntermTraverser101{102public:103VerifyOutputVariableInitializers(TIntermBlock *root) : TIntermTraverser(true, false, false)104{105ASSERT(root != nullptr);106107// The traversal starts in the body of main because this is where the varyings and output108// variables are initialized.109sh::TIntermFunctionDefinition *main = FindMain(root);110ASSERT(main != nullptr);111main->traverse(this);112}113114bool visitBinary(Visit visit, TIntermBinary *node) override115{116if (node->getOp() == EOpAssign && IsZero(node->getRight()))117{118mCandidateLValues.push_back(node->getLeft());119return false;120}121return true;122}123124// The collected lvalues are considered valid if every expected lvalue in expectedLValues is125// matched by name and type with any lvalue in mCandidateLValues.126bool areAllExpectedLValuesFound(const ExpectedLValues &expectedLValues) const127{128for (size_t i = 0u; i < expectedLValues.size(); ++i)129{130if (!isExpectedLValueFound(expectedLValues[i]))131{132return false;133}134}135return true;136}137138bool isExpectedLValueFound(TIntermTyped *expectedLValue) const139{140bool isFound = false;141for (size_t j = 0; j < mCandidateLValues.size() && !isFound; ++j)142{143isFound = AreLValuesTheSame(expectedLValue, mCandidateLValues[j]);144}145return isFound;146}147148const ExpectedLValues &getCandidates() const { return mCandidateLValues; }149150private:151ExpectedLValues mCandidateLValues;152};153154// Traverses the AST and records a pointer to a structure with a given name.155class FindStructByName final : public TIntermTraverser156{157public:158FindStructByName(const ImmutableString &structName)159: TIntermTraverser(true, false, false), mStructName(structName), mStructure(nullptr)160{}161162void visitSymbol(TIntermSymbol *symbol) override163{164if (isStructureFound())165{166return;167}168169const TStructure *structure = symbol->getType().getStruct();170171if (structure != nullptr && structure->symbolType() != SymbolType::Empty &&172structure->name() == mStructName)173{174mStructure = structure;175}176}177178bool isStructureFound() const { return mStructure != nullptr; }179const TStructure *getStructure() const { return mStructure; }180181private:182ImmutableString mStructName;183const TStructure *mStructure;184};185186} // namespace187188class InitOutputVariablesWebGL2Test : public ShaderCompileTreeTest189{190public:191void SetUp() override192{193mExtraCompileOptions |= SH_VARIABLES;194mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;195if (getShaderType() == GL_VERTEX_SHADER)196{197mExtraCompileOptions |= SH_INIT_GL_POSITION;198}199ShaderCompileTreeTest::SetUp();200}201202protected:203ShShaderSpec getShaderSpec() const override { return SH_WEBGL2_SPEC; }204};205206class InitOutputVariablesWebGL2VertexShaderTest : public InitOutputVariablesWebGL2Test207{208protected:209::GLenum getShaderType() const override { return GL_VERTEX_SHADER; }210};211212class InitOutputVariablesWebGL2FragmentShaderTest : public InitOutputVariablesWebGL2Test213{214protected:215::GLenum getShaderType() const override { return GL_FRAGMENT_SHADER; }216void initResources(ShBuiltInResources *resources) override217{218resources->EXT_draw_buffers = 1;219resources->MaxDrawBuffers = 2;220}221};222223class InitOutputVariablesWebGL1FragmentShaderTest : public ShaderCompileTreeTest224{225public:226InitOutputVariablesWebGL1FragmentShaderTest()227{228mExtraCompileOptions |= SH_VARIABLES;229mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;230}231232protected:233::GLenum getShaderType() const override { return GL_FRAGMENT_SHADER; }234ShShaderSpec getShaderSpec() const override { return SH_WEBGL_SPEC; }235void initResources(ShBuiltInResources *resources) override236{237resources->EXT_draw_buffers = 1;238resources->MaxDrawBuffers = 2;239}240};241242class InitOutputVariablesVertexShaderClipDistanceTest : public ShaderCompileTreeTest243{244public:245InitOutputVariablesVertexShaderClipDistanceTest()246{247mExtraCompileOptions |= SH_VARIABLES;248mExtraCompileOptions |= SH_INIT_OUTPUT_VARIABLES;249mExtraCompileOptions |= SH_VALIDATE_AST;250}251252protected:253::GLenum getShaderType() const override { return GL_VERTEX_SHADER; }254ShShaderSpec getShaderSpec() const override { return SH_GLES2_SPEC; }255void initResources(ShBuiltInResources *resources) override256{257resources->APPLE_clip_distance = 1;258resources->MaxClipDistances = 8;259}260};261262// Test the initialization of output variables with various qualifiers in a vertex shader.263TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputAllQualifiers)264{265const std::string &shaderString =266"#version 300 es\n"267"precision mediump float;\n"268"precision lowp int;\n"269"out vec4 out1;\n"270"flat out int out2;\n"271"centroid out float out3;\n"272"smooth out float out4;\n"273"void main() {\n"274"}\n";275compileAssumeSuccess(shaderString);276VerifyOutputVariableInitializers verifier(mASTRoot);277278ExpectedLValues expectedLValues = {279CreateLValueNode(ImmutableString("out1"), TType(EbtFloat, EbpMedium, EvqVertexOut, 4)),280CreateLValueNode(ImmutableString("out2"), TType(EbtInt, EbpLow, EvqFlatOut)),281CreateLValueNode(ImmutableString("out3"), TType(EbtFloat, EbpMedium, EvqCentroidOut)),282CreateLValueNode(ImmutableString("out4"), TType(EbtFloat, EbpMedium, EvqSmoothOut))};283EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues));284}285286// Test the initialization of an output array in a vertex shader.287TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputArray)288{289const std::string &shaderString =290"#version 300 es\n"291"precision mediump float;\n"292"out float out1[2];\n"293"void main() {\n"294"}\n";295compileAssumeSuccess(shaderString);296VerifyOutputVariableInitializers verifier(mASTRoot);297298ExpectedLValues expectedLValues = CreateIndexedLValueNodeList(299ImmutableString("out1"), TType(EbtFloat, EbpMedium, EvqVertexOut), 2);300EXPECT_TRUE(verifier.areAllExpectedLValuesFound(expectedLValues));301}302303// Test the initialization of a struct output variable in a vertex shader.304TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputStruct)305{306const std::string &shaderString =307"#version 300 es\n"308"precision mediump float;\n"309"struct MyS{\n"310" float a;\n"311" float b;\n"312"};\n"313"out MyS out1;\n"314"void main() {\n"315"}\n";316compileAssumeSuccess(shaderString);317VerifyOutputVariableInitializers verifier(mASTRoot);318319FindStructByName findStruct(ImmutableString("MyS"));320mASTRoot->traverse(&findStruct);321ASSERT(findStruct.isStructureFound());322323TType type(findStruct.getStructure(), false);324type.setQualifier(EvqVertexOut);325326TIntermTyped *expectedLValue = CreateLValueNode(ImmutableString("out1"), type);327EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValue));328delete expectedLValue;329}330331// Test the initialization of a varying variable in an ESSL1 vertex shader.332TEST_F(InitOutputVariablesWebGL2VertexShaderTest, OutputFromESSL1Shader)333{334const std::string &shaderString =335"precision mediump float;\n"336"varying vec4 out1;\n"337"void main() {\n"338"}\n";339compileAssumeSuccess(shaderString);340VerifyOutputVariableInitializers verifier(mASTRoot);341342TIntermTyped *expectedLValue =343CreateLValueNode(ImmutableString("out1"), TType(EbtFloat, EbpMedium, EvqVaryingOut, 4));344EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValue));345delete expectedLValue;346}347348// Test the initialization of output variables in a fragment shader.349TEST_F(InitOutputVariablesWebGL2FragmentShaderTest, Output)350{351const std::string &shaderString =352"#version 300 es\n"353"precision mediump float;\n"354"out vec4 out1;\n"355"void main() {\n"356"}\n";357compileAssumeSuccess(shaderString);358VerifyOutputVariableInitializers verifier(mASTRoot);359360TIntermTyped *expectedLValue =361CreateLValueNode(ImmutableString("out1"), TType(EbtFloat, EbpMedium, EvqFragmentOut, 4));362EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValue));363delete expectedLValue;364}365366// Test the initialization of gl_FragData in a WebGL2 ESSL1 fragment shader. Only writes to367// gl_FragData[0] should be found.368TEST_F(InitOutputVariablesWebGL2FragmentShaderTest, FragData)369{370const std::string &shaderString =371"precision mediump float;\n"372"void main() {\n"373" gl_FragData[0] = vec4(1.);\n"374"}\n";375compileAssumeSuccess(shaderString);376VerifyOutputVariableInitializers verifier(mASTRoot);377378ExpectedLValues expectedLValues = CreateIndexedLValueNodeList(379ImmutableString("gl_FragData"), TType(EbtFloat, EbpMedium, EvqFragData, 4), 1);380EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));381EXPECT_EQ(1u, verifier.getCandidates().size());382}383384// Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader. Only writes to385// gl_FragData[0] should be found.386TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragData)387{388const std::string &shaderString =389"precision mediump float;\n"390"void main() {\n"391" gl_FragData[0] = vec4(1.);\n"392"}\n";393compileAssumeSuccess(shaderString);394VerifyOutputVariableInitializers verifier(mASTRoot);395396// In the symbol table, gl_FragData array has 2 elements. However, only the 1st one should be397// initialized.398ExpectedLValues expectedLValues = CreateIndexedLValueNodeList(399ImmutableString("gl_FragData"), TType(EbtFloat, EbpMedium, EvqFragData, 4), 2);400EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));401EXPECT_EQ(1u, verifier.getCandidates().size());402}403404// Test the initialization of gl_FragData in a WebGL1 ESSL1 fragment shader with GL_EXT_draw_buffers405// enabled. All attachment slots should be initialized.406TEST_F(InitOutputVariablesWebGL1FragmentShaderTest, FragDataWithDrawBuffersExtEnabled)407{408const std::string &shaderString =409"#extension GL_EXT_draw_buffers : enable\n"410"precision mediump float;\n"411"void main() {\n"412" gl_FragData[0] = vec4(1.);\n"413"}\n";414compileAssumeSuccess(shaderString);415VerifyOutputVariableInitializers verifier(mASTRoot);416417ExpectedLValues expectedLValues = CreateIndexedLValueNodeList(418ImmutableString("gl_FragData"), TType(EbtFloat, EbpMedium, EvqFragData, 4), 2);419EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[0]));420EXPECT_TRUE(verifier.isExpectedLValueFound(expectedLValues[1]));421EXPECT_EQ(2u, verifier.getCandidates().size());422}423424// Test that gl_Position is initialized once in case it is not statically used and both425// SH_INIT_OUTPUT_VARIABLES and SH_INIT_GL_POSITION flags are set.426TEST_F(InitOutputVariablesWebGL2VertexShaderTest, InitGLPositionWhenNotStaticallyUsed)427{428const std::string &shaderString =429"#version 300 es\n"430"precision highp float;\n"431"void main() {\n"432"}\n";433compileAssumeSuccess(shaderString);434VerifyOutputVariableInitializers verifier(mASTRoot);435436TIntermTyped *glPosition =437CreateLValueNode(ImmutableString("gl_Position"), TType(EbtFloat, EbpHigh, EvqPosition, 4));438EXPECT_TRUE(verifier.isExpectedLValueFound(glPosition));439EXPECT_EQ(1u, verifier.getCandidates().size());440}441442// Test that gl_Position is initialized once in case it is statically used and both443// SH_INIT_OUTPUT_VARIABLES and SH_INIT_GL_POSITION flags are set.444TEST_F(InitOutputVariablesWebGL2VertexShaderTest, InitGLPositionOnceWhenStaticallyUsed)445{446const std::string &shaderString =447"#version 300 es\n"448"precision highp float;\n"449"void main() {\n"450" gl_Position = vec4(1.0);\n"451"}\n";452compileAssumeSuccess(shaderString);453VerifyOutputVariableInitializers verifier(mASTRoot);454455TIntermTyped *glPosition =456CreateLValueNode(ImmutableString("gl_Position"), TType(EbtFloat, EbpHigh, EvqPosition, 4));457EXPECT_TRUE(verifier.isExpectedLValueFound(glPosition));458EXPECT_EQ(1u, verifier.getCandidates().size());459}460461// Mirrors ClipDistanceTest.ThreeClipDistancesRedeclared462TEST_F(InitOutputVariablesVertexShaderClipDistanceTest, RedeclareClipDistance)463{464constexpr char shaderString[] = R"(465#extension GL_APPLE_clip_distance : require466467varying highp float gl_ClipDistance[3];468469void computeClipDistances(in vec4 position, in vec4 plane[3])470{471gl_ClipDistance[0] = dot(position, plane[0]);472gl_ClipDistance[1] = dot(position, plane[1]);473gl_ClipDistance[2] = dot(position, plane[2]);474}475476uniform vec4 u_plane[3];477478attribute vec2 a_position;479480void main()481{482gl_Position = vec4(a_position, 0.0, 1.0);483484computeClipDistances(gl_Position, u_plane);485})";486487compileAssumeSuccess(shaderString);488}489} // namespace sh490491492