• 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 
GetLocationCount(const TIntermSymbol * varying,bool ignoreVaryingArraySize)28 unsigned int GetLocationCount(const TIntermSymbol *varying, bool ignoreVaryingArraySize)
29 {
30     const auto &varyingType = varying->getType();
31     if (varyingType.getStruct() != nullptr)
32     {
33         ASSERT(!varyingType.isArray());
34         unsigned int totalLocation = 0;
35         for (const auto *field : varyingType.getStruct()->fields())
36         {
37             const auto *fieldType = field->type();
38             ASSERT(fieldType->getStruct() == nullptr && !fieldType->isArray());
39 
40             totalLocation +=
41                 fieldType->isMatrix() ? fieldType->getNominalSize() : fieldType->getSecondarySize();
42         }
43         return totalLocation;
44     }
45     // [GL_EXT_shader_io_blocks SPEC Chapter 4.4.1]
46     // Geometry shader inputs, tessellation control shader inputs and outputs, and tessellation
47     // evaluation inputs all have an additional level of arrayness relative to other shader inputs
48     // and outputs. This outer array level is removed from the type before considering how many
49     // locations the type consumes.
50     else if (ignoreVaryingArraySize)
51     {
52         // Array-of-arrays cannot be inputs or outputs of a geometry shader.
53         // (GL_EXT_geometry_shader SPEC issues(5))
54         ASSERT(!varyingType.isArrayOfArrays());
55         return varyingType.getSecondarySize();
56     }
57     else if (varyingType.isMatrix())
58     {
59         return varyingType.getNominalSize() * varyingType.getArraySizeProduct();
60     }
61     else
62     {
63         return varyingType.getArraySizeProduct();
64     }
65 }
66 
67 using VaryingVector = std::vector<const TIntermSymbol *>;
68 
ValidateShaderInterface(TDiagnostics * diagnostics,VaryingVector & varyingVector,bool ignoreVaryingArraySize)69 void ValidateShaderInterface(TDiagnostics *diagnostics,
70                              VaryingVector &varyingVector,
71                              bool ignoreVaryingArraySize)
72 {
73     // Location conflicts can only happen when there are two or more varyings in varyingVector.
74     if (varyingVector.size() <= 1)
75     {
76         return;
77     }
78 
79     std::map<int, const TIntermSymbol *> locationMap;
80     for (const TIntermSymbol *varying : varyingVector)
81     {
82         const int location = varying->getType().getLayoutQualifier().location;
83         ASSERT(location >= 0);
84 
85         const int elementCount = GetLocationCount(varying, ignoreVaryingArraySize);
86         for (int elementIndex = 0; elementIndex < elementCount; ++elementIndex)
87         {
88             const int offsetLocation = location + elementIndex;
89             if (locationMap.find(offsetLocation) != locationMap.end())
90             {
91                 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
92                 strstr << "'" << varying->getName()
93                        << "' conflicting location with previously defined '"
94                        << locationMap[offsetLocation]->getName() << "'";
95                 error(*varying, strstr.str().c_str(), diagnostics);
96             }
97             else
98             {
99                 locationMap[offsetLocation] = varying;
100             }
101         }
102     }
103 }
104 
105 class ValidateVaryingLocationsTraverser : public TIntermTraverser
106 {
107   public:
108     ValidateVaryingLocationsTraverser(GLenum shaderType);
109     void validate(TDiagnostics *diagnostics);
110 
111   private:
112     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
113     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
114 
115     VaryingVector mInputVaryingsWithLocation;
116     VaryingVector mOutputVaryingsWithLocation;
117     GLenum mShaderType;
118 };
119 
ValidateVaryingLocationsTraverser(GLenum shaderType)120 ValidateVaryingLocationsTraverser::ValidateVaryingLocationsTraverser(GLenum shaderType)
121     : TIntermTraverser(true, false, false), mShaderType(shaderType)
122 {}
123 
visitDeclaration(Visit visit,TIntermDeclaration * node)124 bool ValidateVaryingLocationsTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
125 {
126     const TIntermSequence &sequence = *(node->getSequence());
127     ASSERT(!sequence.empty());
128 
129     const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
130     if (symbol == nullptr)
131     {
132         return false;
133     }
134 
135     if (symbol->variable().symbolType() == SymbolType::Empty)
136     {
137         return false;
138     }
139 
140     // Collect varyings that have explicit 'location' qualifiers.
141     const TQualifier qualifier = symbol->getQualifier();
142     if (symbol->getType().getLayoutQualifier().location != -1)
143     {
144         if (IsVaryingIn(qualifier))
145         {
146             mInputVaryingsWithLocation.push_back(symbol);
147         }
148         else if (IsVaryingOut(qualifier))
149         {
150             mOutputVaryingsWithLocation.push_back(symbol);
151         }
152     }
153 
154     return false;
155 }
156 
visitFunctionDefinition(Visit visit,TIntermFunctionDefinition * node)157 bool ValidateVaryingLocationsTraverser::visitFunctionDefinition(Visit visit,
158                                                                 TIntermFunctionDefinition *node)
159 {
160     // We stop traversing function definitions because varyings cannot be defined in a function.
161     return false;
162 }
163 
validate(TDiagnostics * diagnostics)164 void ValidateVaryingLocationsTraverser::validate(TDiagnostics *diagnostics)
165 {
166     ASSERT(diagnostics);
167 
168     ValidateShaderInterface(diagnostics, mInputVaryingsWithLocation,
169                             mShaderType == GL_GEOMETRY_SHADER_EXT);
170     ValidateShaderInterface(diagnostics, mOutputVaryingsWithLocation, false);
171 }
172 
173 }  // anonymous namespace
174 
CalculateVaryingLocationCount(TIntermSymbol * varying,GLenum shaderType)175 unsigned int CalculateVaryingLocationCount(TIntermSymbol *varying, GLenum shaderType)
176 {
177     return GetLocationCount(varying, shaderType == GL_GEOMETRY_SHADER_EXT);
178 }
179 
ValidateVaryingLocations(TIntermBlock * root,TDiagnostics * diagnostics,GLenum shaderType)180 bool ValidateVaryingLocations(TIntermBlock *root, TDiagnostics *diagnostics, GLenum shaderType)
181 {
182     ValidateVaryingLocationsTraverser varyingValidator(shaderType);
183     root->traverse(&varyingValidator);
184     int numErrorsBefore = diagnostics->numErrors();
185     varyingValidator.validate(diagnostics);
186     return (diagnostics->numErrors() == numErrorsBefore);
187 }
188 
189 }  // namespace sh
190