blob: d37b3dc366f7d581b38e45bd57c1e6bf976a1a69 [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
Jamie Madill45bcc782016-11-07 13:58:48 -050014namespace sh
15{
16
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070017namespace
18{
19
20// Class used to traverse the AST of a function definition, checking if the
21// function uses a gradient, and writing the set of control flow using gradients.
22// It assumes that the analysis has already been made for the function's
23// callees.
24class PullGradient : public TIntermTraverser
25{
26 public:
27 PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)
28 : TIntermTraverser(true, false, true),
29 mMetadataList(metadataList),
30 mMetadata(&(*metadataList)[index]),
31 mIndex(index),
32 mDag(dag)
33 {
34 ASSERT(index < metadataList->size());
35 }
36
Olli Etuaho336b1472016-10-05 16:37:55 +010037 void traverse(TIntermFunctionDefinition *node)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070038 {
39 node->traverse(this);
40 ASSERT(mParents.empty());
41 }
42
43 // Called when a gradient operation or a call to a function using a gradient is found.
44 void onGradient()
45 {
46 mMetadata->mUsesGradient = true;
47 // Mark the latest control flow as using a gradient.
48 if (!mParents.empty())
49 {
50 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
51 }
52 }
53
54 void visitControlFlow(Visit visit, TIntermNode *node)
55 {
56 if (visit == PreVisit)
57 {
58 mParents.push_back(node);
59 }
60 else if (visit == PostVisit)
61 {
62 ASSERT(mParents.back() == node);
63 mParents.pop_back();
64 // A control flow's using a gradient means its parents are too.
Jamie Madilld7b1ab52016-12-12 14:42:19 -050065 if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070066 {
67 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
68 }
69 }
70 }
71
Austin Kinross0998fe92015-12-11 11:31:38 -080072 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070073 {
74 visitControlFlow(visit, loop);
75 return true;
76 }
77
Olli Etuaho57961272016-09-14 13:57:46 +030078 bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070079 {
Olli Etuaho57961272016-09-14 13:57:46 +030080 visitControlFlow(visit, ifElse);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070081 return true;
82 }
83
84 bool visitUnary(Visit visit, TIntermUnary *node) override
85 {
86 if (visit == PreVisit)
87 {
88 switch (node->getOp())
89 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -050090 case EOpDFdx:
91 case EOpDFdy:
92 onGradient();
93 default:
94 break;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070095 }
96 }
97
98 return true;
99 }
100
101 bool visitAggregate(Visit visit, TIntermAggregate *node) override
102 {
103 if (visit == PreVisit)
104 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800105 if (node->getOp() == EOpCallFunctionInAST)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700106 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800107 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
108 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700109
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800110 if ((*mMetadataList)[calleeIndex].mUsesGradient)
111 {
112 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700113 }
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800114 }
115 else if (node->getOp() == EOpCallBuiltInFunction)
116 {
117 TString name = TFunction::unmangleName(node->getFunctionSymbolInfo()->getName());
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700118
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800119 if (name == "texture2D" || name == "texture2DProj" || name == "textureCube")
120 {
121 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700122 }
123 }
124 }
125
126 return true;
127 }
128
129 private:
130 MetadataList *mMetadataList;
131 ASTMetadataHLSL *mMetadata;
132 size_t mIndex;
133 const CallDAG &mDag;
134
135 // Contains a stack of the control flow nodes that are parents of the node being
136 // currently visited. It is used to mark control flows using a gradient.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500137 std::vector<TIntermNode *> mParents;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700138};
139
Corentin Wallez477b2432015-08-31 10:41:16 -0700140// Traverses the AST of a function definition to compute the the discontinuous loops
141// and the if statements containing gradient loops. It assumes that the gradient loops
142// (loops that contain a gradient) have already been computed and that it has already
143// traversed the current function's callees.
144class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700145{
146 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700147 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
148 size_t index,
149 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700150 : TIntermTraverser(true, false, true),
151 mMetadataList(metadataList),
152 mMetadata(&(*metadataList)[index]),
153 mIndex(index),
154 mDag(dag)
155 {
156 }
157
Olli Etuaho336b1472016-10-05 16:37:55 +0100158 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700159 {
160 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300161 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700162 ASSERT(mIfs.empty());
163 }
164
Corentin Wallez477b2432015-08-31 10:41:16 -0700165 // Called when traversing a gradient loop or a call to a function with a
166 // gradient loop in its call graph.
167 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700168 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700169 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700170 // Mark the latest if as using a discontinuous loop.
171 if (!mIfs.empty())
172 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700173 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700174 }
175 }
176
Olli Etuaho3873cd02015-04-10 15:00:55 +0300177 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700178 {
179 if (visit == PreVisit)
180 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300181 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700182
183 if (mMetadata->hasGradientInCallGraph(loop))
184 {
185 onGradientLoop();
186 }
Corentin Wallez50931452015-03-19 10:39:13 -0700187 }
188 else if (visit == PostVisit)
189 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300190 ASSERT(mLoopsAndSwitches.back() == loop);
191 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700192 }
193
194 return true;
195 }
196
Olli Etuaho57961272016-09-14 13:57:46 +0300197 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700198 {
199 if (visit == PreVisit)
200 {
201 mIfs.push_back(node);
202 }
203 else if (visit == PostVisit)
204 {
205 ASSERT(mIfs.back() == node);
206 mIfs.pop_back();
207 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700208 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700209 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700210 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700211 }
212 }
213
214 return true;
215 }
216
Olli Etuaho3873cd02015-04-10 15:00:55 +0300217 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700218 {
219 if (visit == PreVisit)
220 {
221 switch (node->getFlowOp())
222 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500223 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300224 {
225 ASSERT(!mLoopsAndSwitches.empty());
226 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
227 if (loop != nullptr)
228 {
229 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300230 }
231 }
232 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500233 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300234 {
235 ASSERT(!mLoopsAndSwitches.empty());
236 TIntermLoop *loop = nullptr;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500237 size_t i = mLoopsAndSwitches.size();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300238 while (loop == nullptr && i > 0)
239 {
240 --i;
241 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
242 }
243 ASSERT(loop != nullptr);
244 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300245 }
Corentin Wallez50931452015-03-19 10:39:13 -0700246 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500247 case EOpKill:
248 case EOpReturn:
249 // A return or discard jumps out of all the enclosing loops
250 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700251 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500252 for (TIntermNode *intermNode : mLoopsAndSwitches)
Olli Etuaho3873cd02015-04-10 15:00:55 +0300253 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500254 TIntermLoop *loop = intermNode->getAsLoopNode();
255 if (loop)
256 {
257 mMetadata->mDiscontinuousLoops.insert(loop);
258 }
Olli Etuaho3873cd02015-04-10 15:00:55 +0300259 }
Corentin Wallez50931452015-03-19 10:39:13 -0700260 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500261 break;
262 default:
263 UNREACHABLE();
Corentin Wallez50931452015-03-19 10:39:13 -0700264 }
265 }
266
267 return true;
268 }
269
270 bool visitAggregate(Visit visit, TIntermAggregate *node) override
271 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800272 if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
Corentin Wallez50931452015-03-19 10:39:13 -0700273 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800274 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
275 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700276
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800277 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
278 {
279 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700280 }
281 }
282
283 return true;
284 }
285
Olli Etuaho3873cd02015-04-10 15:00:55 +0300286 bool visitSwitch(Visit visit, TIntermSwitch *node) override
287 {
288 if (visit == PreVisit)
289 {
290 mLoopsAndSwitches.push_back(node);
291 }
292 else if (visit == PostVisit)
293 {
294 ASSERT(mLoopsAndSwitches.back() == node);
295 mLoopsAndSwitches.pop_back();
296 }
297 return true;
298 }
299
Corentin Wallez50931452015-03-19 10:39:13 -0700300 private:
301 MetadataList *mMetadataList;
302 ASTMetadataHLSL *mMetadata;
303 size_t mIndex;
304 const CallDAG &mDag;
305
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500306 std::vector<TIntermNode *> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300307 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700308};
309
310// Tags all the functions called in a discontinuous loop
311class PushDiscontinuousLoops : public TIntermTraverser
312{
313 public:
314 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
315 : TIntermTraverser(true, true, true),
316 mMetadataList(metadataList),
317 mMetadata(&(*metadataList)[index]),
318 mIndex(index),
319 mDag(dag),
320 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
321 {
322 }
323
Olli Etuaho336b1472016-10-05 16:37:55 +0100324 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700325 {
326 node->traverse(this);
327 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
328 }
329
Austin Kinross0998fe92015-12-11 11:31:38 -0800330 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700331 {
332 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
333
334 if (visit == PreVisit && isDiscontinuous)
335 {
336 mNestedDiscont++;
337 }
338 else if (visit == PostVisit && isDiscontinuous)
339 {
340 mNestedDiscont--;
341 }
342
343 return true;
344 }
345
346 bool visitAggregate(Visit visit, TIntermAggregate *node) override
347 {
348 switch (node->getOp())
349 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800350 case EOpCallFunctionInAST:
351 if (visit == PreVisit && mNestedDiscont > 0)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500352 {
353 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
354 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700355
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500356 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
357 }
358 break;
359 default:
360 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700361 }
362 return true;
363 }
364
365 private:
366 MetadataList *mMetadataList;
367 ASTMetadataHLSL *mMetadata;
368 size_t mIndex;
369 const CallDAG &mDag;
370
371 int mNestedDiscont;
372};
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700373}
374
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700375bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
376{
377 return mControlFlowsContainingGradient.count(node) > 0;
378}
379
Olli Etuaho57961272016-09-14 13:57:46 +0300380bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700381{
Corentin Wallez477b2432015-08-31 10:41:16 -0700382 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700383}
384
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700385MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
386{
387 MetadataList metadataList(callDag.size());
388
389 // Compute all the information related to when gradient operations are used.
390 // We want to know for each function and control flow operation if they have
391 // a gradient operation in their call graph (shortened to "using a gradient"
392 // in the rest of the file).
393 //
394 // This computation is logically split in three steps:
395 // 1 - For each function compute if it uses a gradient in its body, ignoring
396 // calls to other user-defined functions.
397 // 2 - For each function determine if it uses a gradient in its call graph,
398 // using the result of step 1 and the CallDAG to know its callees.
399 // 3 - For each control flow statement of each function, check if it uses a
400 // gradient in the function's body, or if it calls a user-defined function that
401 // uses a gradient.
402 //
403 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
404 // for leaves first, then going down the tree. This is correct because 1 doesn't
405 // depend on other functions, and 2 and 3 depend only on callees.
406 for (size_t i = 0; i < callDag.size(); i++)
407 {
408 PullGradient pull(&metadataList, i, callDag);
409 pull.traverse(callDag.getRecordFromIndex(i).node);
410 }
411
Corentin Wallez50931452015-03-19 10:39:13 -0700412 // Compute which loops are discontinuous and which function are called in
413 // these loops. The same way computing gradient usage is a "pull" process,
414 // computing "bing used in a discont. loop" is a push process. However we also
415 // need to know what ifs have a discontinuous loop inside so we do the same type
416 // of callgraph analysis as for the gradient.
417
418 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700419 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700420 for (size_t i = 0; i < callDag.size(); i++)
421 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700422 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700423 pull.traverse(callDag.getRecordFromIndex(i).node);
424 }
425
426 // Then push the information to callees, either from the a local discontinuous
427 // loop or from the caller being called in a discontinuous loop already
428 for (size_t i = callDag.size(); i-- > 0;)
429 {
430 PushDiscontinuousLoops push(&metadataList, i, callDag);
431 push.traverse(callDag.getRecordFromIndex(i).node);
432 }
433
434 // We create "Lod0" version of functions with the gradient operations replaced
435 // by non-gradient operations so that the D3D compiler is happier with discont
436 // loops.
437 for (auto &metadata : metadataList)
438 {
439 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
440 }
441
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700442 return metadataList;
443}
Jamie Madill45bcc782016-11-07 13:58:48 -0500444
445} // namespace sh