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/RemovePow.h"
12
13 #include "compiler/translator/InfoSink.h"
14 #include "compiler/translator/IntermTraverse.h"
15
16 namespace sh
17 {
18
19 namespace
20 {
21
IsProblematicPow(TIntermTyped * node)22 bool IsProblematicPow(TIntermTyped *node)
23 {
24 TIntermAggregate *agg = node->getAsAggregate();
25 if (agg != nullptr && agg->getOp() == EOpPow)
26 {
27 ASSERT(agg->getSequence()->size() == 2);
28 return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr;
29 }
30 return false;
31 }
32
33 // Traverser that converts all pow operations simultaneously.
34 class RemovePowTraverser : public TIntermTraverser
35 {
36 public:
37 RemovePowTraverser();
38
39 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
40
nextIteration()41 void nextIteration() { mNeedAnotherIteration = false; }
needAnotherIteration() const42 bool needAnotherIteration() const { return mNeedAnotherIteration; }
43
44 protected:
45 bool mNeedAnotherIteration;
46 };
47
RemovePowTraverser()48 RemovePowTraverser::RemovePowTraverser()
49 : TIntermTraverser(true, false, false), mNeedAnotherIteration(false)
50 {
51 }
52
visitAggregate(Visit visit,TIntermAggregate * node)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 TIntermUnary *log = new TIntermUnary(EOpLog2, x);
61 log->setLine(node->getLine());
62
63 TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
64 TIntermBinary *mul = new TIntermBinary(op, y, log);
65 mul->setLine(node->getLine());
66
67 TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
68 exp->setLine(node->getLine());
69
70 queueReplacement(exp, OriginalNode::IS_DROPPED);
71
72 // If the x parameter also needs to be replaced, we need to do that in another traversal,
73 // since it's parent node will change in a way that's not handled correctly by updateTree().
74 if (IsProblematicPow(x))
75 {
76 mNeedAnotherIteration = true;
77 return false;
78 }
79 }
80 return true;
81 }
82
83 } // namespace
84
RemovePow(TIntermNode * root)85 void RemovePow(TIntermNode *root)
86 {
87 RemovePowTraverser traverser;
88 // Iterate as necessary, and reset the traverser between iterations.
89 do
90 {
91 traverser.nextIteration();
92 root->traverse(&traverser);
93 traverser.updateTree();
94 } while (traverser.needAnotherIteration());
95 }
96
97 } // namespace sh
98