blob: 192084c36c58fe4cf088d35d557f12c31f879841 [file] [log] [blame]
Olli Etuaho5c407bb2015-06-01 12:20:39 +03001//
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/IntermNode.h"
15
Jamie Madill45bcc782016-11-07 13:58:48 -050016namespace sh
17{
18
Olli Etuaho5c407bb2015-06-01 12:20:39 +030019namespace
20{
21
22bool 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.
34class RemovePowTraverser : public TIntermTraverser
35{
36 public:
37 RemovePowTraverser();
38
39 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
40
41 void nextIteration() { mNeedAnotherIteration = false; }
42 bool needAnotherIteration() const { return mNeedAnotherIteration; }
43
44 protected:
45 bool mNeedAnotherIteration;
46};
47
48RemovePowTraverser::RemovePowTraverser()
49 : TIntermTraverser(true, false, false),
50 mNeedAnotherIteration(false)
51{
52}
53
54bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
55{
56 if (IsProblematicPow(node))
57 {
Olli Etuaho5c407bb2015-06-01 12:20:39 +030058 TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
59 TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
60
Olli Etuahoa2234302016-08-31 12:05:39 +030061 TIntermUnary *log = new TIntermUnary(EOpLog2, x);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030062 log->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030063
Olli Etuaho1dded802016-08-18 18:13:13 +030064 TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
65 TIntermBinary *mul = new TIntermBinary(op, y, log);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030066 mul->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030067
Olli Etuahoa2234302016-08-31 12:05:39 +030068 TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030069 exp->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030070
Jamie Madill03d863c2016-07-27 18:15:53 -040071 queueReplacement(node, exp, OriginalNode::IS_DROPPED);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030072
73 // If the x parameter also needs to be replaced, we need to do that in another traversal,
74 // since it's parent node will change in a way that's not handled correctly by updateTree().
75 if (IsProblematicPow(x))
76 {
77 mNeedAnotherIteration = true;
78 return false;
79 }
80 }
81 return true;
82}
83
84} // namespace
85
86void RemovePow(TIntermNode *root)
87{
88 RemovePowTraverser traverser;
89 // Iterate as necessary, and reset the traverser between iterations.
90 do
91 {
92 traverser.nextIteration();
93 root->traverse(&traverser);
94 traverser.updateTree();
95 }
96 while (traverser.needAnotherIteration());
97}
Jamie Madill45bcc782016-11-07 13:58:48 -050098
99} // namespace sh