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