blob: 80a8ab612aae03885a54f9d05cd3eae3de48d552 [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());
Corentin Wallezc16678a2017-02-22 15:24:55 -080035
36 // ESSL 100 builtin gradient functions
37 mGradientBuiltinFunctions.insert("texture2D");
38 mGradientBuiltinFunctions.insert("texture2DProj");
39 mGradientBuiltinFunctions.insert("textureCube");
40
41 // ESSL 300 builtin gradient functions
42 mGradientBuiltinFunctions.insert("texture");
43 mGradientBuiltinFunctions.insert("textureProj");
44 mGradientBuiltinFunctions.insert("textureOffset");
45 mGradientBuiltinFunctions.insert("textureProjOffset");
46
47 // ESSL 310 doesn't add builtin gradient functions
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070048 }
49
Olli Etuaho336b1472016-10-05 16:37:55 +010050 void traverse(TIntermFunctionDefinition *node)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070051 {
52 node->traverse(this);
53 ASSERT(mParents.empty());
54 }
55
56 // Called when a gradient operation or a call to a function using a gradient is found.
57 void onGradient()
58 {
59 mMetadata->mUsesGradient = true;
60 // Mark the latest control flow as using a gradient.
61 if (!mParents.empty())
62 {
63 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
64 }
65 }
66
67 void visitControlFlow(Visit visit, TIntermNode *node)
68 {
69 if (visit == PreVisit)
70 {
71 mParents.push_back(node);
72 }
73 else if (visit == PostVisit)
74 {
75 ASSERT(mParents.back() == node);
76 mParents.pop_back();
77 // A control flow's using a gradient means its parents are too.
Jamie Madilld7b1ab52016-12-12 14:42:19 -050078 if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070079 {
80 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
81 }
82 }
83 }
84
Austin Kinross0998fe92015-12-11 11:31:38 -080085 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070086 {
87 visitControlFlow(visit, loop);
88 return true;
89 }
90
Olli Etuaho57961272016-09-14 13:57:46 +030091 bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070092 {
Olli Etuaho57961272016-09-14 13:57:46 +030093 visitControlFlow(visit, ifElse);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070094 return true;
95 }
96
97 bool visitUnary(Visit visit, TIntermUnary *node) override
98 {
99 if (visit == PreVisit)
100 {
101 switch (node->getOp())
102 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500103 case EOpDFdx:
104 case EOpDFdy:
Corentin Wallezc16678a2017-02-22 15:24:55 -0800105 case EOpFwidth:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500106 onGradient();
107 default:
108 break;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700109 }
110 }
111
112 return true;
113 }
114
115 bool visitAggregate(Visit visit, TIntermAggregate *node) override
116 {
117 if (visit == PreVisit)
118 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800119 if (node->getOp() == EOpCallFunctionInAST)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700120 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800121 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
122 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700123
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800124 if ((*mMetadataList)[calleeIndex].mUsesGradient)
125 {
126 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700127 }
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800128 }
129 else if (node->getOp() == EOpCallBuiltInFunction)
130 {
Olli Etuahoec9232b2017-03-27 17:01:37 +0300131 if (mGradientBuiltinFunctions.find(node->getFunctionSymbolInfo()->getName()) !=
132 mGradientBuiltinFunctions.end())
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800133 {
134 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700135 }
136 }
137 }
138
139 return true;
140 }
141
142 private:
143 MetadataList *mMetadataList;
144 ASTMetadataHLSL *mMetadata;
145 size_t mIndex;
146 const CallDAG &mDag;
147
148 // Contains a stack of the control flow nodes that are parents of the node being
149 // currently visited. It is used to mark control flows using a gradient.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500150 std::vector<TIntermNode *> mParents;
Corentin Wallezc16678a2017-02-22 15:24:55 -0800151
152 // A list of builtin functions that use gradients
153 std::set<TString> mGradientBuiltinFunctions;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700154};
155
Corentin Wallez477b2432015-08-31 10:41:16 -0700156// Traverses the AST of a function definition to compute the the discontinuous loops
157// and the if statements containing gradient loops. It assumes that the gradient loops
158// (loops that contain a gradient) have already been computed and that it has already
159// traversed the current function's callees.
160class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700161{
162 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700163 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
164 size_t index,
165 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700166 : TIntermTraverser(true, false, true),
167 mMetadataList(metadataList),
168 mMetadata(&(*metadataList)[index]),
169 mIndex(index),
170 mDag(dag)
171 {
172 }
173
Olli Etuaho336b1472016-10-05 16:37:55 +0100174 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700175 {
176 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300177 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700178 ASSERT(mIfs.empty());
179 }
180
Corentin Wallez477b2432015-08-31 10:41:16 -0700181 // Called when traversing a gradient loop or a call to a function with a
182 // gradient loop in its call graph.
183 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700184 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700185 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700186 // Mark the latest if as using a discontinuous loop.
187 if (!mIfs.empty())
188 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700189 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700190 }
191 }
192
Olli Etuaho3873cd02015-04-10 15:00:55 +0300193 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700194 {
195 if (visit == PreVisit)
196 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300197 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700198
199 if (mMetadata->hasGradientInCallGraph(loop))
200 {
201 onGradientLoop();
202 }
Corentin Wallez50931452015-03-19 10:39:13 -0700203 }
204 else if (visit == PostVisit)
205 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300206 ASSERT(mLoopsAndSwitches.back() == loop);
207 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700208 }
209
210 return true;
211 }
212
Olli Etuaho57961272016-09-14 13:57:46 +0300213 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700214 {
215 if (visit == PreVisit)
216 {
217 mIfs.push_back(node);
218 }
219 else if (visit == PostVisit)
220 {
221 ASSERT(mIfs.back() == node);
222 mIfs.pop_back();
223 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700224 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700225 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700226 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700227 }
228 }
229
230 return true;
231 }
232
Olli Etuaho3873cd02015-04-10 15:00:55 +0300233 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700234 {
235 if (visit == PreVisit)
236 {
237 switch (node->getFlowOp())
238 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500239 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300240 {
241 ASSERT(!mLoopsAndSwitches.empty());
242 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
243 if (loop != nullptr)
244 {
245 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300246 }
247 }
248 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500249 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300250 {
251 ASSERT(!mLoopsAndSwitches.empty());
252 TIntermLoop *loop = nullptr;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500253 size_t i = mLoopsAndSwitches.size();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300254 while (loop == nullptr && i > 0)
255 {
256 --i;
257 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
258 }
259 ASSERT(loop != nullptr);
260 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300261 }
Corentin Wallez50931452015-03-19 10:39:13 -0700262 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500263 case EOpKill:
264 case EOpReturn:
265 // A return or discard jumps out of all the enclosing loops
266 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700267 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500268 for (TIntermNode *intermNode : mLoopsAndSwitches)
Olli Etuaho3873cd02015-04-10 15:00:55 +0300269 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500270 TIntermLoop *loop = intermNode->getAsLoopNode();
271 if (loop)
272 {
273 mMetadata->mDiscontinuousLoops.insert(loop);
274 }
Olli Etuaho3873cd02015-04-10 15:00:55 +0300275 }
Corentin Wallez50931452015-03-19 10:39:13 -0700276 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500277 break;
278 default:
279 UNREACHABLE();
Corentin Wallez50931452015-03-19 10:39:13 -0700280 }
281 }
282
283 return true;
284 }
285
286 bool visitAggregate(Visit visit, TIntermAggregate *node) override
287 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800288 if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
Corentin Wallez50931452015-03-19 10:39:13 -0700289 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800290 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
291 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700292
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800293 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
294 {
295 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700296 }
297 }
298
299 return true;
300 }
301
Olli Etuaho3873cd02015-04-10 15:00:55 +0300302 bool visitSwitch(Visit visit, TIntermSwitch *node) override
303 {
304 if (visit == PreVisit)
305 {
306 mLoopsAndSwitches.push_back(node);
307 }
308 else if (visit == PostVisit)
309 {
310 ASSERT(mLoopsAndSwitches.back() == node);
311 mLoopsAndSwitches.pop_back();
312 }
313 return true;
314 }
315
Corentin Wallez50931452015-03-19 10:39:13 -0700316 private:
317 MetadataList *mMetadataList;
318 ASTMetadataHLSL *mMetadata;
319 size_t mIndex;
320 const CallDAG &mDag;
321
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500322 std::vector<TIntermNode *> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300323 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700324};
325
326// Tags all the functions called in a discontinuous loop
327class PushDiscontinuousLoops : public TIntermTraverser
328{
329 public:
330 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
331 : TIntermTraverser(true, true, true),
332 mMetadataList(metadataList),
333 mMetadata(&(*metadataList)[index]),
334 mIndex(index),
335 mDag(dag),
336 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
337 {
338 }
339
Olli Etuaho336b1472016-10-05 16:37:55 +0100340 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700341 {
342 node->traverse(this);
343 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
344 }
345
Austin Kinross0998fe92015-12-11 11:31:38 -0800346 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700347 {
348 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
349
350 if (visit == PreVisit && isDiscontinuous)
351 {
352 mNestedDiscont++;
353 }
354 else if (visit == PostVisit && isDiscontinuous)
355 {
356 mNestedDiscont--;
357 }
358
359 return true;
360 }
361
362 bool visitAggregate(Visit visit, TIntermAggregate *node) override
363 {
364 switch (node->getOp())
365 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800366 case EOpCallFunctionInAST:
367 if (visit == PreVisit && mNestedDiscont > 0)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500368 {
369 size_t calleeIndex = mDag.findIndex(node->getFunctionSymbolInfo());
370 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700371
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500372 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
373 }
374 break;
375 default:
376 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700377 }
378 return true;
379 }
380
381 private:
382 MetadataList *mMetadataList;
383 ASTMetadataHLSL *mMetadata;
384 size_t mIndex;
385 const CallDAG &mDag;
386
387 int mNestedDiscont;
388};
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700389}
390
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700391bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
392{
393 return mControlFlowsContainingGradient.count(node) > 0;
394}
395
Olli Etuaho57961272016-09-14 13:57:46 +0300396bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700397{
Corentin Wallez477b2432015-08-31 10:41:16 -0700398 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700399}
400
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700401MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
402{
403 MetadataList metadataList(callDag.size());
404
405 // Compute all the information related to when gradient operations are used.
406 // We want to know for each function and control flow operation if they have
407 // a gradient operation in their call graph (shortened to "using a gradient"
408 // in the rest of the file).
409 //
410 // This computation is logically split in three steps:
411 // 1 - For each function compute if it uses a gradient in its body, ignoring
412 // calls to other user-defined functions.
413 // 2 - For each function determine if it uses a gradient in its call graph,
414 // using the result of step 1 and the CallDAG to know its callees.
415 // 3 - For each control flow statement of each function, check if it uses a
416 // gradient in the function's body, or if it calls a user-defined function that
417 // uses a gradient.
418 //
419 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
420 // for leaves first, then going down the tree. This is correct because 1 doesn't
421 // depend on other functions, and 2 and 3 depend only on callees.
422 for (size_t i = 0; i < callDag.size(); i++)
423 {
424 PullGradient pull(&metadataList, i, callDag);
425 pull.traverse(callDag.getRecordFromIndex(i).node);
426 }
427
Corentin Wallez50931452015-03-19 10:39:13 -0700428 // Compute which loops are discontinuous and which function are called in
429 // these loops. The same way computing gradient usage is a "pull" process,
430 // computing "bing used in a discont. loop" is a push process. However we also
431 // need to know what ifs have a discontinuous loop inside so we do the same type
432 // of callgraph analysis as for the gradient.
433
434 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700435 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700436 for (size_t i = 0; i < callDag.size(); i++)
437 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700438 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700439 pull.traverse(callDag.getRecordFromIndex(i).node);
440 }
441
442 // Then push the information to callees, either from the a local discontinuous
443 // loop or from the caller being called in a discontinuous loop already
444 for (size_t i = callDag.size(); i-- > 0;)
445 {
446 PushDiscontinuousLoops push(&metadataList, i, callDag);
447 push.traverse(callDag.getRecordFromIndex(i).node);
448 }
449
450 // We create "Lod0" version of functions with the gradient operations replaced
451 // by non-gradient operations so that the D3D compiler is happier with discont
452 // loops.
453 for (auto &metadata : metadataList)
454 {
455 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
456 }
457
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700458 return metadataList;
459}
Jamie Madill45bcc782016-11-07 13:58:48 -0500460
461} // namespace sh