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