1 //
2 // Copyright 2020 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 ValidateClipCullDistance function checks if the sum of array sizes for gl_ClipDistance and
7 // gl_CullDistance exceeds gl_MaxCombinedClipAndCullDistances
8 //
9
10 #include "ValidateClipCullDistance.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 class ValidateClipCullDistanceTraverser : public TIntermTraverser
29 {
30 public:
31 ValidateClipCullDistanceTraverser();
32 void validate(TDiagnostics *diagnostics,
33 const unsigned int maxCullDistances,
34 const unsigned int maxCombinedClipAndCullDistances,
35 uint8_t *clipDistanceSizeOut,
36 uint8_t *cullDistanceSizeOut,
37 bool *clipDistanceRedeclaredOut,
38 bool *cullDistanceRedeclaredOut,
39 bool *clipDistanceUsedOut);
40
41 private:
42 bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
43 bool visitBinary(Visit visit, TIntermBinary *node) override;
44
45 uint8_t mClipDistanceSize;
46 uint8_t mCullDistanceSize;
47
48 int8_t mMaxClipDistanceIndex;
49 int8_t mMaxCullDistanceIndex;
50
51 bool mHasNonConstClipDistanceIndex;
52 bool mHasNonConstCullDistanceIndex;
53
54 const TIntermSymbol *mClipDistance;
55 const TIntermSymbol *mCullDistance;
56 };
57
ValidateClipCullDistanceTraverser()58 ValidateClipCullDistanceTraverser::ValidateClipCullDistanceTraverser()
59 : TIntermTraverser(true, false, false),
60 mClipDistanceSize(0),
61 mCullDistanceSize(0),
62 mMaxClipDistanceIndex(-1),
63 mMaxCullDistanceIndex(-1),
64 mHasNonConstClipDistanceIndex(false),
65 mHasNonConstCullDistanceIndex(false),
66 mClipDistance(nullptr),
67 mCullDistance(nullptr)
68 {}
69
visitDeclaration(Visit visit,TIntermDeclaration * node)70 bool ValidateClipCullDistanceTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
71 {
72 const TIntermSequence &sequence = *(node->getSequence());
73
74 if (sequence.size() != 1)
75 {
76 return true;
77 }
78
79 const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
80 if (symbol == nullptr)
81 {
82 return true;
83 }
84
85 if (symbol->getName() == "gl_ClipDistance")
86 {
87 mClipDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
88 mClipDistance = symbol;
89 }
90 else if (symbol->getName() == "gl_CullDistance")
91 {
92 mCullDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
93 mCullDistance = symbol;
94 }
95
96 return true;
97 }
98
visitBinary(Visit visit,TIntermBinary * node)99 bool ValidateClipCullDistanceTraverser::visitBinary(Visit visit, TIntermBinary *node)
100 {
101 TOperator op = node->getOp();
102 if (op != EOpIndexDirect && op != EOpIndexIndirect)
103 {
104 return true;
105 }
106
107 TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
108 if (!left)
109 {
110 return true;
111 }
112
113 ImmutableString varName(left->getName());
114 if (varName != "gl_ClipDistance" && varName != "gl_CullDistance")
115 {
116 return true;
117 }
118
119 const TConstantUnion *constIdx = node->getRight()->getConstantValue();
120 if (constIdx)
121 {
122 int idx = 0;
123 switch (constIdx->getType())
124 {
125 case EbtInt:
126 idx = constIdx->getIConst();
127 break;
128 case EbtUInt:
129 idx = constIdx->getUConst();
130 break;
131 case EbtFloat:
132 idx = static_cast<int>(constIdx->getFConst());
133 break;
134 case EbtBool:
135 idx = constIdx->getBConst() ? 1 : 0;
136 break;
137 default:
138 UNREACHABLE();
139 break;
140 }
141
142 if (varName == "gl_ClipDistance")
143 {
144 if (idx > mMaxClipDistanceIndex)
145 {
146 mMaxClipDistanceIndex = static_cast<int8_t>(idx);
147 if (!mClipDistance)
148 {
149 mClipDistance = left;
150 }
151 }
152 }
153 else
154 {
155 ASSERT(varName == "gl_CullDistance");
156 if (idx > mMaxCullDistanceIndex)
157 {
158 mMaxCullDistanceIndex = static_cast<int8_t>(idx);
159 if (!mCullDistance)
160 {
161 mCullDistance = left;
162 }
163 }
164 }
165 }
166 else
167 {
168 if (varName == "gl_ClipDistance")
169 {
170 mHasNonConstClipDistanceIndex = true;
171 if (!mClipDistance)
172 {
173 mClipDistance = left;
174 }
175 }
176 else
177 {
178 ASSERT(varName == "gl_CullDistance");
179 mHasNonConstCullDistanceIndex = true;
180 if (!mCullDistance)
181 {
182 mCullDistance = left;
183 }
184 }
185 }
186
187 return true;
188 }
189
validate(TDiagnostics * diagnostics,const unsigned int maxCullDistances,const unsigned int maxCombinedClipAndCullDistances,uint8_t * clipDistanceSizeOut,uint8_t * cullDistanceSizeOut,bool * clipDistanceRedeclaredOut,bool * cullDistanceRedeclaredOut,bool * clipDistanceUsedOut)190 void ValidateClipCullDistanceTraverser::validate(TDiagnostics *diagnostics,
191 const unsigned int maxCullDistances,
192 const unsigned int maxCombinedClipAndCullDistances,
193 uint8_t *clipDistanceSizeOut,
194 uint8_t *cullDistanceSizeOut,
195 bool *clipDistanceRedeclaredOut,
196 bool *cullDistanceRedeclaredOut,
197 bool *clipDistanceUsedOut)
198 {
199 ASSERT(diagnostics);
200
201 if (mClipDistanceSize == 0 && mHasNonConstClipDistanceIndex)
202 {
203 error(*mClipDistance,
204 "The array must be sized by the shader either redeclaring it with a size or "
205 "indexing it only with constant integral expressions",
206 diagnostics);
207 }
208
209 if (mCullDistanceSize == 0 && mHasNonConstCullDistanceIndex)
210 {
211 error(*mCullDistance,
212 "The array must be sized by the shader either redeclaring it with a size or "
213 "indexing it only with constant integral expressions",
214 diagnostics);
215 }
216
217 unsigned int enabledClipDistances =
218 (mClipDistanceSize > 0 ? mClipDistanceSize
219 : (mClipDistance ? mMaxClipDistanceIndex + 1 : 0));
220 unsigned int enabledCullDistances =
221 (mCullDistanceSize > 0 ? mCullDistanceSize
222 : (mCullDistance ? mMaxCullDistanceIndex + 1 : 0));
223 unsigned int combinedClipAndCullDistances =
224 (enabledClipDistances > 0 && enabledCullDistances > 0
225 ? enabledClipDistances + enabledCullDistances
226 : 0);
227
228 if (enabledCullDistances > 0 && maxCullDistances == 0)
229 {
230 error(*mCullDistance, "Cull distance functionality is not available", diagnostics);
231 }
232
233 if (combinedClipAndCullDistances > maxCombinedClipAndCullDistances)
234 {
235 const TIntermSymbol *greaterSymbol =
236 (enabledClipDistances >= enabledCullDistances ? mClipDistance : mCullDistance);
237
238 std::stringstream strstr = sh::InitializeStream<std::stringstream>();
239 strstr << "The sum of 'gl_ClipDistance' and 'gl_CullDistance' size is greater than "
240 "gl_MaxCombinedClipAndCullDistances ("
241 << combinedClipAndCullDistances << " > " << maxCombinedClipAndCullDistances << ")";
242 error(*greaterSymbol, strstr.str().c_str(), diagnostics);
243 }
244
245 // Update the compiler state
246 *clipDistanceSizeOut = mClipDistanceSize ? mClipDistanceSize : (mMaxClipDistanceIndex + 1);
247 *cullDistanceSizeOut = mCullDistanceSize ? mCullDistanceSize : (mMaxCullDistanceIndex + 1);
248 *clipDistanceRedeclaredOut = mClipDistanceSize != 0;
249 *cullDistanceRedeclaredOut = mCullDistanceSize != 0;
250 *clipDistanceUsedOut = (mMaxClipDistanceIndex != -1) || mHasNonConstClipDistanceIndex;
251 }
252
253 } // anonymous namespace
254
ValidateClipCullDistance(TIntermBlock * root,TDiagnostics * diagnostics,const unsigned int maxCullDistances,const unsigned int maxCombinedClipAndCullDistances,uint8_t * clipDistanceSizeOut,uint8_t * cullDistanceSizeOut,bool * clipDistanceRedeclaredOut,bool * cullDistanceRedeclaredOut,bool * clipDistanceUsedOut)255 bool ValidateClipCullDistance(TIntermBlock *root,
256 TDiagnostics *diagnostics,
257 const unsigned int maxCullDistances,
258 const unsigned int maxCombinedClipAndCullDistances,
259 uint8_t *clipDistanceSizeOut,
260 uint8_t *cullDistanceSizeOut,
261 bool *clipDistanceRedeclaredOut,
262 bool *cullDistanceRedeclaredOut,
263 bool *clipDistanceUsedOut)
264 {
265 ValidateClipCullDistanceTraverser varyingValidator;
266 root->traverse(&varyingValidator);
267 int numErrorsBefore = diagnostics->numErrors();
268 varyingValidator.validate(diagnostics, maxCullDistances, maxCombinedClipAndCullDistances,
269 clipDistanceSizeOut, cullDistanceSizeOut, clipDistanceRedeclaredOut,
270 cullDistanceRedeclaredOut, clipDistanceUsedOut);
271 return (diagnostics->numErrors() == numErrorsBefore);
272 }
273
274 } // namespace sh
275