blob: 4b0fe694886a8e6dc98e9e3c0c5cf8202e922313 [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
16namespace
17{
18
19bool IsProblematicPow(TIntermTyped *node)
20{
21 TIntermAggregate *agg = node->getAsAggregate();
22 if (agg != nullptr && agg->getOp() == EOpPow)
23 {
24 ASSERT(agg->getSequence()->size() == 2);
25 return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr;
26 }
27 return false;
28}
29
30// Traverser that converts all pow operations simultaneously.
31class RemovePowTraverser : public TIntermTraverser
32{
33 public:
34 RemovePowTraverser();
35
36 bool visitAggregate(Visit visit, TIntermAggregate *node) override;
37
38 void nextIteration() { mNeedAnotherIteration = false; }
39 bool needAnotherIteration() const { return mNeedAnotherIteration; }
40
41 protected:
42 bool mNeedAnotherIteration;
43};
44
45RemovePowTraverser::RemovePowTraverser()
46 : TIntermTraverser(true, false, false),
47 mNeedAnotherIteration(false)
48{
49}
50
51bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
52{
53 if (IsProblematicPow(node))
54 {
Olli Etuaho5c407bb2015-06-01 12:20:39 +030055 TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
56 TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
57
Olli Etuahoa2234302016-08-31 12:05:39 +030058 TIntermUnary *log = new TIntermUnary(EOpLog2, x);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030059 log->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030060
Olli Etuaho1dded802016-08-18 18:13:13 +030061 TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
62 TIntermBinary *mul = new TIntermBinary(op, y, log);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030063 mul->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030064
Olli Etuahoa2234302016-08-31 12:05:39 +030065 TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030066 exp->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030067
Jamie Madill03d863c2016-07-27 18:15:53 -040068 queueReplacement(node, exp, OriginalNode::IS_DROPPED);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030069
70 // If the x parameter also needs to be replaced, we need to do that in another traversal,
71 // since it's parent node will change in a way that's not handled correctly by updateTree().
72 if (IsProblematicPow(x))
73 {
74 mNeedAnotherIteration = true;
75 return false;
76 }
77 }
78 return true;
79}
80
81} // namespace
82
83void RemovePow(TIntermNode *root)
84{
85 RemovePowTraverser traverser;
86 // Iterate as necessary, and reset the traverser between iterations.
87 do
88 {
89 traverser.nextIteration();
90 root->traverse(&traverser);
91 traverser.updateTree();
92 }
93 while (traverser.needAnotherIteration());
94}