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