blob: cbeaff5aed7429f20977959847612c282babe5aa [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
Olli Etuaho336b1472016-10-05 16:37:55 +010034 void traverse(TIntermFunctionDefinition *node)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070035 {
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
Austin Kinross0998fe92015-12-11 11:31:38 -080069 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070070 {
71 visitControlFlow(visit, loop);
72 return true;
73 }
74
Olli Etuaho57961272016-09-14 13:57:46 +030075 bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070076 {
Olli Etuaho57961272016-09-14 13:57:46 +030077 visitControlFlow(visit, ifElse);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070078 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 {
Olli Etuahobd674552016-10-06 13:28:42 +0100106 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700107 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 {
Olli Etuahobd674552016-10-06 13:28:42 +0100116 TString name =
117 TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700118
119 if (name == "texture2D" ||
120 name == "texture2DProj" ||
121 name == "textureCube")
122 {
123 onGradient();
124 }
125 }
126 }
127 }
128
129 return true;
130 }
131
132 private:
133 MetadataList *mMetadataList;
134 ASTMetadataHLSL *mMetadata;
135 size_t mIndex;
136 const CallDAG &mDag;
137
138 // Contains a stack of the control flow nodes that are parents of the node being
139 // currently visited. It is used to mark control flows using a gradient.
140 std::vector<TIntermNode*> mParents;
141};
142
Corentin Wallez477b2432015-08-31 10:41:16 -0700143// Traverses the AST of a function definition to compute the the discontinuous loops
144// and the if statements containing gradient loops. It assumes that the gradient loops
145// (loops that contain a gradient) have already been computed and that it has already
146// traversed the current function's callees.
147class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700148{
149 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700150 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
151 size_t index,
152 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700153 : TIntermTraverser(true, false, true),
154 mMetadataList(metadataList),
155 mMetadata(&(*metadataList)[index]),
156 mIndex(index),
157 mDag(dag)
158 {
159 }
160
Olli Etuaho336b1472016-10-05 16:37:55 +0100161 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700162 {
163 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300164 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700165 ASSERT(mIfs.empty());
166 }
167
Corentin Wallez477b2432015-08-31 10:41:16 -0700168 // Called when traversing a gradient loop or a call to a function with a
169 // gradient loop in its call graph.
170 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700171 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700172 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700173 // Mark the latest if as using a discontinuous loop.
174 if (!mIfs.empty())
175 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700176 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700177 }
178 }
179
Olli Etuaho3873cd02015-04-10 15:00:55 +0300180 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700181 {
182 if (visit == PreVisit)
183 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300184 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700185
186 if (mMetadata->hasGradientInCallGraph(loop))
187 {
188 onGradientLoop();
189 }
Corentin Wallez50931452015-03-19 10:39:13 -0700190 }
191 else if (visit == PostVisit)
192 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300193 ASSERT(mLoopsAndSwitches.back() == loop);
194 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700195 }
196
197 return true;
198 }
199
Olli Etuaho57961272016-09-14 13:57:46 +0300200 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700201 {
202 if (visit == PreVisit)
203 {
204 mIfs.push_back(node);
205 }
206 else if (visit == PostVisit)
207 {
208 ASSERT(mIfs.back() == node);
209 mIfs.pop_back();
210 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700211 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700212 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700213 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700214 }
215 }
216
217 return true;
218 }
219
Olli Etuaho3873cd02015-04-10 15:00:55 +0300220 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700221 {
222 if (visit == PreVisit)
223 {
224 switch (node->getFlowOp())
225 {
Corentin Wallez50931452015-03-19 10:39:13 -0700226 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300227 {
228 ASSERT(!mLoopsAndSwitches.empty());
229 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
230 if (loop != nullptr)
231 {
232 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300233 }
234 }
235 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700236 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300237 {
238 ASSERT(!mLoopsAndSwitches.empty());
239 TIntermLoop *loop = nullptr;
240 size_t i = mLoopsAndSwitches.size();
241 while (loop == nullptr && i > 0)
242 {
243 --i;
244 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
245 }
246 ASSERT(loop != nullptr);
247 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300248 }
Corentin Wallez50931452015-03-19 10:39:13 -0700249 break;
Corentin Walleza1884f22015-04-29 10:15:16 -0700250 case EOpKill:
Corentin Wallez50931452015-03-19 10:39:13 -0700251 case EOpReturn:
Corentin Walleza1884f22015-04-29 10:15:16 -0700252 // A return or discard jumps out of all the enclosing loops
Olli Etuaho3873cd02015-04-10 15:00:55 +0300253 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700254 {
Cooper Partin4f488492015-08-07 16:05:25 -0700255 for (TIntermNode *intermNode : mLoopsAndSwitches)
Corentin Wallez50931452015-03-19 10:39:13 -0700256 {
Cooper Partin4f488492015-08-07 16:05:25 -0700257 TIntermLoop *loop = intermNode->getAsLoopNode();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300258 if (loop)
259 {
260 mMetadata->mDiscontinuousLoops.insert(loop);
261 }
Corentin Wallez50931452015-03-19 10:39:13 -0700262 }
Corentin Wallez50931452015-03-19 10:39:13 -0700263 }
264 break;
265 default:
266 UNREACHABLE();
267 }
268 }
269
270 return true;
271 }
272
273 bool visitAggregate(Visit visit, TIntermAggregate *node) override
274 {
275 if (visit == PreVisit && node->getOp() == EOpFunctionCall)
276 {
277 if (node->isUserDefined())
278 {
Olli Etuahobd674552016-10-06 13:28:42 +0100279 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallez50931452015-03-19 10:39:13 -0700280 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Nico Weber44897142015-07-10 09:50:00 -0700281 UNUSED_ASSERTION_VARIABLE(mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700282
Corentin Wallez477b2432015-08-31 10:41:16 -0700283 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
Corentin Wallez50931452015-03-19 10:39:13 -0700284 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700285 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700286 }
287 }
288 }
289
290 return true;
291 }
292
Olli Etuaho3873cd02015-04-10 15:00:55 +0300293 bool visitSwitch(Visit visit, TIntermSwitch *node) override
294 {
295 if (visit == PreVisit)
296 {
297 mLoopsAndSwitches.push_back(node);
298 }
299 else if (visit == PostVisit)
300 {
301 ASSERT(mLoopsAndSwitches.back() == node);
302 mLoopsAndSwitches.pop_back();
303 }
304 return true;
305 }
306
Corentin Wallez50931452015-03-19 10:39:13 -0700307 private:
308 MetadataList *mMetadataList;
309 ASTMetadataHLSL *mMetadata;
310 size_t mIndex;
311 const CallDAG &mDag;
312
Olli Etuaho3873cd02015-04-10 15:00:55 +0300313 std::vector<TIntermNode*> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300314 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700315};
316
317// Tags all the functions called in a discontinuous loop
318class PushDiscontinuousLoops : public TIntermTraverser
319{
320 public:
321 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
322 : TIntermTraverser(true, true, true),
323 mMetadataList(metadataList),
324 mMetadata(&(*metadataList)[index]),
325 mIndex(index),
326 mDag(dag),
327 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
328 {
329 }
330
Olli Etuaho336b1472016-10-05 16:37:55 +0100331 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700332 {
333 node->traverse(this);
334 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
335 }
336
Austin Kinross0998fe92015-12-11 11:31:38 -0800337 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700338 {
339 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
340
341 if (visit == PreVisit && isDiscontinuous)
342 {
343 mNestedDiscont++;
344 }
345 else if (visit == PostVisit && isDiscontinuous)
346 {
347 mNestedDiscont--;
348 }
349
350 return true;
351 }
352
353 bool visitAggregate(Visit visit, TIntermAggregate *node) override
354 {
355 switch (node->getOp())
356 {
357 case EOpFunctionCall:
358 if (visit == PreVisit && node->isUserDefined() && mNestedDiscont > 0)
359 {
Olli Etuahobd674552016-10-06 13:28:42 +0100360 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
Corentin Wallez50931452015-03-19 10:39:13 -0700361 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Nico Weber44897142015-07-10 09:50:00 -0700362 UNUSED_ASSERTION_VARIABLE(mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700363
364 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
365 }
366 break;
367 default:
368 break;
369 }
370 return true;
371 }
372
373 private:
374 MetadataList *mMetadataList;
375 ASTMetadataHLSL *mMetadata;
376 size_t mIndex;
377 const CallDAG &mDag;
378
379 int mNestedDiscont;
380};
381
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700382}
383
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700384bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
385{
386 return mControlFlowsContainingGradient.count(node) > 0;
387}
388
Olli Etuaho57961272016-09-14 13:57:46 +0300389bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700390{
Corentin Wallez477b2432015-08-31 10:41:16 -0700391 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700392}
393
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700394MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
395{
396 MetadataList metadataList(callDag.size());
397
398 // Compute all the information related to when gradient operations are used.
399 // We want to know for each function and control flow operation if they have
400 // a gradient operation in their call graph (shortened to "using a gradient"
401 // in the rest of the file).
402 //
403 // This computation is logically split in three steps:
404 // 1 - For each function compute if it uses a gradient in its body, ignoring
405 // calls to other user-defined functions.
406 // 2 - For each function determine if it uses a gradient in its call graph,
407 // using the result of step 1 and the CallDAG to know its callees.
408 // 3 - For each control flow statement of each function, check if it uses a
409 // gradient in the function's body, or if it calls a user-defined function that
410 // uses a gradient.
411 //
412 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
413 // for leaves first, then going down the tree. This is correct because 1 doesn't
414 // depend on other functions, and 2 and 3 depend only on callees.
415 for (size_t i = 0; i < callDag.size(); i++)
416 {
417 PullGradient pull(&metadataList, i, callDag);
418 pull.traverse(callDag.getRecordFromIndex(i).node);
419 }
420
Corentin Wallez50931452015-03-19 10:39:13 -0700421 // Compute which loops are discontinuous and which function are called in
422 // these loops. The same way computing gradient usage is a "pull" process,
423 // computing "bing used in a discont. loop" is a push process. However we also
424 // need to know what ifs have a discontinuous loop inside so we do the same type
425 // of callgraph analysis as for the gradient.
426
427 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700428 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700429 for (size_t i = 0; i < callDag.size(); i++)
430 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700431 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700432 pull.traverse(callDag.getRecordFromIndex(i).node);
433 }
434
435 // Then push the information to callees, either from the a local discontinuous
436 // loop or from the caller being called in a discontinuous loop already
437 for (size_t i = callDag.size(); i-- > 0;)
438 {
439 PushDiscontinuousLoops push(&metadataList, i, callDag);
440 push.traverse(callDag.getRecordFromIndex(i).node);
441 }
442
443 // We create "Lod0" version of functions with the gradient operations replaced
444 // by non-gradient operations so that the D3D compiler is happier with discont
445 // loops.
446 for (auto &metadata : metadataList)
447 {
448 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
449 }
450
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700451 return metadataList;
452}