xref: /aosp_15_r20/external/angle/src/compiler/translator/ValidateClipCullDistance.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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:
7 // * gathers clip/cull distance usages
8 // * checks if the sum of array sizes for gl_ClipDistance and
9 //   gl_CullDistance exceeds gl_MaxCombinedClipAndCullDistances
10 // * checks if length() operator is used correctly
11 // * adds an explicit clip/cull distance declaration
12 //
13 
14 #include "ValidateClipCullDistance.h"
15 
16 #include "compiler/translator/Diagnostics.h"
17 #include "compiler/translator/SymbolTable.h"
18 #include "compiler/translator/tree_util/IntermTraverse.h"
19 #include "compiler/translator/tree_util/ReplaceVariable.h"
20 #include "compiler/translator/util.h"
21 
22 namespace sh
23 {
24 
25 namespace
26 {
27 
error(const TIntermSymbol & symbol,const char * reason,TDiagnostics * diagnostics)28 void error(const TIntermSymbol &symbol, const char *reason, TDiagnostics *diagnostics)
29 {
30     diagnostics->error(symbol.getLine(), reason, symbol.getName().data());
31 }
32 
33 class ValidateClipCullDistanceTraverser : public TIntermTraverser
34 {
35   public:
36     ValidateClipCullDistanceTraverser();
37     void validate(TDiagnostics *diagnostics,
38                   const unsigned int maxCombinedClipAndCullDistances,
39                   uint8_t *clipDistanceSizeOut,
40                   uint8_t *cullDistanceSizeOut,
41                   bool *clipDistanceRedeclaredOut,
42                   bool *cullDistanceRedeclaredOut,
43                   bool *clipDistanceUsedOut);
44 
45   private:
46     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
47     bool visitBinary(Visit visit, TIntermBinary *node) override;
48 
49     uint8_t mClipDistanceSize;
50     uint8_t mCullDistanceSize;
51 
52     int8_t mMaxClipDistanceIndex;
53     int8_t mMaxCullDistanceIndex;
54 
55     bool mHasNonConstClipDistanceIndex;
56     bool mHasNonConstCullDistanceIndex;
57 
58     const TIntermSymbol *mClipDistance;
59     const TIntermSymbol *mCullDistance;
60 };
61 
ValidateClipCullDistanceTraverser()62 ValidateClipCullDistanceTraverser::ValidateClipCullDistanceTraverser()
63     : TIntermTraverser(true, false, false),
64       mClipDistanceSize(0),
65       mCullDistanceSize(0),
66       mMaxClipDistanceIndex(-1),
67       mMaxCullDistanceIndex(-1),
68       mHasNonConstClipDistanceIndex(false),
69       mHasNonConstCullDistanceIndex(false),
70       mClipDistance(nullptr),
71       mCullDistance(nullptr)
72 {}
73 
visitDeclaration(Visit visit,TIntermDeclaration * node)74 bool ValidateClipCullDistanceTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
75 {
76     const TIntermSequence &sequence = *(node->getSequence());
77 
78     if (sequence.size() != 1)
79     {
80         return true;
81     }
82 
83     const TIntermSymbol *symbol = sequence.front()->getAsSymbolNode();
84     if (symbol == nullptr)
85     {
86         return true;
87     }
88 
89     if (symbol->getName() == "gl_ClipDistance")
90     {
91         mClipDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
92         mClipDistance     = symbol;
93     }
94     else if (symbol->getName() == "gl_CullDistance")
95     {
96         mCullDistanceSize = static_cast<uint8_t>(symbol->getOutermostArraySize());
97         mCullDistance     = symbol;
98     }
99 
100     return true;
101 }
102 
visitBinary(Visit visit,TIntermBinary * node)103 bool ValidateClipCullDistanceTraverser::visitBinary(Visit visit, TIntermBinary *node)
104 {
105     TOperator op = node->getOp();
106     if (op != EOpIndexDirect && op != EOpIndexIndirect)
107     {
108         return true;
109     }
110 
111     TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
112     if (!left)
113     {
114         return true;
115     }
116 
117     ImmutableString varName(left->getName());
118     if (varName != "gl_ClipDistance" && varName != "gl_CullDistance")
119     {
120         return true;
121     }
122 
123     const TConstantUnion *constIdx = node->getRight()->getConstantValue();
124     if (constIdx)
125     {
126         int idx = 0;
127         switch (constIdx->getType())
128         {
129             case EbtInt:
130                 idx = constIdx->getIConst();
131                 break;
132             case EbtUInt:
133                 idx = constIdx->getUConst();
134                 break;
135             default:
136                 UNREACHABLE();
137                 break;
138         }
139 
140         if (varName == "gl_ClipDistance")
141         {
142             if (idx > mMaxClipDistanceIndex)
143             {
144                 mMaxClipDistanceIndex = static_cast<int8_t>(idx);
145                 if (!mClipDistance)
146                 {
147                     mClipDistance = left;
148                 }
149             }
150         }
151         else
152         {
153             ASSERT(varName == "gl_CullDistance");
154             if (idx > mMaxCullDistanceIndex)
155             {
156                 mMaxCullDistanceIndex = static_cast<int8_t>(idx);
157                 if (!mCullDistance)
158                 {
159                     mCullDistance = left;
160                 }
161             }
162         }
163     }
164     else
165     {
166         if (varName == "gl_ClipDistance")
167         {
168             mHasNonConstClipDistanceIndex = true;
169             if (!mClipDistance)
170             {
171                 mClipDistance = left;
172             }
173         }
174         else
175         {
176             ASSERT(varName == "gl_CullDistance");
177             mHasNonConstCullDistanceIndex = true;
178             if (!mCullDistance)
179             {
180                 mCullDistance = left;
181             }
182         }
183     }
184 
185     return true;
186 }
187 
validate(TDiagnostics * diagnostics,const unsigned int maxCombinedClipAndCullDistances,uint8_t * clipDistanceSizeOut,uint8_t * cullDistanceSizeOut,bool * clipDistanceRedeclaredOut,bool * cullDistanceRedeclaredOut,bool * clipDistanceUsedOut)188 void ValidateClipCullDistanceTraverser::validate(TDiagnostics *diagnostics,
189                                                  const unsigned int maxCombinedClipAndCullDistances,
190                                                  uint8_t *clipDistanceSizeOut,
191                                                  uint8_t *cullDistanceSizeOut,
192                                                  bool *clipDistanceRedeclaredOut,
193                                                  bool *cullDistanceRedeclaredOut,
194                                                  bool *clipDistanceUsedOut)
195 {
196     ASSERT(diagnostics);
197 
198     if (mClipDistanceSize == 0 && mHasNonConstClipDistanceIndex)
199     {
200         error(*mClipDistance,
201               "The array must be sized by the shader either redeclaring it with a size or "
202               "indexing it only with constant integral expressions",
203               diagnostics);
204     }
205 
206     if (mCullDistanceSize == 0 && mHasNonConstCullDistanceIndex)
207     {
208         error(*mCullDistance,
209               "The array must be sized by the shader either redeclaring it with a size or "
210               "indexing it only with constant integral expressions",
211               diagnostics);
212     }
213 
214     unsigned int enabledClipDistances =
215         (mClipDistanceSize > 0 ? mClipDistanceSize
216                                : (mClipDistance ? mMaxClipDistanceIndex + 1 : 0));
217     unsigned int enabledCullDistances =
218         (mCullDistanceSize > 0 ? mCullDistanceSize
219                                : (mCullDistance ? mMaxCullDistanceIndex + 1 : 0));
220     unsigned int combinedClipAndCullDistances =
221         (enabledClipDistances > 0 && enabledCullDistances > 0
222              ? enabledClipDistances + enabledCullDistances
223              : 0);
224 
225     // When cull distances are not supported, i.e., when GL_ANGLE_clip_cull_distance is
226     // exposed but GL_EXT_clip_cull_distance is not exposed, the combined limit is 0.
227     if (enabledCullDistances > 0 && maxCombinedClipAndCullDistances == 0)
228     {
229         error(*mCullDistance, "Cull distance functionality is not available", diagnostics);
230     }
231 
232     if (combinedClipAndCullDistances > maxCombinedClipAndCullDistances)
233     {
234         const TIntermSymbol *greaterSymbol =
235             (enabledClipDistances >= enabledCullDistances ? mClipDistance : mCullDistance);
236 
237         std::stringstream strstr = sh::InitializeStream<std::stringstream>();
238         strstr << "The sum of 'gl_ClipDistance' and 'gl_CullDistance' size is greater than "
239                   "gl_MaxCombinedClipAndCullDistances ("
240                << combinedClipAndCullDistances << " > " << maxCombinedClipAndCullDistances << ")";
241         error(*greaterSymbol, strstr.str().c_str(), diagnostics);
242     }
243 
244     // Update the compiler state
245     *clipDistanceSizeOut = mClipDistanceSize ? mClipDistanceSize : (mMaxClipDistanceIndex + 1);
246     *cullDistanceSizeOut = mCullDistanceSize ? mCullDistanceSize : (mMaxCullDistanceIndex + 1);
247     *clipDistanceRedeclaredOut = mClipDistanceSize != 0;
248     *cullDistanceRedeclaredOut = mCullDistanceSize != 0;
249     *clipDistanceUsedOut       = (mMaxClipDistanceIndex != -1) || mHasNonConstClipDistanceIndex;
250 }
251 
252 class ValidateClipCullDistanceLengthTraverser : public TIntermTraverser
253 {
254   public:
255     ValidateClipCullDistanceLengthTraverser(TDiagnostics *diagnostics,
256                                             uint8_t clipDistanceSized,
257                                             uint8_t cullDistanceSized);
258 
259   private:
260     bool visitUnary(Visit visit, TIntermUnary *node) override;
261 
262     TDiagnostics *mDiagnostics;
263     const bool mClipDistanceSized;
264     const bool mCullDistanceSized;
265 };
266 
ValidateClipCullDistanceLengthTraverser(TDiagnostics * diagnostics,uint8_t clipDistanceSize,uint8_t cullDistanceSize)267 ValidateClipCullDistanceLengthTraverser::ValidateClipCullDistanceLengthTraverser(
268     TDiagnostics *diagnostics,
269     uint8_t clipDistanceSize,
270     uint8_t cullDistanceSize)
271     : TIntermTraverser(true, false, false),
272       mDiagnostics(diagnostics),
273       mClipDistanceSized(clipDistanceSize > 0),
274       mCullDistanceSized(cullDistanceSize > 0)
275 {}
276 
visitUnary(Visit visit,TIntermUnary * node)277 bool ValidateClipCullDistanceLengthTraverser::visitUnary(Visit visit, TIntermUnary *node)
278 {
279     if (node->getOp() == EOpArrayLength)
280     {
281         TIntermTyped *operand = node->getOperand();
282         if ((operand->getQualifier() == EvqClipDistance && !mClipDistanceSized) ||
283             (operand->getQualifier() == EvqCullDistance && !mCullDistanceSized))
284         {
285             error(*operand->getAsSymbolNode(),
286                   "The length() method cannot be called on an array that is not "
287                   "runtime sized and also has not yet been explicitly sized",
288                   mDiagnostics);
289         }
290     }
291     return true;
292 }
293 
ReplaceAndDeclareVariable(TCompiler * compiler,TIntermBlock * root,const ImmutableString & name,unsigned int size)294 bool ReplaceAndDeclareVariable(TCompiler *compiler,
295                                TIntermBlock *root,
296                                const ImmutableString &name,
297                                unsigned int size)
298 {
299     const TVariable *var = static_cast<const TVariable *>(
300         compiler->getSymbolTable().findBuiltIn(name, compiler->getShaderVersion()));
301     ASSERT(var != nullptr);
302 
303     if (size != var->getType().getOutermostArraySize())
304     {
305         TType *resizedType = new TType(var->getType());
306         resizedType->setArraySize(0, size);
307         TVariable *resizedVar =
308             new TVariable(&compiler->getSymbolTable(), name, resizedType, SymbolType::BuiltIn);
309         if (!ReplaceVariable(compiler, root, var, resizedVar))
310         {
311             return false;
312         }
313         var = resizedVar;
314     }
315 
316     TIntermDeclaration *globalDecl = new TIntermDeclaration();
317     globalDecl->appendDeclarator(new TIntermSymbol(var));
318     root->insertStatement(0, globalDecl);
319 
320     return true;
321 }
322 
323 }  // anonymous namespace
324 
ValidateClipCullDistance(TCompiler * compiler,TIntermBlock * root,TDiagnostics * diagnostics,const unsigned int maxCombinedClipAndCullDistances,uint8_t * clipDistanceSizeOut,uint8_t * cullDistanceSizeOut,bool * clipDistanceUsedOut)325 bool ValidateClipCullDistance(TCompiler *compiler,
326                               TIntermBlock *root,
327                               TDiagnostics *diagnostics,
328                               const unsigned int maxCombinedClipAndCullDistances,
329                               uint8_t *clipDistanceSizeOut,
330                               uint8_t *cullDistanceSizeOut,
331                               bool *clipDistanceUsedOut)
332 {
333     ValidateClipCullDistanceTraverser varyingValidator;
334     root->traverse(&varyingValidator);
335     int numErrorsBefore = diagnostics->numErrors();
336     bool clipDistanceRedeclared;
337     bool cullDistanceRedeclared;
338     varyingValidator.validate(diagnostics, maxCombinedClipAndCullDistances, clipDistanceSizeOut,
339                               cullDistanceSizeOut, &clipDistanceRedeclared, &cullDistanceRedeclared,
340                               clipDistanceUsedOut);
341 
342     ValidateClipCullDistanceLengthTraverser lengthValidator(diagnostics, *clipDistanceSizeOut,
343                                                             *cullDistanceSizeOut);
344     root->traverse(&lengthValidator);
345     if (diagnostics->numErrors() != numErrorsBefore)
346     {
347         return false;
348     }
349 
350     // If the clip/cull distance variables are not explicitly redeclared in the incoming shader,
351     // redeclare them to ensure that various pruning passes will not cause inconsistent AST state.
352     if (*clipDistanceSizeOut > 0 && !clipDistanceRedeclared &&
353         !ReplaceAndDeclareVariable(compiler, root, ImmutableString("gl_ClipDistance"),
354                                    *clipDistanceSizeOut))
355     {
356 
357         return false;
358     }
359     if (*cullDistanceSizeOut > 0 && !cullDistanceRedeclared &&
360         !ReplaceAndDeclareVariable(compiler, root, ImmutableString("gl_CullDistance"),
361                                    *cullDistanceSizeOut))
362     {
363         return false;
364     }
365 
366     return true;
367 }
368 
369 }  // namespace sh
370