blob: 55bb412d0a02bdd0daa3f35c5373bdcc60ff59ca [file] [log] [blame]
Corentin Wallezf4eab3b2015-03-18 12:55:45 -07001//
2// Copyright (c) 2002-2015 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
7// Analysis of the AST needed for HLSL generation
8
9#include "compiler/translator/ASTMetadataHLSL.h"
10
11#include "compiler/translator/CallDAG.h"
12#include "compiler/translator/SymbolTable.h"
13
14namespace
15{
16
17// Class used to traverse the AST of a function definition, checking if the
18// function uses a gradient, and writing the set of control flow using gradients.
19// It assumes that the analysis has already been made for the function's
20// callees.
21class PullGradient : public TIntermTraverser
22{
23 public:
24 PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)
25 : TIntermTraverser(true, false, true),
26 mMetadataList(metadataList),
27 mMetadata(&(*metadataList)[index]),
28 mIndex(index),
29 mDag(dag)
30 {
31 ASSERT(index < metadataList->size());
32 }
33
34 void traverse(TIntermAggregate *node)
35 {
36 node->traverse(this);
37 ASSERT(mParents.empty());
38 }
39
40 // Called when a gradient operation or a call to a function using a gradient is found.
41 void onGradient()
42 {
43 mMetadata->mUsesGradient = true;
44 // Mark the latest control flow as using a gradient.
45 if (!mParents.empty())
46 {
47 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
48 }
49 }
50
51 void visitControlFlow(Visit visit, TIntermNode *node)
52 {
53 if (visit == PreVisit)
54 {
55 mParents.push_back(node);
56 }
57 else if (visit == PostVisit)
58 {
59 ASSERT(mParents.back() == node);
60 mParents.pop_back();
61 // A control flow's using a gradient means its parents are too.
62 if (mMetadata->mControlFlowsContainingGradient.count(node)> 0 && !mParents.empty())
63 {
64 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
65 }
66 }
67 }
68
69 bool visitLoop(Visit visit, TIntermLoop *loop)
70 {
71 visitControlFlow(visit, loop);
72 return true;
73 }
74
75 bool visitSelection(Visit visit, TIntermSelection *selection)
76 {
77 visitControlFlow(visit, selection);
78 return true;
79 }
80
81 bool visitUnary(Visit visit, TIntermUnary *node) override
82 {
83 if (visit == PreVisit)
84 {
85 switch (node->getOp())
86 {
87 case EOpDFdx:
88 case EOpDFdy:
89 onGradient();
90 default:
91 break;
92 }
93 }
94
95 return true;
96 }
97
98 bool visitAggregate(Visit visit, TIntermAggregate *node) override
99 {
100 if (visit == PreVisit)
101 {
102 if (node->getOp() == EOpFunctionCall)
103 {
104 if (node->isUserDefined())
105 {
106 size_t calleeIndex = mDag.findIndex(node);
107 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Nico Weber44897142015-07-10 09:50:00 -0700108 UNUSED_ASSERTION_VARIABLE(mIndex);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700109
110 if ((*mMetadataList)[calleeIndex].mUsesGradient) {
111 onGradient();
112 }
113 }
114 else
115 {
116 TString name = TFunction::unmangleName(node->getName());
117
118 if (name == "texture2D" ||
119 name == "texture2DProj" ||
120 name == "textureCube")
121 {
122 onGradient();
123 }
124 }
125 }
126 }
127
128 return true;
129 }
130
131 private:
132 MetadataList *mMetadataList;
133 ASTMetadataHLSL *mMetadata;
134 size_t mIndex;
135 const CallDAG &mDag;
136
137 // Contains a stack of the control flow nodes that are parents of the node being
138 // currently visited. It is used to mark control flows using a gradient.
139 std::vector<TIntermNode*> mParents;
140};
141
Corentin Wallez50931452015-03-19 10:39:13 -0700142// Traverses the AST of a function definition, assuming it has already been used to
143// traverse the callees of that function; computes the discontinuous loops and the if
144// statements that contain a discontinuous loop in their call graph.
145class PullComputeDiscontinuousLoops : public TIntermTraverser
146{
147 public:
148 PullComputeDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
149 : TIntermTraverser(true, false, true),
150 mMetadataList(metadataList),
151 mMetadata(&(*metadataList)[index]),
152 mIndex(index),
153 mDag(dag)
154 {
155 }
156
157 void traverse(TIntermAggregate *node)
158 {
159 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300160 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700161 ASSERT(mIfs.empty());
162 }
163
164 // Called when a discontinuous loop or a call to a function with a discontinuous loop
165 // in its call graph is found.
166 void onDiscontinuousLoop()
167 {
168 mMetadata->mHasDiscontinuousLoopInCallGraph = true;
169 // Mark the latest if as using a discontinuous loop.
170 if (!mIfs.empty())
171 {
172 mMetadata->mIfsContainingDiscontinuousLoop.insert(mIfs.back());
173 }
174 }
175
Olli Etuaho3873cd02015-04-10 15:00:55 +0300176 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700177 {
178 if (visit == PreVisit)
179 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300180 mLoopsAndSwitches.push_back(loop);
Corentin Wallez50931452015-03-19 10:39:13 -0700181 }
182 else if (visit == PostVisit)
183 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300184 ASSERT(mLoopsAndSwitches.back() == loop);
185 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700186 }
187
188 return true;
189 }
190
Olli Etuaho3873cd02015-04-10 15:00:55 +0300191 bool visitSelection(Visit visit, TIntermSelection *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700192 {
193 if (visit == PreVisit)
194 {
195 mIfs.push_back(node);
196 }
197 else if (visit == PostVisit)
198 {
199 ASSERT(mIfs.back() == node);
200 mIfs.pop_back();
201 // An if using a discontinuous loop means its parents ifs are also discontinuous.
202 if (mMetadata->mIfsContainingDiscontinuousLoop.count(node) > 0 && !mIfs.empty())
203 {
204 mMetadata->mIfsContainingDiscontinuousLoop.insert(mIfs.back());
205 }
206 }
207
208 return true;
209 }
210
Olli Etuaho3873cd02015-04-10 15:00:55 +0300211 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700212 {
213 if (visit == PreVisit)
214 {
215 switch (node->getFlowOp())
216 {
Corentin Wallez50931452015-03-19 10:39:13 -0700217 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300218 {
219 ASSERT(!mLoopsAndSwitches.empty());
220 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
221 if (loop != nullptr)
222 {
223 mMetadata->mDiscontinuousLoops.insert(loop);
224 onDiscontinuousLoop();
225 }
226 }
227 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700228 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300229 {
230 ASSERT(!mLoopsAndSwitches.empty());
231 TIntermLoop *loop = nullptr;
232 size_t i = mLoopsAndSwitches.size();
233 while (loop == nullptr && i > 0)
234 {
235 --i;
236 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
237 }
238 ASSERT(loop != nullptr);
239 mMetadata->mDiscontinuousLoops.insert(loop);
240 onDiscontinuousLoop();
241 }
Corentin Wallez50931452015-03-19 10:39:13 -0700242 break;
Corentin Walleza1884f22015-04-29 10:15:16 -0700243 case EOpKill:
Corentin Wallez50931452015-03-19 10:39:13 -0700244 case EOpReturn:
Corentin Walleza1884f22015-04-29 10:15:16 -0700245 // A return or discard jumps out of all the enclosing loops
Olli Etuaho3873cd02015-04-10 15:00:55 +0300246 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700247 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300248 for (TIntermNode* node : mLoopsAndSwitches)
Corentin Wallez50931452015-03-19 10:39:13 -0700249 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300250 TIntermLoop *loop = node->getAsLoopNode();
251 if (loop)
252 {
253 mMetadata->mDiscontinuousLoops.insert(loop);
254 }
Corentin Wallez50931452015-03-19 10:39:13 -0700255 }
256 onDiscontinuousLoop();
257 }
258 break;
259 default:
260 UNREACHABLE();
261 }
262 }
263
264 return true;
265 }
266
267 bool visitAggregate(Visit visit, TIntermAggregate *node) override
268 {
269 if (visit == PreVisit && node->getOp() == EOpFunctionCall)
270 {
271 if (node->isUserDefined())
272 {
273 size_t calleeIndex = mDag.findIndex(node);
274 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Nico Weber44897142015-07-10 09:50:00 -0700275 UNUSED_ASSERTION_VARIABLE(mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700276
277 if ((*mMetadataList)[calleeIndex].mHasDiscontinuousLoopInCallGraph)
278 {
279 onDiscontinuousLoop();
280 }
281 }
282 }
283
284 return true;
285 }
286
Olli Etuaho3873cd02015-04-10 15:00:55 +0300287 bool visitSwitch(Visit visit, TIntermSwitch *node) override
288 {
289 if (visit == PreVisit)
290 {
291 mLoopsAndSwitches.push_back(node);
292 }
293 else if (visit == PostVisit)
294 {
295 ASSERT(mLoopsAndSwitches.back() == node);
296 mLoopsAndSwitches.pop_back();
297 }
298 return true;
299 }
300
Corentin Wallez50931452015-03-19 10:39:13 -0700301 private:
302 MetadataList *mMetadataList;
303 ASTMetadataHLSL *mMetadata;
304 size_t mIndex;
305 const CallDAG &mDag;
306
Olli Etuaho3873cd02015-04-10 15:00:55 +0300307 std::vector<TIntermNode*> mLoopsAndSwitches;
Corentin Wallez50931452015-03-19 10:39:13 -0700308 std::vector<TIntermSelection*> mIfs;
309};
310
311// Tags all the functions called in a discontinuous loop
312class PushDiscontinuousLoops : public TIntermTraverser
313{
314 public:
315 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
316 : TIntermTraverser(true, true, true),
317 mMetadataList(metadataList),
318 mMetadata(&(*metadataList)[index]),
319 mIndex(index),
320 mDag(dag),
321 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
322 {
323 }
324
325 void traverse(TIntermAggregate *node)
326 {
327 node->traverse(this);
328 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
329 }
330
331 bool visitLoop(Visit visit, TIntermLoop *loop)
332 {
333 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
334
335 if (visit == PreVisit && isDiscontinuous)
336 {
337 mNestedDiscont++;
338 }
339 else if (visit == PostVisit && isDiscontinuous)
340 {
341 mNestedDiscont--;
342 }
343
344 return true;
345 }
346
347 bool visitAggregate(Visit visit, TIntermAggregate *node) override
348 {
349 switch (node->getOp())
350 {
351 case EOpFunctionCall:
352 if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
353 {
354 size_t calleeIndex = mDag.findIndex(node);
355 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Nico Weber44897142015-07-10 09:50:00 -0700356 UNUSED_ASSERTION_VARIABLE(mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700357
358 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
359 }
360 break;
361 default:
362 break;
363 }
364 return true;
365 }
366
367 private:
368 MetadataList *mMetadataList;
369 ASTMetadataHLSL *mMetadata;
370 size_t mIndex;
371 const CallDAG &mDag;
372
373 int mNestedDiscont;
374};
375
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700376}
377
378bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermSelection *node)
379{
380 return mControlFlowsContainingGradient.count(node) > 0;
381}
382
383bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
384{
385 return mControlFlowsContainingGradient.count(node) > 0;
386}
387
Corentin Wallez50931452015-03-19 10:39:13 -0700388bool ASTMetadataHLSL::hasDiscontinuousLoop(TIntermSelection *node)
389{
390 return mIfsContainingDiscontinuousLoop.count(node) > 0;
391}
392
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700393MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
394{
395 MetadataList metadataList(callDag.size());
396
397 // Compute all the information related to when gradient operations are used.
398 // We want to know for each function and control flow operation if they have
399 // a gradient operation in their call graph (shortened to "using a gradient"
400 // in the rest of the file).
401 //
402 // This computation is logically split in three steps:
403 // 1 - For each function compute if it uses a gradient in its body, ignoring
404 // calls to other user-defined functions.
405 // 2 - For each function determine if it uses a gradient in its call graph,
406 // using the result of step 1 and the CallDAG to know its callees.
407 // 3 - For each control flow statement of each function, check if it uses a
408 // gradient in the function's body, or if it calls a user-defined function that
409 // uses a gradient.
410 //
411 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
412 // for leaves first, then going down the tree. This is correct because 1 doesn't
413 // depend on other functions, and 2 and 3 depend only on callees.
414 for (size_t i = 0; i < callDag.size(); i++)
415 {
416 PullGradient pull(&metadataList, i, callDag);
417 pull.traverse(callDag.getRecordFromIndex(i).node);
418 }
419
Corentin Wallez50931452015-03-19 10:39:13 -0700420 // Compute which loops are discontinuous and which function are called in
421 // these loops. The same way computing gradient usage is a "pull" process,
422 // computing "bing used in a discont. loop" is a push process. However we also
423 // need to know what ifs have a discontinuous loop inside so we do the same type
424 // of callgraph analysis as for the gradient.
425
426 // First compute which loops are discontinuous (no specific order) and pull
427 // the ifs and functions using a discontinuous loop.
428 for (size_t i = 0; i < callDag.size(); i++)
429 {
430 PullComputeDiscontinuousLoops pull(&metadataList, i, callDag);
431 pull.traverse(callDag.getRecordFromIndex(i).node);
432 }
433
434 // Then push the information to callees, either from the a local discontinuous
435 // loop or from the caller being called in a discontinuous loop already
436 for (size_t i = callDag.size(); i-- > 0;)
437 {
438 PushDiscontinuousLoops push(&metadataList, i, callDag);
439 push.traverse(callDag.getRecordFromIndex(i).node);
440 }
441
442 // We create "Lod0" version of functions with the gradient operations replaced
443 // by non-gradient operations so that the D3D compiler is happier with discont
444 // loops.
445 for (auto &metadata : metadataList)
446 {
447 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
448 }
449
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700450 return metadataList;
451}