blob: 232a958239093c58d75e1ba7526e3291f467a085 [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"
Olli Etuahocccf2b02017-07-05 14:50:54 +030012#include "compiler/translator/IntermTraverse.h"
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070013#include "compiler/translator/SymbolTable.h"
14
Jamie Madill45bcc782016-11-07 13:58:48 -050015namespace sh
16{
17
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070018namespace
19{
20
21// Class used to traverse the AST of a function definition, checking if the
22// function uses a gradient, and writing the set of control flow using gradients.
23// It assumes that the analysis has already been made for the function's
24// callees.
25class PullGradient : public TIntermTraverser
26{
27 public:
28 PullGradient(MetadataList *metadataList, size_t index, const CallDAG &dag)
29 : TIntermTraverser(true, false, true),
30 mMetadataList(metadataList),
31 mMetadata(&(*metadataList)[index]),
32 mIndex(index),
33 mDag(dag)
34 {
35 ASSERT(index < metadataList->size());
Corentin Wallezc16678a2017-02-22 15:24:55 -080036
37 // ESSL 100 builtin gradient functions
Olli Etuahofbb1c792018-01-19 16:26:59 +020038 mGradientBuiltinFunctions.insert(ImmutableString("texture2D"));
39 mGradientBuiltinFunctions.insert(ImmutableString("texture2DProj"));
40 mGradientBuiltinFunctions.insert(ImmutableString("textureCube"));
Corentin Wallezc16678a2017-02-22 15:24:55 -080041
42 // ESSL 300 builtin gradient functions
Olli Etuahofbb1c792018-01-19 16:26:59 +020043 mGradientBuiltinFunctions.insert(ImmutableString("texture"));
44 mGradientBuiltinFunctions.insert(ImmutableString("textureProj"));
45 mGradientBuiltinFunctions.insert(ImmutableString("textureOffset"));
46 mGradientBuiltinFunctions.insert(ImmutableString("textureProjOffset"));
Corentin Wallezc16678a2017-02-22 15:24:55 -080047
48 // ESSL 310 doesn't add builtin gradient functions
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070049 }
50
Olli Etuaho336b1472016-10-05 16:37:55 +010051 void traverse(TIntermFunctionDefinition *node)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070052 {
53 node->traverse(this);
54 ASSERT(mParents.empty());
55 }
56
57 // Called when a gradient operation or a call to a function using a gradient is found.
58 void onGradient()
59 {
60 mMetadata->mUsesGradient = true;
61 // Mark the latest control flow as using a gradient.
62 if (!mParents.empty())
63 {
64 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
65 }
66 }
67
68 void visitControlFlow(Visit visit, TIntermNode *node)
69 {
70 if (visit == PreVisit)
71 {
72 mParents.push_back(node);
73 }
74 else if (visit == PostVisit)
75 {
76 ASSERT(mParents.back() == node);
77 mParents.pop_back();
78 // A control flow's using a gradient means its parents are too.
Jamie Madilld7b1ab52016-12-12 14:42:19 -050079 if (mMetadata->mControlFlowsContainingGradient.count(node) > 0 && !mParents.empty())
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070080 {
81 mMetadata->mControlFlowsContainingGradient.insert(mParents.back());
82 }
83 }
84 }
85
Austin Kinross0998fe92015-12-11 11:31:38 -080086 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070087 {
88 visitControlFlow(visit, loop);
89 return true;
90 }
91
Olli Etuaho57961272016-09-14 13:57:46 +030092 bool visitIfElse(Visit visit, TIntermIfElse *ifElse) override
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070093 {
Olli Etuaho57961272016-09-14 13:57:46 +030094 visitControlFlow(visit, ifElse);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -070095 return true;
96 }
97
98 bool visitUnary(Visit visit, TIntermUnary *node) override
99 {
100 if (visit == PreVisit)
101 {
102 switch (node->getOp())
103 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500104 case EOpDFdx:
105 case EOpDFdy:
Corentin Wallezc16678a2017-02-22 15:24:55 -0800106 case EOpFwidth:
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500107 onGradient();
Nico Weber41b072b2018-02-09 10:01:32 -0500108 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500109 default:
110 break;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700111 }
112 }
113
114 return true;
115 }
116
117 bool visitAggregate(Visit visit, TIntermAggregate *node) override
118 {
119 if (visit == PreVisit)
120 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800121 if (node->getOp() == EOpCallFunctionInAST)
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700122 {
Olli Etuaho1bb85282017-12-14 13:39:53 +0200123 size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800124 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700125
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800126 if ((*mMetadataList)[calleeIndex].mUsesGradient)
127 {
128 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700129 }
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800130 }
131 else if (node->getOp() == EOpCallBuiltInFunction)
132 {
Olli Etuahobed35d72017-12-20 16:36:26 +0200133 if (mGradientBuiltinFunctions.find(node->getFunction()->name()) !=
Olli Etuahoec9232b2017-03-27 17:01:37 +0300134 mGradientBuiltinFunctions.end())
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800135 {
136 onGradient();
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700137 }
138 }
139 }
140
141 return true;
142 }
143
144 private:
145 MetadataList *mMetadataList;
146 ASTMetadataHLSL *mMetadata;
147 size_t mIndex;
148 const CallDAG &mDag;
149
150 // Contains a stack of the control flow nodes that are parents of the node being
151 // currently visited. It is used to mark control flows using a gradient.
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500152 std::vector<TIntermNode *> mParents;
Corentin Wallezc16678a2017-02-22 15:24:55 -0800153
154 // A list of builtin functions that use gradients
Olli Etuahofbb1c792018-01-19 16:26:59 +0200155 std::set<ImmutableString> mGradientBuiltinFunctions;
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700156};
157
Corentin Wallez477b2432015-08-31 10:41:16 -0700158// Traverses the AST of a function definition to compute the the discontinuous loops
159// and the if statements containing gradient loops. It assumes that the gradient loops
160// (loops that contain a gradient) have already been computed and that it has already
161// traversed the current function's callees.
162class PullComputeDiscontinuousAndGradientLoops : public TIntermTraverser
Corentin Wallez50931452015-03-19 10:39:13 -0700163{
164 public:
Corentin Wallez477b2432015-08-31 10:41:16 -0700165 PullComputeDiscontinuousAndGradientLoops(MetadataList *metadataList,
166 size_t index,
167 const CallDAG &dag)
Corentin Wallez50931452015-03-19 10:39:13 -0700168 : TIntermTraverser(true, false, true),
169 mMetadataList(metadataList),
170 mMetadata(&(*metadataList)[index]),
171 mIndex(index),
172 mDag(dag)
173 {
174 }
175
Olli Etuaho336b1472016-10-05 16:37:55 +0100176 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700177 {
178 node->traverse(this);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300179 ASSERT(mLoopsAndSwitches.empty());
Corentin Wallez50931452015-03-19 10:39:13 -0700180 ASSERT(mIfs.empty());
181 }
182
Corentin Wallez477b2432015-08-31 10:41:16 -0700183 // Called when traversing a gradient loop or a call to a function with a
184 // gradient loop in its call graph.
185 void onGradientLoop()
Corentin Wallez50931452015-03-19 10:39:13 -0700186 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700187 mMetadata->mHasGradientLoopInCallGraph = true;
Corentin Wallez50931452015-03-19 10:39:13 -0700188 // Mark the latest if as using a discontinuous loop.
189 if (!mIfs.empty())
190 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700191 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700192 }
193 }
194
Olli Etuaho3873cd02015-04-10 15:00:55 +0300195 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700196 {
197 if (visit == PreVisit)
198 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300199 mLoopsAndSwitches.push_back(loop);
Corentin Wallez477b2432015-08-31 10:41:16 -0700200
201 if (mMetadata->hasGradientInCallGraph(loop))
202 {
203 onGradientLoop();
204 }
Corentin Wallez50931452015-03-19 10:39:13 -0700205 }
206 else if (visit == PostVisit)
207 {
Olli Etuaho3873cd02015-04-10 15:00:55 +0300208 ASSERT(mLoopsAndSwitches.back() == loop);
209 mLoopsAndSwitches.pop_back();
Corentin Wallez50931452015-03-19 10:39:13 -0700210 }
211
212 return true;
213 }
214
Olli Etuaho57961272016-09-14 13:57:46 +0300215 bool visitIfElse(Visit visit, TIntermIfElse *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700216 {
217 if (visit == PreVisit)
218 {
219 mIfs.push_back(node);
220 }
221 else if (visit == PostVisit)
222 {
223 ASSERT(mIfs.back() == node);
224 mIfs.pop_back();
225 // An if using a discontinuous loop means its parents ifs are also discontinuous.
Corentin Wallez477b2432015-08-31 10:41:16 -0700226 if (mMetadata->mIfsContainingGradientLoop.count(node) > 0 && !mIfs.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700227 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700228 mMetadata->mIfsContainingGradientLoop.insert(mIfs.back());
Corentin Wallez50931452015-03-19 10:39:13 -0700229 }
230 }
231
232 return true;
233 }
234
Olli Etuaho3873cd02015-04-10 15:00:55 +0300235 bool visitBranch(Visit visit, TIntermBranch *node) override
Corentin Wallez50931452015-03-19 10:39:13 -0700236 {
237 if (visit == PreVisit)
238 {
239 switch (node->getFlowOp())
240 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500241 case EOpBreak:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300242 {
243 ASSERT(!mLoopsAndSwitches.empty());
244 TIntermLoop *loop = mLoopsAndSwitches.back()->getAsLoopNode();
245 if (loop != nullptr)
246 {
247 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300248 }
249 }
250 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500251 case EOpContinue:
Olli Etuaho3873cd02015-04-10 15:00:55 +0300252 {
253 ASSERT(!mLoopsAndSwitches.empty());
254 TIntermLoop *loop = nullptr;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500255 size_t i = mLoopsAndSwitches.size();
Olli Etuaho3873cd02015-04-10 15:00:55 +0300256 while (loop == nullptr && i > 0)
257 {
258 --i;
259 loop = mLoopsAndSwitches.at(i)->getAsLoopNode();
260 }
261 ASSERT(loop != nullptr);
262 mMetadata->mDiscontinuousLoops.insert(loop);
Olli Etuaho3873cd02015-04-10 15:00:55 +0300263 }
Corentin Wallez50931452015-03-19 10:39:13 -0700264 break;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500265 case EOpKill:
266 case EOpReturn:
267 // A return or discard jumps out of all the enclosing loops
268 if (!mLoopsAndSwitches.empty())
Corentin Wallez50931452015-03-19 10:39:13 -0700269 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500270 for (TIntermNode *intermNode : mLoopsAndSwitches)
Olli Etuaho3873cd02015-04-10 15:00:55 +0300271 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500272 TIntermLoop *loop = intermNode->getAsLoopNode();
273 if (loop)
274 {
275 mMetadata->mDiscontinuousLoops.insert(loop);
276 }
Olli Etuaho3873cd02015-04-10 15:00:55 +0300277 }
Corentin Wallez50931452015-03-19 10:39:13 -0700278 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500279 break;
280 default:
281 UNREACHABLE();
Corentin Wallez50931452015-03-19 10:39:13 -0700282 }
283 }
284
285 return true;
286 }
287
288 bool visitAggregate(Visit visit, TIntermAggregate *node) override
289 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800290 if (visit == PreVisit && node->getOp() == EOpCallFunctionInAST)
Corentin Wallez50931452015-03-19 10:39:13 -0700291 {
Olli Etuaho1bb85282017-12-14 13:39:53 +0200292 size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800293 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700294
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800295 if ((*mMetadataList)[calleeIndex].mHasGradientLoopInCallGraph)
296 {
297 onGradientLoop();
Corentin Wallez50931452015-03-19 10:39:13 -0700298 }
299 }
300
301 return true;
302 }
303
Olli Etuaho3873cd02015-04-10 15:00:55 +0300304 bool visitSwitch(Visit visit, TIntermSwitch *node) override
305 {
306 if (visit == PreVisit)
307 {
308 mLoopsAndSwitches.push_back(node);
309 }
310 else if (visit == PostVisit)
311 {
312 ASSERT(mLoopsAndSwitches.back() == node);
313 mLoopsAndSwitches.pop_back();
314 }
315 return true;
316 }
317
Corentin Wallez50931452015-03-19 10:39:13 -0700318 private:
319 MetadataList *mMetadataList;
320 ASTMetadataHLSL *mMetadata;
321 size_t mIndex;
322 const CallDAG &mDag;
323
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500324 std::vector<TIntermNode *> mLoopsAndSwitches;
Olli Etuaho57961272016-09-14 13:57:46 +0300325 std::vector<TIntermIfElse *> mIfs;
Corentin Wallez50931452015-03-19 10:39:13 -0700326};
327
328// Tags all the functions called in a discontinuous loop
329class PushDiscontinuousLoops : public TIntermTraverser
330{
331 public:
332 PushDiscontinuousLoops(MetadataList *metadataList, size_t index, const CallDAG &dag)
333 : TIntermTraverser(true, true, true),
334 mMetadataList(metadataList),
335 mMetadata(&(*metadataList)[index]),
336 mIndex(index),
337 mDag(dag),
338 mNestedDiscont(mMetadata->mCalledInDiscontinuousLoop ? 1 : 0)
339 {
340 }
341
Olli Etuaho336b1472016-10-05 16:37:55 +0100342 void traverse(TIntermFunctionDefinition *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700343 {
344 node->traverse(this);
345 ASSERT(mNestedDiscont == (mMetadata->mCalledInDiscontinuousLoop ? 1 : 0));
346 }
347
Austin Kinross0998fe92015-12-11 11:31:38 -0800348 bool visitLoop(Visit visit, TIntermLoop *loop) override
Corentin Wallez50931452015-03-19 10:39:13 -0700349 {
350 bool isDiscontinuous = mMetadata->mDiscontinuousLoops.count(loop) > 0;
351
352 if (visit == PreVisit && isDiscontinuous)
353 {
354 mNestedDiscont++;
355 }
356 else if (visit == PostVisit && isDiscontinuous)
357 {
358 mNestedDiscont--;
359 }
360
361 return true;
362 }
363
364 bool visitAggregate(Visit visit, TIntermAggregate *node) override
365 {
366 switch (node->getOp())
367 {
Olli Etuaho1ecd14b2017-01-26 13:54:15 -0800368 case EOpCallFunctionInAST:
369 if (visit == PreVisit && mNestedDiscont > 0)
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500370 {
Olli Etuaho1bb85282017-12-14 13:39:53 +0200371 size_t calleeIndex = mDag.findIndex(node->getFunction()->uniqueId());
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500372 ASSERT(calleeIndex != CallDAG::InvalidIndex && calleeIndex < mIndex);
Corentin Wallez50931452015-03-19 10:39:13 -0700373
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500374 (*mMetadataList)[calleeIndex].mCalledInDiscontinuousLoop = true;
375 }
376 break;
377 default:
378 break;
Corentin Wallez50931452015-03-19 10:39:13 -0700379 }
380 return true;
381 }
382
383 private:
384 MetadataList *mMetadataList;
385 ASTMetadataHLSL *mMetadata;
386 size_t mIndex;
387 const CallDAG &mDag;
388
389 int mNestedDiscont;
390};
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700391}
392
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700393bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
394{
395 return mControlFlowsContainingGradient.count(node) > 0;
396}
397
Olli Etuaho57961272016-09-14 13:57:46 +0300398bool ASTMetadataHLSL::hasGradientLoop(TIntermIfElse *node)
Corentin Wallez50931452015-03-19 10:39:13 -0700399{
Corentin Wallez477b2432015-08-31 10:41:16 -0700400 return mIfsContainingGradientLoop.count(node) > 0;
Corentin Wallez50931452015-03-19 10:39:13 -0700401}
402
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700403MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
404{
405 MetadataList metadataList(callDag.size());
406
407 // Compute all the information related to when gradient operations are used.
408 // We want to know for each function and control flow operation if they have
409 // a gradient operation in their call graph (shortened to "using a gradient"
410 // in the rest of the file).
411 //
412 // This computation is logically split in three steps:
413 // 1 - For each function compute if it uses a gradient in its body, ignoring
414 // calls to other user-defined functions.
415 // 2 - For each function determine if it uses a gradient in its call graph,
416 // using the result of step 1 and the CallDAG to know its callees.
417 // 3 - For each control flow statement of each function, check if it uses a
418 // gradient in the function's body, or if it calls a user-defined function that
419 // uses a gradient.
420 //
421 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
422 // for leaves first, then going down the tree. This is correct because 1 doesn't
423 // depend on other functions, and 2 and 3 depend only on callees.
424 for (size_t i = 0; i < callDag.size(); i++)
425 {
426 PullGradient pull(&metadataList, i, callDag);
427 pull.traverse(callDag.getRecordFromIndex(i).node);
428 }
429
Corentin Wallez50931452015-03-19 10:39:13 -0700430 // Compute which loops are discontinuous and which function are called in
431 // these loops. The same way computing gradient usage is a "pull" process,
432 // computing "bing used in a discont. loop" is a push process. However we also
433 // need to know what ifs have a discontinuous loop inside so we do the same type
434 // of callgraph analysis as for the gradient.
435
436 // First compute which loops are discontinuous (no specific order) and pull
Corentin Wallez477b2432015-08-31 10:41:16 -0700437 // the ifs and functions using a gradient loop.
Corentin Wallez50931452015-03-19 10:39:13 -0700438 for (size_t i = 0; i < callDag.size(); i++)
439 {
Corentin Wallez477b2432015-08-31 10:41:16 -0700440 PullComputeDiscontinuousAndGradientLoops pull(&metadataList, i, callDag);
Corentin Wallez50931452015-03-19 10:39:13 -0700441 pull.traverse(callDag.getRecordFromIndex(i).node);
442 }
443
444 // Then push the information to callees, either from the a local discontinuous
445 // loop or from the caller being called in a discontinuous loop already
446 for (size_t i = callDag.size(); i-- > 0;)
447 {
448 PushDiscontinuousLoops push(&metadataList, i, callDag);
449 push.traverse(callDag.getRecordFromIndex(i).node);
450 }
451
452 // We create "Lod0" version of functions with the gradient operations replaced
453 // by non-gradient operations so that the D3D compiler is happier with discont
454 // loops.
455 for (auto &metadata : metadataList)
456 {
457 metadata.mNeedsLod0 = metadata.mCalledInDiscontinuousLoop && metadata.mUsesGradient;
458 }
459
Corentin Wallezf4eab3b2015-03-18 12:55:45 -0700460 return metadataList;
461}
Jamie Madill45bcc782016-11-07 13:58:48 -0500462
463} // namespace sh