1 | // |
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 | // RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a |
7 | // constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series |
8 | // OpenGL drivers. |
9 | // |
10 | |
11 | #include "compiler/translator/tree_ops/RemovePow.h" |
12 | |
13 | #include "compiler/translator/InfoSink.h" |
14 | #include "compiler/translator/tree_util/IntermNode_util.h" |
15 | #include "compiler/translator/tree_util/IntermTraverse.h" |
16 | |
17 | namespace sh |
18 | { |
19 | |
20 | namespace |
21 | { |
22 | |
23 | bool IsProblematicPow(TIntermTyped *node) |
24 | { |
25 | TIntermAggregate *agg = node->getAsAggregate(); |
26 | if (agg != nullptr && agg->getOp() == EOpPow) |
27 | { |
28 | ASSERT(agg->getSequence()->size() == 2); |
29 | return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr; |
30 | } |
31 | return false; |
32 | } |
33 | |
34 | // Traverser that converts all pow operations simultaneously. |
35 | class RemovePowTraverser : public TIntermTraverser |
36 | { |
37 | public: |
38 | RemovePowTraverser(TSymbolTable *symbolTable); |
39 | |
40 | bool visitAggregate(Visit visit, TIntermAggregate *node) override; |
41 | |
42 | void nextIteration() { mNeedAnotherIteration = false; } |
43 | bool needAnotherIteration() const { return mNeedAnotherIteration; } |
44 | |
45 | protected: |
46 | bool mNeedAnotherIteration; |
47 | }; |
48 | |
49 | RemovePowTraverser::RemovePowTraverser(TSymbolTable *symbolTable) |
50 | : TIntermTraverser(true, false, false, symbolTable), mNeedAnotherIteration(false) |
51 | {} |
52 | |
53 | bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node) |
54 | { |
55 | if (IsProblematicPow(node)) |
56 | { |
57 | TIntermTyped *x = node->getSequence()->at(0)->getAsTyped(); |
58 | TIntermTyped *y = node->getSequence()->at(1)->getAsTyped(); |
59 | |
60 | TIntermSequence *logArgs = new TIntermSequence(); |
61 | logArgs->push_back(x); |
62 | TIntermTyped *log = CreateBuiltInFunctionCallNode("log2" , logArgs, *mSymbolTable, 100); |
63 | log->setLine(node->getLine()); |
64 | |
65 | TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType()); |
66 | TIntermBinary *mul = new TIntermBinary(op, y, log); |
67 | mul->setLine(node->getLine()); |
68 | |
69 | TIntermSequence *expArgs = new TIntermSequence(); |
70 | expArgs->push_back(mul); |
71 | TIntermTyped *exp = CreateBuiltInFunctionCallNode("exp2" , expArgs, *mSymbolTable, 100); |
72 | exp->setLine(node->getLine()); |
73 | |
74 | queueReplacement(exp, OriginalNode::IS_DROPPED); |
75 | |
76 | // If the x parameter also needs to be replaced, we need to do that in another traversal, |
77 | // since it's parent node will change in a way that's not handled correctly by updateTree(). |
78 | if (IsProblematicPow(x)) |
79 | { |
80 | mNeedAnotherIteration = true; |
81 | return false; |
82 | } |
83 | } |
84 | return true; |
85 | } |
86 | |
87 | } // namespace |
88 | |
89 | void RemovePow(TIntermNode *root, TSymbolTable *symbolTable) |
90 | { |
91 | RemovePowTraverser traverser(symbolTable); |
92 | // Iterate as necessary, and reset the traverser between iterations. |
93 | do |
94 | { |
95 | traverser.nextIteration(); |
96 | root->traverse(&traverser); |
97 | traverser.updateTree(); |
98 | } while (traverser.needAnotherIteration()); |
99 | } |
100 | |
101 | } // namespace sh |
102 | |