blob: ca7d08dff9f353da0570d419cf48d07e0934c206 [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()
Jamie Madilld7b1ab52016-12-12 14:42:19 -050049 : TIntermTraverser(true, false, false), mNeedAnotherIteration(false)
Olli Etuaho5c407bb2015-06-01 12:20:39 +030050{
51}
52
53bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
54{
55 if (IsProblematicPow(node))
56 {
Olli Etuaho5c407bb2015-06-01 12:20:39 +030057 TIntermTyped *x = node->getSequence()->at(0)->getAsTyped();
58 TIntermTyped *y = node->getSequence()->at(1)->getAsTyped();
59
Olli Etuahoa2234302016-08-31 12:05:39 +030060 TIntermUnary *log = new TIntermUnary(EOpLog2, x);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030061 log->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030062
Olli Etuaho1dded802016-08-18 18:13:13 +030063 TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType());
64 TIntermBinary *mul = new TIntermBinary(op, y, log);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030065 mul->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030066
Olli Etuahoa2234302016-08-31 12:05:39 +030067 TIntermUnary *exp = new TIntermUnary(EOpExp2, mul);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030068 exp->setLine(node->getLine());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030069
Jamie Madill03d863c2016-07-27 18:15:53 -040070 queueReplacement(node, exp, OriginalNode::IS_DROPPED);
Olli Etuaho5c407bb2015-06-01 12:20:39 +030071
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
Jamie Madilld7b1ab52016-12-12 14:42:19 -050083} // namespace
Olli Etuaho5c407bb2015-06-01 12:20:39 +030084
85void 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();
Jamie Madilld7b1ab52016-12-12 14:42:19 -050094 } while (traverser.needAnotherIteration());
Olli Etuaho5c407bb2015-06-01 12:20:39 +030095}
Jamie Madill45bcc782016-11-07 13:58:48 -050096
97} // namespace sh