1 //
2 // Copyright (c) 2002-2017 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // The ValidateVaryingLocations function checks if there exists location conflicts on shader
7 // varyings.
8 //
9 
10 #include "ValidateVaryingLocations.h"
11 
12 #include "compiler/translator/Diagnostics.h"
13 #include "compiler/translator/IntermTraverse.h"
14 #include "compiler/translator/util.h"
15 
16 namespace sh
17 {
18 
19 namespace
20 {
21 
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)22 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
23 {
24     diagnostics->error(symbol.getLine(), reason, symbol.getSymbol().c_str());
25 }
26 
GetLocationCount(const TIntermSymbol * varying,bool ignoreVaryingArraySize)27 int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
28 {
29     const auto &varyingType = varying->getType();
30     if (varyingType.getStruct() != nullptr)
31     {
32         ASSERT(!varyingType.isArray());
33         int totalLocation = 0;
34         for (const auto *field : varyingType.getStruct()->fields())
35         {
36             const auto *fieldType = field->type();
37             ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
38 
39             totalLocation += fieldType->getSecondarySize();
40         }
41         return totalLocation;
42     }
43     // [GL_OES_shader_io_blocks SPEC Chapter 4.4.1]
44     // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
45     // evaluation inputs all have an additional level of arrayness relative to other shader inputs
46     // and outputs. This outer array level is removed from the type before considering how many
47     // locations the type consumes.
48     else if (ignoreVaryingArraySize)
49     {
50         // Array-of-arrays cannot be inputs or outputs of a geometry shader.
51         // (GL_OES_geometry_shader SPEC issues(5))
52         ASSERT(!varyingType.isArrayOfArrays());
53         return varyingType.getSecondarySize();
54     }
55     else
56     {
57         return varyingType.getSecondarySize() * static_cast<int>(varyingType.getArraySizeProduct());
58     }
59 }
60 
61 using VaryingVector = std::vector<const TIntermSymbol *>;
62 
ValidateShaderInterface(TDiagnostics * diagnostics,VaryingVector & varyingVector,bool ignoreVaryingArraySize)63 void ValidateShaderInterface(TDiagnostics *diagnostics,
64                              VaryingVector &varyingVector,
65                              bool ignoreVaryingArraySize)
66 {
67     // Location conflicts can only happen when there are two or more varyings in varyingVector.
68     if (varyingVector.size() <= 1)
69     {
70         return;
71     }
72 
73     std::map<int, const TIntermSymbol *> locationMap;
74     for (const TIntermSymbol *varying : varyingVector)
75     {
76         const int location = varying->getType().getLayoutQualifier().location;
77         ASSERT(location >= 0);
78 
79         const int elementCount = GetLocationCount(varying, ignoreVaryingArraySize);
80         for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
81         {
82             const int offsetLocation = location + elementIndex;
83             if (locationMap.find(offsetLocation) != locationMap.end())
84             {
85                 std::stringstream strstr;
86                 strstr << "'" << varying->getSymbol()
87                        << "' conflicting location with previously defined '"
88                        << locationMap[offsetLocation]->getSymbol() << "'";
89                 error(*varying, strstr.str().c_str(), diagnostics);
90             }
91             else
92             {
93                 locationMap[offsetLocation] = varying;
94             }
95         }
96     }
97 }
98 
99 class ValidateVaryingLocationsTraverser : public TIntermTraverser
100 {
101   public:
102     ValidateVaryingLocationsTraverser(GLenum shaderType);
103     void validate(TDiagnostics *diagnostics);
104 
105   private:
106     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
107     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
108 
109     VaryingVector mInputVaryingsWithLocation;
110     VaryingVector mOutputVaryingsWithLocation;
111     GLenum mShaderType;
112 };
113 
ValidateVaryingLocationsTraverser(GLenum shaderType)114 ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
115     : TIntermTraverser(true, false, false), mShaderType(shaderType)
116 {
117 }
118 
visitDeclaration(Visit visit,TIntermDeclaration * node)119 bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
120 {
121     const TIntermSequence &sequence = *(node->getSequence());
122     ASSERT(!sequence.empty());
123 
124     const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
125     if (symbol == nullptr)
126     {
127         return false;
128     }
129 
130     // Collect varyings that have explicit 'location' qualifiers.
131     const TQualifier qualifier = symbol->getQualifier();
132     if (symbol->getType().getLayoutQualifier().location != -1)
133     {
134         if (IsVaryingIn(qualifier))
135         {
136             mInputVaryingsWithLocation.push_back(symbol);
137         }
138         else if (IsVaryingOut(qualifier))
139         {
140             mOutputVaryingsWithLocation.push_back(symbol);
141         }
142     }
143 
144     return false;
145 }
146 
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)147 bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
148                                                                 TIntermFunctionDefinition *node)
149 {
150     // We stop traversing function definitions because varyings cannot be defined in a function.
151     return false;
152 }
153 
validate(TDiagnostics * diagnostics)154 void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
155 {
156     ASSERT(diagnostics);
157 
158     ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation,
159                             mShaderType == GL_GEOMETRY_SHADER_OES);
160     ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false);
161 }
162 
163 }  // anonymous namespace
164 
ValidateVaryingLocations(TIntermBlock * root,TDiagnostics * diagnostics,GLenum shaderType)165 bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
166 {
167     ValidateVaryingLocationsTraverser varyingValidator(shaderType);
168     root->traverse(&varyingValidator);
169     int numErrorsBefore = diagnostics->numErrors();
170     varyingValidator.validate(diagnostics);
171     return (diagnostics->numErrors() == numErrorsBefore);
172 }
173 
174 }  // namespace sh