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 // ReplaceClipCullDistanceVariable.cpp: Find any references to gl_ClipDistance or gl_CullDistance
7 // and replace it with ANGLEClipDistance or ANGLECullDistance.
8 //
9 
10 #include "compiler/translator/tree_util/ReplaceClipCullDistanceVariable.h"
11 
12 #include "common/bitset_utils.h"
13 #include "common/debug.h"
14 #include "common/utilities.h"
15 #include "compiler/translator/SymbolTable.h"
16 #include "compiler/translator/tree_util/BuiltIn.h"
17 #include "compiler/translator/tree_util/IntermNode_util.h"
18 #include "compiler/translator/tree_util/IntermTraverse.h"
19 #include "compiler/translator/tree_util/RunAtTheBeginningOfShader.h"
20 #include "compiler/translator/tree_util/RunAtTheEndOfShader.h"
21 
22 namespace sh
23 {
24 namespace
25 {
26 
27 using ClipCullDistanceIdxSet = angle::BitSet<32>;
28 
29 typedef TIntermNode *AssignFunc(const unsigned int index,
30                                 TIntermSymbol *left,
31                                 TIntermSymbol *right,
32                                 const TIntermTyped *enableFlags);
33 
34 template <typename Variable>
FindVariable(const std::vector<Variable> & mVars,const ImmutableString & name)35 const Variable *FindVariable(const std::vector<Variable> &mVars, const ImmutableString &name)
36 {
37     for (const Variable &var : mVars)
38     {
39         if (name == var.instanceName)
40         {
41             return &var;
42         }
43     }
44 
45     return nullptr;
46 }
47 
48 // Traverse the tree and collect the redeclaration and all constant index references of
49 // gl_ClipDistance/gl_CullDistance
50 class GLClipCullDistanceReferenceTraverser : public TIntermTraverser
51 {
52   public:
GLClipCullDistanceReferenceTraverser(const TIntermSymbol ** redeclaredSymOut,bool * nonConstIdxUsedOut,unsigned int * maxConstIdxOut,ClipCullDistanceIdxSet * constIndicesOut,const ImmutableString & targetStr)53     GLClipCullDistanceReferenceTraverser(const TIntermSymbol **redeclaredSymOut,
54                                          bool *nonConstIdxUsedOut,
55                                          unsigned int *maxConstIdxOut,
56                                          ClipCullDistanceIdxSet *constIndicesOut,
57                                          const ImmutableString &targetStr)
58         : TIntermTraverser(true, false, false),
59           mRedeclaredSym(redeclaredSymOut),
60           mUseNonConstClipCullDistanceIndex(nonConstIdxUsedOut),
61           mMaxConstClipCullDistanceIndex(maxConstIdxOut),
62           mConstClipCullDistanceIndices(constIndicesOut),
63           mTargetStr(targetStr)
64     {
65         *mRedeclaredSym                    = nullptr;
66         *mUseNonConstClipCullDistanceIndex = false;
67         *mMaxConstClipCullDistanceIndex    = 0;
68         mConstClipCullDistanceIndices->reset();
69     }
70 
visitDeclaration(Visit visit,TIntermDeclaration * node)71     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
72     {
73         // If gl_ClipDistance/gl_CullDistance is redeclared, we need to collect its information
74         const TIntermSequence &sequence = *(node->getSequence());
75 
76         if (sequence.size() != 1)
77         {
78             return true;
79         }
80 
81         TIntermTyped *variable = sequence.front()->getAsTyped();
82         if (!variable->getAsSymbolNode() || variable->getAsSymbolNode()->getName() != mTargetStr)
83         {
84             return true;
85         }
86 
87         *mRedeclaredSym = variable->getAsSymbolNode();
88 
89         return true;
90     }
91 
visitBinary(Visit visit,TIntermBinary * node)92     bool visitBinary(Visit visit, TIntermBinary *node) override
93     {
94         TOperator op = node->getOp();
95         if (op != EOpIndexDirect && op != EOpIndexIndirect)
96         {
97             return true;
98         }
99 
100         // gl_ClipDistance / gl_CullDistance
101         TIntermTyped *left = node->getLeft()->getAsTyped();
102         if (!left)
103         {
104             return true;
105         }
106 
107         ASSERT(op == EOpIndexDirect || op == EOpIndexIndirect);
108 
109         TIntermSymbol *clipCullDistance = left->getAsSymbolNode();
110         if (!clipCullDistance)
111         {
112             return true;
113         }
114         if (clipCullDistance->getName() != mTargetStr)
115         {
116             return true;
117         }
118 
119         const TConstantUnion *constIdx = node->getRight()->getConstantValue();
120         if (!constIdx)
121         {
122             *mUseNonConstClipCullDistanceIndex = true;
123         }
124         else
125         {
126             unsigned 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                 case EbtFloat:
136                     idx = static_cast<unsigned int>(constIdx->getFConst());
137                     break;
138                 case EbtBool:
139                     idx = constIdx->getBConst() ? 1 : 0;
140                     break;
141                 default:
142                     UNREACHABLE();
143                     break;
144             }
145             ASSERT(idx < mConstClipCullDistanceIndices->size());
146             mConstClipCullDistanceIndices->set(idx);
147 
148             *mMaxConstClipCullDistanceIndex = std::max(*mMaxConstClipCullDistanceIndex, idx);
149         }
150 
151         return true;
152     }
153 
154   private:
155     const TIntermSymbol **mRedeclaredSym;
156     // Flag indicating whether there is at least one reference of gl_ClipDistance with non-constant
157     // index
158     bool *mUseNonConstClipCullDistanceIndex;
159     // Max constant index that is used to reference gl_ClipDistance
160     unsigned int *mMaxConstClipCullDistanceIndex;
161     // List of constant index reference of gl_ClipDistance
162     ClipCullDistanceIdxSet *mConstClipCullDistanceIndices;
163     // String for gl_ClipDistance/gl_CullDistance
164     const ImmutableString mTargetStr;
165 };
166 
167 // Replace all symbolic occurrences of given variables except one symbol.
168 class ReplaceVariableExceptOneTraverser : public TIntermTraverser
169 {
170   public:
ReplaceVariableExceptOneTraverser(const TVariable * toBeReplaced,const TIntermTyped * replacement,const TIntermSymbol * exception)171     ReplaceVariableExceptOneTraverser(const TVariable *toBeReplaced,
172                                       const TIntermTyped *replacement,
173                                       const TIntermSymbol *exception)
174         : TIntermTraverser(true, false, false),
175           mToBeReplaced(toBeReplaced),
176           mException(exception),
177           mReplacement(replacement)
178     {}
179 
visitSymbol(TIntermSymbol * node)180     void visitSymbol(TIntermSymbol *node) override
181     {
182         if (&node->variable() == mToBeReplaced && node != mException)
183         {
184             queueReplacement(mReplacement->deepCopy(), OriginalNode::IS_DROPPED);
185         }
186     }
187 
188   private:
189     const TVariable *const mToBeReplaced;
190     const TIntermSymbol *const mException;
191     const TIntermTyped *const mReplacement;
192 };
193 
simpleAssignFunc(const unsigned int index,TIntermSymbol * leftSymbol,TIntermSymbol * rightSymbol,const TIntermTyped *)194 TIntermNode *simpleAssignFunc(const unsigned int index,
195                               TIntermSymbol *leftSymbol,
196                               TIntermSymbol *rightSymbol,
197                               const TIntermTyped * /*enableFlags*/)
198 {
199     // leftSymbol[index] = rightSymbol[index]
200     // E.g., ANGLEClipDistance[index] = gl_ClipDistance[index]
201     TIntermBinary *left =
202         new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index));
203     TIntermBinary *right =
204         new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index));
205 
206     return new TIntermBinary(EOpAssign, left, right);
207 }
208 
209 // This is only used for gl_Clipdistance
assignFuncWithEnableFlags(const unsigned int index,TIntermSymbol * leftSymbol,TIntermSymbol * rightSymbol,const TIntermTyped * enableFlags)210 TIntermNode *assignFuncWithEnableFlags(const unsigned int index,
211                                        TIntermSymbol *leftSymbol,
212                                        TIntermSymbol *rightSymbol,
213                                        const TIntermTyped *enableFlags)
214 {
215     //  if (ANGLEUniforms.clipDistancesEnabled & (0x1 << index))
216     //      gl_ClipDistance[index] = ANGLEClipDistance[index];
217     //  else
218     //      gl_ClipDistance[index] = 0;
219     TIntermConstantUnion *bitMask = CreateUIntNode(0x1 << index);
220     TIntermBinary *bitwiseAnd = new TIntermBinary(EOpBitwiseAnd, enableFlags->deepCopy(), bitMask);
221     TIntermBinary *nonZero    = new TIntermBinary(EOpNotEqual, bitwiseAnd, CreateUIntNode(0));
222 
223     TIntermBinary *left =
224         new TIntermBinary(EOpIndexDirect, leftSymbol->deepCopy(), CreateIndexNode(index));
225     TIntermBinary *right =
226         new TIntermBinary(EOpIndexDirect, rightSymbol->deepCopy(), CreateIndexNode(index));
227     TIntermBinary *assignment = new TIntermBinary(EOpAssign, left, right);
228     TIntermBlock *trueBlock   = new TIntermBlock();
229     trueBlock->appendStatement(assignment);
230 
231     TIntermBinary *zeroAssignment =
232         new TIntermBinary(EOpAssign, left->deepCopy(), CreateFloatNode(0));
233     TIntermBlock *falseBlock = new TIntermBlock();
234     falseBlock->appendStatement(zeroAssignment);
235 
236     return new TIntermIfElse(nonZero, trueBlock, falseBlock);
237 }
238 
239 class ReplaceClipCullDistanceAssignments : angle::NonCopyable
240 {
241   public:
ReplaceClipCullDistanceAssignments(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const TVariable * glClipCullDistanceVar,const TIntermSymbol * redeclaredGlClipDistance,const ImmutableString & angleVarName)242     ReplaceClipCullDistanceAssignments(TCompiler *compiler,
243                                        TIntermBlock *root,
244                                        TSymbolTable *symbolTable,
245                                        const TVariable *glClipCullDistanceVar,
246                                        const TIntermSymbol *redeclaredGlClipDistance,
247                                        const ImmutableString &angleVarName)
248         : mCompiler(compiler),
249           mRoot(root),
250           mSymbolTable(symbolTable),
251           mGlVar(glClipCullDistanceVar),
252           mRedeclaredGLVar(redeclaredGlClipDistance),
253           mANGLEVarName(angleVarName)
254     {
255         mEnabledDistances = 0;
256     }
257 
258     unsigned int getEnabledClipCullDistance(const bool useNonConstIndex,
259                                             const unsigned int maxConstIndex);
260     const TVariable *declareANGLEVariable();
261     bool assignOriginalValueToANGLEVariable(const GLenum shaderType);
262     bool assignANGLEValueToOriginalVariable(const GLenum shaderType,
263                                             const bool isRedeclared,
264                                             const TIntermTyped *enableFlags,
265                                             const ClipCullDistanceIdxSet *constIndices,
266                                             AssignFunc assignFunc);
267 
268   private:
269     bool assignOriginalValueToANGLEVariableImpl();
270     bool assignANGLEValueToOriginalVariableImpl(const bool isRedeclared,
271                                                 const TIntermTyped *enableFlags,
272                                                 const ClipCullDistanceIdxSet *constIndices,
273                                                 AssignFunc assignFunc);
274 
275     // Common variables for replacing gl_Clip/CullDistances with ANGLEClip/CullDistances
276     TCompiler *mCompiler;
277     TIntermBlock *mRoot;
278     TSymbolTable *mSymbolTable;
279 
280     const TVariable *mGlVar;
281     const TIntermSymbol *mRedeclaredGLVar;
282     const ImmutableString mANGLEVarName;
283 
284     unsigned int mEnabledDistances;
285     const TVariable *mANGLEVar;
286 };
287 
getEnabledClipCullDistance(const bool useNonConstIndex,const unsigned int maxConstIndex)288 unsigned int ReplaceClipCullDistanceAssignments::getEnabledClipCullDistance(
289     const bool useNonConstIndex,
290     const unsigned int maxConstIndex)
291 {
292     if (mRedeclaredGLVar)
293     {
294         // If array is redeclared by user, use that redeclared size.
295         mEnabledDistances = mRedeclaredGLVar->getType().getOutermostArraySize();
296     }
297     else if (!useNonConstIndex)
298     {
299         ASSERT(maxConstIndex < mGlVar->getType().getOutermostArraySize());
300         // Only use constant index, then use max array index used.
301         mEnabledDistances = maxConstIndex + 1;
302     }
303 
304     return mEnabledDistances;
305 }
306 
declareANGLEVariable()307 const TVariable *ReplaceClipCullDistanceAssignments::declareANGLEVariable()
308 {
309     ASSERT(mEnabledDistances > 0);
310 
311     TType *clipCullDistanceType = new TType(EbtFloat, EbpMedium, EvqGlobal, 1);
312     clipCullDistanceType->makeArray(mEnabledDistances);
313 
314     mANGLEVar =
315         new TVariable(mSymbolTable, mANGLEVarName, clipCullDistanceType, SymbolType::AngleInternal);
316 
317     TIntermSymbol *clipCullDistanceDeclarator = new TIntermSymbol(mANGLEVar);
318     TIntermDeclaration *clipCullDistanceDecl  = new TIntermDeclaration;
319     clipCullDistanceDecl->appendDeclarator(clipCullDistanceDeclarator);
320 
321     // Must declare ANGLEClipdistance/ANGLECullDistance before any function, since
322     // gl_ClipDistance/gl_CullDistance might be accessed within a function declared before main.
323     mRoot->insertStatement(0, clipCullDistanceDecl);
324 
325     return mANGLEVar;
326 }
327 
assignOriginalValueToANGLEVariableImpl()328 bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariableImpl()
329 {
330     ASSERT(mEnabledDistances > 0);
331 
332     TIntermBlock *readBlock                 = new TIntermBlock;
333     TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar);
334     TIntermSymbol *clipCullDistanceSymbol   = new TIntermSymbol(mANGLEVar);
335 
336     for (unsigned int i = 0; i < mEnabledDistances; i++)
337     {
338         readBlock->appendStatement(
339             simpleAssignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, nullptr));
340     }
341 
342     return RunAtTheBeginningOfShader(mCompiler, mRoot, readBlock);
343 }
344 
assignANGLEValueToOriginalVariableImpl(const bool isRedeclared,const TIntermTyped * enableFlags,const ClipCullDistanceIdxSet * constIndices,AssignFunc assignFunc)345 bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariableImpl(
346     const bool isRedeclared,
347     const TIntermTyped *enableFlags,
348     const ClipCullDistanceIdxSet *constIndices,
349     AssignFunc assignFunc)
350 {
351     ASSERT(mEnabledDistances > 0);
352 
353     TIntermBlock *assignBlock               = new TIntermBlock;
354     TIntermSymbol *glClipCullDistanceSymbol = new TIntermSymbol(mGlVar);
355     TIntermSymbol *clipCullDistanceSymbol   = new TIntermSymbol(mANGLEVar);
356 
357     // The array size is decided by either redeclaring the variable or accessing the variable with a
358     // integral constant index. And this size is the count of the enabled value. So, if the index
359     // which is greater than the array size, is used to access the variable, this access will be
360     // ignored.
361     if (isRedeclared || !constIndices)
362     {
363         for (unsigned int i = 0; i < mEnabledDistances; ++i)
364         {
365             assignBlock->appendStatement(
366                 assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags));
367         }
368     }
369     else
370     {
371         // Assign ANGLEClip/CullDistance[i]'s value to gl_Clip/CullDistance[i] if i is in the
372         // constant indices list. Those elements whose index is not in the constant index list will
373         // be zeroise for initialization.
374         for (unsigned int i = 0; i < mEnabledDistances; ++i)
375         {
376             if (constIndices->test(i))
377             {
378                 assignBlock->appendStatement(
379                     assignFunc(i, glClipCullDistanceSymbol, clipCullDistanceSymbol, enableFlags));
380             }
381             else
382             {
383                 // gl_Clip/CullDistance[i] = 0;
384                 TIntermBinary *left = new TIntermBinary(
385                     EOpIndexDirect, glClipCullDistanceSymbol->deepCopy(), CreateIndexNode(i));
386                 TIntermBinary *zeroAssignment =
387                     new TIntermBinary(EOpAssign, left, CreateFloatNode(0));
388                 assignBlock->appendStatement(zeroAssignment);
389             }
390         }
391     }
392 
393     return RunAtTheEndOfShader(mCompiler, mRoot, assignBlock, mSymbolTable);
394 }
395 
assignOriginalValueToANGLEVariable(const GLenum shaderType)396 ANGLE_NO_DISCARD bool ReplaceClipCullDistanceAssignments::assignOriginalValueToANGLEVariable(
397     const GLenum shaderType)
398 {
399     switch (shaderType)
400     {
401         case GL_VERTEX_SHADER:
402             // Vertex shader can use gl_Clip/CullDistance as a output only
403             break;
404         case GL_FRAGMENT_SHADER:
405         {
406             // These shader types can use gl_Clip/CullDistance as input
407             if (!assignOriginalValueToANGLEVariableImpl())
408             {
409                 return false;
410             }
411             break;
412         }
413         default:
414         {
415             UNREACHABLE();
416             return false;
417         }
418     }
419 
420     return true;
421 }
422 
assignANGLEValueToOriginalVariable(const GLenum shaderType,const bool isRedeclared,const TIntermTyped * enableFlags,const ClipCullDistanceIdxSet * constIndices,AssignFunc assignFunc)423 ANGLE_NO_DISCARD bool ReplaceClipCullDistanceAssignments::assignANGLEValueToOriginalVariable(
424     const GLenum shaderType,
425     const bool isRedeclared,
426     const TIntermTyped *enableFlags,
427     const ClipCullDistanceIdxSet *constIndices,
428     AssignFunc assignFunc)
429 {
430     switch (shaderType)
431     {
432         case GL_VERTEX_SHADER:
433         {
434             // Vertex shader can use gl_Clip/CullDistance as output.
435             // If the enabled gl_Clip/CullDistances are not initialized, results are undefined.
436             // EXT_clip_cull_distance spec :
437             // The shader must also set all values in gl_ClipDistance that have been enabled via the
438             // OpenGL ES API, or results are undefined. Values written into gl_ClipDistance for
439             // planes that are not enabled have no effect.
440             // ...
441             // Shaders writing gl_CullDistance must write all enabled distances, or culling results
442             // are undefined.
443             if (!assignANGLEValueToOriginalVariableImpl(isRedeclared, enableFlags, constIndices,
444                                                         assignFuncWithEnableFlags))
445             {
446                 return false;
447             }
448             break;
449         }
450         case GL_FRAGMENT_SHADER:
451             // Fragment shader can use gl_Clip/CullDistance as input only
452             break;
453         default:
454         {
455             UNREACHABLE();
456             return false;
457         }
458     }
459 
460     return true;
461 }
462 
463 }  // anonymous namespace
464 
ReplaceClipDistanceAssignments(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const GLenum shaderType,const TIntermTyped * clipDistanceEnableFlags)465 ANGLE_NO_DISCARD bool ReplaceClipDistanceAssignments(TCompiler *compiler,
466                                                      TIntermBlock *root,
467                                                      TSymbolTable *symbolTable,
468                                                      const GLenum shaderType,
469                                                      const TIntermTyped *clipDistanceEnableFlags)
470 {
471     // Collect all constant index references of gl_ClipDistance
472     ImmutableString glClipDistanceName("gl_ClipDistance");
473     ClipCullDistanceIdxSet constIndices;
474     bool useNonConstIndex                         = false;
475     const TIntermSymbol *redeclaredGlClipDistance = nullptr;
476     unsigned int maxConstIndex                    = 0;
477     GLClipCullDistanceReferenceTraverser indexTraverser(&redeclaredGlClipDistance,
478                                                         &useNonConstIndex, &maxConstIndex,
479                                                         &constIndices, glClipDistanceName);
480     root->traverse(&indexTraverser);
481     if (!useNonConstIndex && constIndices.none())
482     {
483         // No references of gl_ClipDistance
484         return true;
485     }
486 
487     // Retrieve gl_ClipDistance variable reference
488     // Search user redeclared gl_ClipDistance first
489     const TVariable *glClipDistanceVar = nullptr;
490     if (redeclaredGlClipDistance)
491     {
492         glClipDistanceVar = &redeclaredGlClipDistance->variable();
493     }
494     else
495     {
496         // User defined not found, find in built-in table
497         glClipDistanceVar =
498             static_cast<const TVariable *>(symbolTable->findBuiltIn(glClipDistanceName, 300));
499     }
500     if (!glClipDistanceVar)
501     {
502         return false;
503     }
504 
505     ReplaceClipCullDistanceAssignments replacementUtils(compiler, root, symbolTable,
506                                                         glClipDistanceVar, redeclaredGlClipDistance,
507                                                         ImmutableString("ANGLEClipDistance"));
508 
509     // Declare a global variable substituting gl_ClipDistance
510     unsigned int enabledClipDistances =
511         replacementUtils.getEnabledClipCullDistance(useNonConstIndex, maxConstIndex);
512     if (!enabledClipDistances)
513     {
514         // Spec :
515         // The gl_ClipDistance array is predeclared as unsized and must be explicitly sized by the
516         // shader either redeclaring it with a size or implicitly sized by indexing it only with
517         // integral constant expressions.
518         return false;
519     }
520 
521     const TVariable *clipDistanceVar = replacementUtils.declareANGLEVariable();
522 
523     // Replace gl_ClipDistance reference with ANGLEClipDistance, except the declaration
524     ReplaceVariableExceptOneTraverser replaceTraverser(glClipDistanceVar,
525                                                        new TIntermSymbol(clipDistanceVar),
526                                                        /** exception */ redeclaredGlClipDistance);
527     root->traverse(&replaceTraverser);
528     if (!replaceTraverser.updateTree(compiler, root))
529     {
530         return false;
531     }
532 
533     // Read gl_ClipDistance to ANGLEClipDistance for getting a original data
534     if (!replacementUtils.assignOriginalValueToANGLEVariable(shaderType))
535     {
536         return false;
537     }
538 
539     // Reassign ANGLEClipDistance to gl_ClipDistance but ignore those that are disabled
540     const bool isRedeclared = (redeclaredGlClipDistance != nullptr);
541     if (!replacementUtils.assignANGLEValueToOriginalVariable(shaderType, isRedeclared,
542                                                              clipDistanceEnableFlags, &constIndices,
543                                                              assignFuncWithEnableFlags))
544     {
545         return false;
546     }
547 
548     return true;
549 }
550 
ReplaceCullDistanceAssignments(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const GLenum shaderType)551 ANGLE_NO_DISCARD bool ReplaceCullDistanceAssignments(TCompiler *compiler,
552                                                      TIntermBlock *root,
553                                                      TSymbolTable *symbolTable,
554                                                      const GLenum shaderType)
555 {
556     // Collect all constant index references of gl_CullDistance
557     ImmutableString glCullDistanceName("gl_CullDistance");
558     ClipCullDistanceIdxSet constIndices;
559     bool useNonConstIndex                         = false;
560     const TIntermSymbol *redeclaredGLCullDistance = nullptr;
561     unsigned int maxConstIndex                    = 0;
562     GLClipCullDistanceReferenceTraverser indexTraverser(&redeclaredGLCullDistance,
563                                                         &useNonConstIndex, &maxConstIndex,
564                                                         &constIndices, glCullDistanceName);
565     root->traverse(&indexTraverser);
566     if (!useNonConstIndex)
567     {
568         // Nothing to do
569         return true;
570     }
571 
572     // Retrieve gl_CullDistance variable reference
573     // Search user redeclared gl_CullDistance first
574     const TVariable *glCullDistanceVar = nullptr;
575     if (redeclaredGLCullDistance)
576     {
577         glCullDistanceVar = &redeclaredGLCullDistance->variable();
578     }
579     else
580     {
581         // User defined not found, find in built-in table
582         glCullDistanceVar =
583             static_cast<const TVariable *>(symbolTable->findBuiltIn(glCullDistanceName, 300));
584     }
585     if (!glCullDistanceVar)
586     {
587         return false;
588     }
589 
590     ReplaceClipCullDistanceAssignments replacementUtils(compiler, root, symbolTable,
591                                                         glCullDistanceVar, redeclaredGLCullDistance,
592                                                         ImmutableString("ANGLECullDistance"));
593 
594     // Declare a global variable substituting gl_CullDistance
595     unsigned int enabledCullDistances =
596         replacementUtils.getEnabledClipCullDistance(useNonConstIndex, maxConstIndex);
597     if (!enabledCullDistances)
598     {
599         // Spec :
600         // The gl_CullDistance array is predeclared as unsized and must be sized by the shader
601         // either redeclaring it with a size or indexing it only with integral constant expressions.
602         return false;
603     }
604 
605     const TVariable *cullDistanceVar = replacementUtils.declareANGLEVariable();
606 
607     // Replace gl_CullDistance reference with ANGLECullDistance, except the declaration
608     ReplaceVariableExceptOneTraverser replaceTraverser(glCullDistanceVar,
609                                                        new TIntermSymbol(cullDistanceVar),
610                                                        /** exception */ redeclaredGLCullDistance);
611     root->traverse(&replaceTraverser);
612     if (!replaceTraverser.updateTree(compiler, root))
613     {
614         return false;
615     }
616 
617     // Read gl_CullDistance to ANGLECullDistance for getting a original data
618     if (!replacementUtils.assignOriginalValueToANGLEVariable(shaderType))
619     {
620         return false;
621     }
622 
623     // Reassign ANGLECullDistance to gl_CullDistance but ignore those that are disabled
624     const bool isRedeclared = (redeclaredGLCullDistance != nullptr);
625     if (!replacementUtils.assignANGLEValueToOriginalVariable(shaderType, isRedeclared, nullptr,
626                                                              &constIndices, simpleAssignFunc))
627     {
628         return false;
629     }
630 
631     return true;
632 }
633 
634 }  // namespace sh
635