• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2002 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/SymbolTable.h"
14 #include "compiler/translator/tree_util/IntermTraverse.h"
15 #include "compiler/translator/util.h"
16 
17 namespace sh
18 {
19 
20 namespace
21 {
22 
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)23 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
24 {
25     diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
26 }
27 
28 int GetStructLocationCount(const TStructure *structure);
29 
GetFieldLocationCount(const TField * field)30 int GetFieldLocationCount(const TField *field)
31 {
32     int field_size         = 0;
33     const TType *fieldType = field->type();
34 
35     if (fieldType->getStruct() != nullptr)
36     {
37         field_size = GetStructLocationCount(fieldType->getStruct());
38     }
39     else if (fieldType->isMatrix())
40     {
41         field_size = fieldType->getNominalSize();
42     }
43     else
44     {
45         ASSERT(fieldType->getSecondarySize() == 1);
46         field_size = 1;
47     }
48 
49     if (fieldType->isArray())
50     {
51         field_size *= fieldType->getArraySizeProduct();
52     }
53 
54     return field_size;
55 }
56 
GetStructLocationCount(const TStructure * structure)57 int GetStructLocationCount(const TStructure *structure)
58 {
59     int totalLocation = 0;
60     for (const TField *field : structure->fields())
61     {
62         totalLocation += GetFieldLocationCount(field);
63     }
64     return totalLocation;
65 }
66 
GetInterfaceBlockLocationCount(const TType & varyingType,bool ignoreVaryingArraySize)67 int GetInterfaceBlockLocationCount(const TType &varyingType, bool ignoreVaryingArraySize)
68 {
69     int totalLocation = 0;
70     for (const TField *field : varyingType.getInterfaceBlock()->fields())
71     {
72         totalLocation += GetFieldLocationCount(field);
73     }
74 
75     if (!ignoreVaryingArraySize && varyingType.isArray())
76     {
77         totalLocation *= varyingType.getArraySizeProduct();
78     }
79     return totalLocation;
80 }
81 
GetLocationCount(const TType & varyingType,bool ignoreVaryingArraySize)82 int GetLocationCount(const TType &varyingType, bool ignoreVaryingArraySize)
83 {
84     ASSERT(!varyingType.isInterfaceBlock());
85 
86     if (varyingType.getStruct() != nullptr)
87     {
88         int totalLocation = 0;
89         for (const TField *field : varyingType.getStruct()->fields())
90         {
91             const TType *fieldType = field->type();
92             ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
93 
94             totalLocation += GetFieldLocationCount(field);
95         }
96         return totalLocation;
97     }
98 
99     ASSERT(varyingType.isMatrix() || varyingType.getSecondarySize() == 1);
100     int elementLocationCount = varyingType.isMatrix() ? varyingType.getNominalSize() : 1;
101 
102     // [GL_EXT_shader_io_blocks SPEC Chapter 4.4.1]
103     // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
104     // evaluation inputs all have an additional level of arrayness relative to other shader inputs
105     // and outputs. This outer array level is removed from the type before considering how many
106     // locations the type consumes.
107     if (ignoreVaryingArraySize)
108     {
109         // Array-of-arrays cannot be inputs or outputs of a geometry shader.
110         // (GL_EXT_geometry_shader SPEC issues(5))
111         ASSERT(!varyingType.isArrayOfArrays());
112         return elementLocationCount;
113     }
114 
115     return elementLocationCount * varyingType.getArraySizeProduct();
116 }
117 
ShouldIgnoreVaryingArraySize(TQualifier qualifier,GLenum shaderType)118 bool ShouldIgnoreVaryingArraySize(TQualifier qualifier, GLenum shaderType)
119 {
120     bool isVaryingIn = IsShaderIn(qualifier) && qualifier != EvqPatchIn;
121 
122     switch (shaderType)
123     {
124         case GL_GEOMETRY_SHADER:
125         case GL_TESS_EVALUATION_SHADER:
126             return isVaryingIn;
127         case GL_TESS_CONTROL_SHADER:
128             return (IsShaderOut(qualifier) && qualifier != EvqPatchOut) || isVaryingIn;
129         default:
130             return false;
131     }
132 }
133 
134 struct SymbolAndField
135 {
136     const TIntermSymbol *symbol;
137     const TField *field;
138 };
139 using LocationMap = std::map<int, SymbolAndField>;
140 
MarkVaryingLocations(TDiagnostics * diagnostics,const TIntermSymbol * varying,const TField * field,int location,int elementCount,LocationMap * locationMap)141 void MarkVaryingLocations(TDiagnostics *diagnostics,
142                           const TIntermSymbol *varying,
143                           const TField *field,
144                           int location,
145                           int elementCount,
146                           LocationMap *locationMap)
147 {
148     for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
149     {
150         const int offsetLocation = location + elementIndex;
151         auto conflict            = locationMap->find(offsetLocation);
152         if (conflict != locationMap->end())
153         {
154             std::stringstream strstr = sh::InitializeStream<std::stringstream>();
155             strstr << "'" << varying->getName();
156             if (field)
157             {
158                 strstr << "." << field->name();
159             }
160             strstr << "' conflicting location with '" << conflict->second.symbol->getName();
161             if (conflict->second.field)
162             {
163                 strstr << "." << conflict->second.field->name();
164             }
165             strstr << "'";
166             error(*varying, strstr.str().c_str(), diagnostics);
167         }
168         else
169         {
170             (*locationMap)[offsetLocation] = {varying, field};
171         }
172     }
173 }
174 
175 using VaryingVector = std::vector<const TIntermSymbol *>;
176 
ValidateShaderInterfaceAndAssignLocations(TDiagnostics * diagnostics,const VaryingVector & varyingVector,GLenum shaderType)177 void ValidateShaderInterfaceAndAssignLocations(TDiagnostics *diagnostics,
178                                                const VaryingVector &varyingVector,
179                                                GLenum shaderType)
180 {
181     // Location conflicts can only happen when there are two or more varyings in varyingVector.
182     if (varyingVector.size() <= 1)
183     {
184         return;
185     }
186 
187     LocationMap locationMap;
188     for (const TIntermSymbol *varying : varyingVector)
189     {
190         const TType &varyingType = varying->getType();
191         const int location       = varyingType.getLayoutQualifier().location;
192         ASSERT(location >= 0);
193 
194         bool ignoreVaryingArraySize =
195             ShouldIgnoreVaryingArraySize(varying->getQualifier(), shaderType);
196 
197         // A varying is either:
198         //
199         // - A vector or matrix, which can take a number of contiguous locations
200         // - A struct, which also takes a number of contiguous locations
201         // - An interface block.
202         //
203         // Interface blocks can assign arbitrary locations to their fields, for example:
204         //
205         //     layout(location = 4) in block {
206         //         vec4 a;                         // gets location 4
207         //         vec4 b;                         // gets location 5
208         //         layout(location = 7) vec4 c;    // gets location 7
209         //         vec4 d;                         // gets location 8
210         //         layout (location = 1) vec4 e;   // gets location 1
211         //         vec4 f;                         // gets location 2
212         //     };
213         //
214         // The following code therefore takes two paths.  For non-interface-block types, the number
215         // of locations for the varying is calculated (elementCount), and all locations in
216         // [location, location + elementCount) are marked as occupied.
217         //
218         // For interface blocks, a similar algorithm is implemented except each field is
219         // individually marked with the location either advancing automatically or taking its value
220         // from the field's layout qualifier.
221 
222         if (varyingType.isInterfaceBlock())
223         {
224             int currentLocation       = location;
225             bool anyFieldWithLocation = false;
226 
227             for (const TField *field : varyingType.getInterfaceBlock()->fields())
228             {
229                 const int fieldLocation = field->type()->getLayoutQualifier().location;
230                 if (fieldLocation >= 0)
231                 {
232                     currentLocation      = fieldLocation;
233                     anyFieldWithLocation = true;
234                 }
235 
236                 const int fieldLocationCount = GetFieldLocationCount(field);
237                 MarkVaryingLocations(diagnostics, varying, field, currentLocation,
238                                      fieldLocationCount, &locationMap);
239 
240                 currentLocation += fieldLocationCount;
241             }
242 
243             // Array interface blocks can't have location qualifiers on fields.
244             ASSERT(ignoreVaryingArraySize || !anyFieldWithLocation || !varyingType.isArray());
245 
246             if (!ignoreVaryingArraySize && varyingType.isArray())
247             {
248                 // This is only reached if the varying is an array of interface blocks, with only a
249                 // layout qualifier on the block itself, for example:
250                 //
251                 //     layout(location = 4) in block {
252                 //         vec4 a;
253                 //         vec4 b;
254                 //         vec4 c;
255                 //         vec4 d;
256                 //     } instance[N];
257                 //
258                 // The locations for instance[0] are already marked by the above code, so we need to
259                 // further mark locations occupied by instances [1, N).  |currentLocation| is
260                 // already just past the end of instance[0], which is the beginning of instance[1].
261                 //
262                 int remainingLocations = currentLocation * (varyingType.getArraySizeProduct() - 1);
263                 MarkVaryingLocations(diagnostics, varying, nullptr, currentLocation,
264                                      remainingLocations, &locationMap);
265             }
266         }
267         else
268         {
269             const int elementCount = GetLocationCount(varying->getType(), ignoreVaryingArraySize);
270             MarkVaryingLocations(diagnostics, varying, nullptr, location, elementCount,
271                                  &locationMap);
272         }
273     }
274 }
275 
276 class ValidateVaryingLocationsTraverser : public TIntermTraverser
277 {
278   public:
279     ValidateVaryingLocationsTraverser(GLenum shaderType);
280     void validate(TDiagnostics *diagnostics);
281 
282   private:
283     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
284     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
285 
286     VaryingVector mInputVaryingsWithLocation;
287     VaryingVector mOutputVaryingsWithLocation;
288     GLenum mShaderType;
289 };
290 
ValidateVaryingLocationsTraverser(GLenum shaderType)291 ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
292     : TIntermTraverser(true, false, false), mShaderType(shaderType)
293 {}
294 
visitDeclaration(Visit visit,TIntermDeclaration * node)295 bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
296 {
297     const TIntermSequence &sequence = *(node->getSequence());
298     ASSERT(!sequence.empty());
299 
300     const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
301     if (symbol == nullptr)
302     {
303         return false;
304     }
305 
306     if (symbol->variable().symbolType() == SymbolType::Empty)
307     {
308         return false;
309     }
310 
311     // Collect varyings that have explicit 'location' qualifiers.
312     const TQualifier qualifier = symbol->getQualifier();
313     if (symbol->getType().getLayoutQualifier().location != -1)
314     {
315         if (IsVaryingIn(qualifier))
316         {
317             mInputVaryingsWithLocation.push_back(symbol);
318         }
319         else if (IsVaryingOut(qualifier))
320         {
321             mOutputVaryingsWithLocation.push_back(symbol);
322         }
323     }
324 
325     return false;
326 }
327 
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)328 bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
329                                                                 TIntermFunctionDefinition *node)
330 {
331     // We stop traversing function definitions because varyings cannot be defined in a function.
332     return false;
333 }
334 
validate(TDiagnostics * diagnostics)335 void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
336 {
337     ASSERT(diagnostics);
338 
339     ValidateShaderInterfaceAndAssignLocations(diagnostics, mInputVaryingsWithLocation, mShaderType);
340     ValidateShaderInterfaceAndAssignLocations(diagnostics, mOutputVaryingsWithLocation,
341                                               mShaderType);
342 }
343 
344 }  // anonymous namespace
345 
CalculateVaryingLocationCount(const TType & varyingType,GLenum shaderType)346 unsigned int CalculateVaryingLocationCount(const TType &varyingType, GLenum shaderType)
347 {
348     const TQualifier qualifier        = varyingType.getQualifier();
349     const bool ignoreVaryingArraySize = ShouldIgnoreVaryingArraySize(qualifier, shaderType);
350 
351     if (varyingType.isInterfaceBlock())
352     {
353         return GetInterfaceBlockLocationCount(varyingType, ignoreVaryingArraySize);
354     }
355 
356     return GetLocationCount(varyingType, ignoreVaryingArraySize);
357 }
358 
ValidateVaryingLocations(TIntermBlock * root,TDiagnostics * diagnostics,GLenum shaderType)359 bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
360 {
361     ValidateVaryingLocationsTraverser varyingValidator(shaderType);
362     root->traverse(&varyingValidator);
363     int numErrorsBefore = diagnostics->numErrors();
364     varyingValidator.validate(diagnostics);
365     return (diagnostics->numErrors() == numErrorsBefore);
366 }
367 
368 }  // namespace sh
369