• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // ReplaceClipDistanceVariable.cpp: Find any references to gl_ClipDistance and
7 // replace it with ANGLEClipDistance.
8 //
9 
10 #include "compiler/translator/tree_util/ReplaceClipDistanceVariable.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/RunAtTheEndOfShader.h"
20 
21 namespace sh
22 {
23 namespace
24 {
25 
26 using ClipDistanceIdxSet = angle::BitSet<32>;
27 
28 // Traverse the tree and collect the redeclaration and all constant index references of
29 // gl_ClipDistance
30 class GLClipDistanceReferenceTraverser : public TIntermTraverser
31 {
32   public:
GLClipDistanceReferenceTraverser(const TIntermSymbol ** redeclaredSymOut,bool * nonConstIdxUsedOut,unsigned int * maxConstIdxOut,ClipDistanceIdxSet * constIndicesOut)33     GLClipDistanceReferenceTraverser(const TIntermSymbol **redeclaredSymOut,
34                                      bool *nonConstIdxUsedOut,
35                                      unsigned int *maxConstIdxOut,
36                                      ClipDistanceIdxSet *constIndicesOut)
37         : TIntermTraverser(true, false, false),
38           mRedeclaredSym(redeclaredSymOut),
39           mUseNonConstClipDistanceIndex(nonConstIdxUsedOut),
40           mMaxConstClipDistanceIndex(maxConstIdxOut),
41           mConstClipDistanceIndices(constIndicesOut)
42     {
43         *mRedeclaredSym                = nullptr;
44         *mUseNonConstClipDistanceIndex = false;
45         *mMaxConstClipDistanceIndex    = 0;
46         mConstClipDistanceIndices->reset();
47     }
48 
visitDeclaration(Visit visit,TIntermDeclaration * node)49     bool visitDeclaration(Visit visit, TIntermDeclaration *node) override
50     {
51         // If gl_ClipDistance is redeclared, we need to collect its information
52         const TIntermSequence &sequence = *(node->getSequence());
53 
54         if (sequence.size() != 1)
55         {
56             return true;
57         }
58 
59         TIntermTyped *variable = sequence.front()->getAsTyped();
60         if (!variable->getAsSymbolNode() ||
61             variable->getAsSymbolNode()->getName() != "gl_ClipDistance")
62         {
63             return true;
64         }
65 
66         *mRedeclaredSym = variable->getAsSymbolNode();
67 
68         return true;
69     }
70 
visitBinary(Visit visit,TIntermBinary * node)71     bool visitBinary(Visit visit, TIntermBinary *node) override
72     {
73         TOperator op = node->getOp();
74         if (op != EOpIndexDirect && op != EOpIndexIndirect)
75         {
76             return true;
77         }
78         TIntermSymbol *left = node->getLeft()->getAsSymbolNode();
79         if (!left)
80         {
81             return true;
82         }
83         if (left->getName() != "gl_ClipDistance")
84         {
85             return true;
86         }
87         const TConstantUnion *constIdx = node->getRight()->getConstantValue();
88         if (!constIdx)
89         {
90             *mUseNonConstClipDistanceIndex = true;
91         }
92         else
93         {
94             unsigned int idx = 0;
95             switch (constIdx->getType())
96             {
97                 case EbtInt:
98                     idx = constIdx->getIConst();
99                     break;
100                 case EbtUInt:
101                     idx = constIdx->getUConst();
102                     break;
103                 case EbtFloat:
104                     idx = static_cast<unsigned int>(constIdx->getFConst());
105                     break;
106                 case EbtBool:
107                     idx = constIdx->getBConst() ? 1 : 0;
108                     break;
109                 default:
110                     UNREACHABLE();
111                     break;
112             }
113             ASSERT(idx < mConstClipDistanceIndices->size());
114             mConstClipDistanceIndices->set(idx);
115 
116             *mMaxConstClipDistanceIndex = std::max(*mMaxConstClipDistanceIndex, idx);
117         }
118 
119         return true;
120     }
121 
122   private:
123     const TIntermSymbol **mRedeclaredSym;
124     // Flag indicating whether there is at least one reference of gl_ClipDistance with non-constant
125     // index
126     bool *mUseNonConstClipDistanceIndex;
127     // Max constant index that is used to reference gl_ClipDistance
128     unsigned int *mMaxConstClipDistanceIndex;
129     // List of constant index reference of gl_ClipDistance
130     ClipDistanceIdxSet *mConstClipDistanceIndices;
131 };
132 
133 // Replace all symbolic occurrences of given variables except one symbol.
134 class ReplaceVariableExceptOneTraverser : public TIntermTraverser
135 {
136   public:
ReplaceVariableExceptOneTraverser(const TVariable * toBeReplaced,const TIntermTyped * replacement,const TIntermSymbol * exception)137     ReplaceVariableExceptOneTraverser(const TVariable *toBeReplaced,
138                                       const TIntermTyped *replacement,
139                                       const TIntermSymbol *exception)
140         : TIntermTraverser(true, false, false),
141           mToBeReplaced(toBeReplaced),
142           mException(exception),
143           mReplacement(replacement)
144     {}
145 
visitSymbol(TIntermSymbol * node)146     void visitSymbol(TIntermSymbol *node) override
147     {
148         if (&node->variable() == mToBeReplaced && node != mException)
149         {
150             queueReplacement(mReplacement->deepCopy(), OriginalNode::IS_DROPPED);
151         }
152     }
153 
154   private:
155     const TVariable *const mToBeReplaced;
156     const TIntermSymbol *const mException;
157     const TIntermTyped *const mReplacement;
158 };
159 
160 }  // anonymous namespace
161 
ReplaceClipDistanceAssignments(TCompiler * compiler,TIntermBlock * root,TSymbolTable * symbolTable,const TIntermTyped * clipDistanceEnableFlags)162 ANGLE_NO_DISCARD bool ReplaceClipDistanceAssignments(TCompiler *compiler,
163                                                      TIntermBlock *root,
164                                                      TSymbolTable *symbolTable,
165                                                      const TIntermTyped *clipDistanceEnableFlags)
166 {
167     // Collect all constant index references of gl_ClipDistance
168     ClipDistanceIdxSet constIndices;
169     bool useNonConstIndex                         = false;
170     const TIntermSymbol *redeclaredGLClipDistance = nullptr;
171     unsigned int maxConstIndex                    = 0;
172     GLClipDistanceReferenceTraverser indexTraverser(&redeclaredGLClipDistance, &useNonConstIndex,
173                                                     &maxConstIndex, &constIndices);
174     root->traverse(&indexTraverser);
175     if (!useNonConstIndex && constIndices.none())
176     {
177         // No references of gl_ClipDistance
178         return true;
179     }
180 
181     // Retrieve gl_ClipDistance variable reference
182     // Search user redeclared gl_ClipDistance first
183     const TVariable *glClipDistanceVar = nullptr;
184     if (redeclaredGLClipDistance)
185     {
186         glClipDistanceVar = &redeclaredGLClipDistance->variable();
187     }
188     else
189     {
190         ImmutableString glClipDistanceName("gl_ClipDistance");
191         // User defined not found, find in built-in table
192         glClipDistanceVar =
193             static_cast<const TVariable *>(symbolTable->findBuiltIn(glClipDistanceName, 0));
194     }
195     if (!glClipDistanceVar)
196     {
197         return false;
198     }
199 
200     // Declare a global variable substituting gl_ClipDistance
201     TType *clipDistanceType = new TType(EbtFloat, EbpMedium, EvqGlobal, 1);
202     if (redeclaredGLClipDistance)
203     {
204         // If array is redeclared by user, use that redeclared size.
205         clipDistanceType->makeArray(redeclaredGLClipDistance->getType().getOutermostArraySize());
206     }
207     else if (!useNonConstIndex)
208     {
209         ASSERT(maxConstIndex < glClipDistanceVar->getType().getOutermostArraySize());
210         // Only use constant index, then use max array index used.
211         clipDistanceType->makeArray(maxConstIndex + 1);
212     }
213     else
214     {
215         clipDistanceType->makeArray(glClipDistanceVar->getType().getOutermostArraySize());
216     }
217 
218     clipDistanceType->realize();
219     TVariable *clipDistanceVar = new TVariable(symbolTable, ImmutableString("ANGLEClipDistance"),
220                                                clipDistanceType, SymbolType::AngleInternal);
221 
222     TIntermSymbol *clipDistanceDeclarator = new TIntermSymbol(clipDistanceVar);
223     TIntermDeclaration *clipDistanceDecl  = new TIntermDeclaration;
224     clipDistanceDecl->appendDeclarator(clipDistanceDeclarator);
225 
226     // Must declare ANGLEClipDistance before any function, since gl_ClipDistance might be accessed
227     // within a function declared before main.
228     root->insertStatement(0, clipDistanceDecl);
229 
230     // Replace gl_ClipDistance reference with ANGLEClipDistance, except the declaration
231     ReplaceVariableExceptOneTraverser replaceTraverser(glClipDistanceVar,
232                                                        new TIntermSymbol(clipDistanceVar),
233                                                        /** exception */ redeclaredGLClipDistance);
234     root->traverse(&replaceTraverser);
235     if (!replaceTraverser.updateTree(compiler, root))
236     {
237         return false;
238     }
239 
240     TIntermBlock *reassignBlock         = new TIntermBlock;
241     TIntermSymbol *glClipDistanceSymbol = new TIntermSymbol(glClipDistanceVar);
242     TIntermSymbol *clipDistanceSymbol   = new TIntermSymbol(clipDistanceVar);
243 
244     // Reassign ANGLEClipDistance to gl_ClipDistance but ignore those that are disabled
245 
246     auto assignFunc = [=](unsigned int index) {
247         //  if (ANGLEUniforms.clipDistancesEnabled & (0x1 << index))
248         //      gl_ClipDistance[index] = ANGLEClipDistance[index];
249         //  else
250         //      gl_ClipDistance[index] = 0;
251         TIntermConstantUnion *bitMask = CreateUIntNode(0x1 << index);
252         TIntermBinary *bitwiseAnd =
253             new TIntermBinary(EOpBitwiseAnd, clipDistanceEnableFlags->deepCopy(), bitMask);
254         TIntermBinary *nonZero = new TIntermBinary(EOpNotEqual, bitwiseAnd, CreateUIntNode(0));
255 
256         TIntermBinary *left  = new TIntermBinary(EOpIndexDirect, glClipDistanceSymbol->deepCopy(),
257                                                 CreateIndexNode(index));
258         TIntermBinary *right = new TIntermBinary(EOpIndexDirect, clipDistanceSymbol->deepCopy(),
259                                                  CreateIndexNode(index));
260         TIntermBinary *assignment = new TIntermBinary(EOpAssign, left, right);
261         TIntermBlock *trueBlock   = new TIntermBlock();
262         trueBlock->appendStatement(assignment);
263 
264         TIntermBinary *zeroAssignment =
265             new TIntermBinary(EOpAssign, left->deepCopy(), CreateFloatNode(0));
266         TIntermBlock *falseBlock = new TIntermBlock();
267         falseBlock->appendStatement(zeroAssignment);
268 
269         return new TIntermIfElse(nonZero, trueBlock, falseBlock);
270     };
271 
272     if (useNonConstIndex)
273     {
274         // If there is at least one non constant index reference,
275         // Then we need to loop through the whole declared size of gl_ClipDistance.
276         // Since we don't know exactly the index at compile time.
277         // As mentioned in
278         // https://www.khronos.org/registry/OpenGL/extensions/APPLE/APPLE_clip_distance.txt
279         // Non constant index can only be used if gl_ClipDistance is redeclared with an explicit
280         // size.
281         for (unsigned int i = 0; i < clipDistanceType->getOutermostArraySize(); ++i)
282         {
283             reassignBlock->appendStatement(assignFunc(i));
284         }
285     }
286     else
287     {
288         // Assign ANGLEClipDistance[i]'s value to gl_ClipDistance[i] if i is in the constant
289         // indices list.
290         // Those elements whose index is not in the constant index list will be zeroise.
291         for (unsigned int i = 0; i < clipDistanceType->getOutermostArraySize(); ++i)
292         {
293             if (constIndices.test(i))
294             {
295                 reassignBlock->appendStatement(assignFunc(i));
296             }
297             else
298             {
299                 // gl_ClipDistance[i] = 0;
300                 TIntermBinary *left = new TIntermBinary(
301                     EOpIndexDirect, glClipDistanceSymbol->deepCopy(), CreateIndexNode(i));
302                 TIntermBinary *zeroAssignment =
303                     new TIntermBinary(EOpAssign, left, CreateFloatNode(0));
304                 reassignBlock->appendStatement(zeroAssignment);
305             }
306         }
307     }
308 
309     return RunAtTheEndOfShader(compiler, root, reassignBlock, symbolTable);
310 }
311 
312 }  // namespace sh
313