blob: 9580667135cd841cbea5e5748b84b77b6b925700 [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);
108
109 if ((*mMetadataList)[calleeIndex].mUsesGradient) {
110 onGradient();
111 }
112 }
113 else
114 {
115 TString name = TFunction::unmangleName(node->getName());
116
117 if (name == "texture2D" ||
118 name == "texture2DProj" ||
119 name == "textureCube")
120 {
121 onGradient();
122 }
123 }
124 }
125 }
126
127 return true;
128 }
129
130 private:
131 MetadataList *mMetadataList;
132 ASTMetadataHLSL *mMetadata;
133 size_t mIndex;
134 const CallDAG &mDag;
135
136 // Contains a stack of the control flow nodes that are parents of the node being
137 // currently visited. It is used to mark control flows using a gradient.
138 std::vector<TIntermNode*> mParents;
139};
140
141}
142
143bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermSelection *node)
144{
145 return mControlFlowsContainingGradient.count(node) > 0;
146}
147
148bool ASTMetadataHLSL::hasGradientInCallGraph(TIntermLoop *node)
149{
150 return mControlFlowsContainingGradient.count(node) > 0;
151}
152
153MetadataList CreateASTMetadataHLSL(TIntermNode *root, const CallDAG &callDag)
154{
155 MetadataList metadataList(callDag.size());
156
157 // Compute all the information related to when gradient operations are used.
158 // We want to know for each function and control flow operation if they have
159 // a gradient operation in their call graph (shortened to "using a gradient"
160 // in the rest of the file).
161 //
162 // This computation is logically split in three steps:
163 // 1 - For each function compute if it uses a gradient in its body, ignoring
164 // calls to other user-defined functions.
165 // 2 - For each function determine if it uses a gradient in its call graph,
166 // using the result of step 1 and the CallDAG to know its callees.
167 // 3 - For each control flow statement of each function, check if it uses a
168 // gradient in the function's body, or if it calls a user-defined function that
169 // uses a gradient.
170 //
171 // We take advantage of the call graph being a DAG and instead compute 1, 2 and 3
172 // for leaves first, then going down the tree. This is correct because 1 doesn't
173 // depend on other functions, and 2 and 3 depend only on callees.
174 for (size_t i = 0; i < callDag.size(); i++)
175 {
176 PullGradient pull(&metadataList, i, callDag);
177 pull.traverse(callDag.getRecordFromIndex(i).node);
178 }
179
180 return metadataList;
181}