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