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