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