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