| // |
| // Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| // RemovePow is an AST traverser to convert pow(x, y) built-in calls where y is a |
| // constant to exp2(y * log2(x)). This works around an issue in NVIDIA 311 series |
| // OpenGL drivers. |
| // |
| |
| #include "compiler/translator/RemovePow.h" |
| |
| #include "compiler/translator/InfoSink.h" |
| #include "compiler/translator/IntermNode.h" |
| |
| namespace sh |
| { |
| |
| namespace |
| { |
| |
| bool IsProblematicPow(TIntermTyped *node) |
| { |
| TIntermAggregate *agg = node->getAsAggregate(); |
| if (agg != nullptr && agg->getOp() == EOpPow) |
| { |
| ASSERT(agg->getSequence()->size() == 2); |
| return agg->getSequence()->at(1)->getAsConstantUnion() != nullptr; |
| } |
| return false; |
| } |
| |
| // Traverser that converts all pow operations simultaneously. |
| class RemovePowTraverser : public TIntermTraverser |
| { |
| public: |
| RemovePowTraverser(); |
| |
| bool visitAggregate(Visit visit, TIntermAggregate *node) override; |
| |
| void nextIteration() { mNeedAnotherIteration = false; } |
| bool needAnotherIteration() const { return mNeedAnotherIteration; } |
| |
| protected: |
| bool mNeedAnotherIteration; |
| }; |
| |
| RemovePowTraverser::RemovePowTraverser() |
| : TIntermTraverser(true, false, false), mNeedAnotherIteration(false) |
| { |
| } |
| |
| bool RemovePowTraverser::visitAggregate(Visit visit, TIntermAggregate *node) |
| { |
| if (IsProblematicPow(node)) |
| { |
| TIntermTyped *x = node->getSequence()->at(0)->getAsTyped(); |
| TIntermTyped *y = node->getSequence()->at(1)->getAsTyped(); |
| |
| TIntermUnary *log = new TIntermUnary(EOpLog2, x); |
| log->setLine(node->getLine()); |
| |
| TOperator op = TIntermBinary::GetMulOpBasedOnOperands(y->getType(), log->getType()); |
| TIntermBinary *mul = new TIntermBinary(op, y, log); |
| mul->setLine(node->getLine()); |
| |
| TIntermUnary *exp = new TIntermUnary(EOpExp2, mul); |
| exp->setLine(node->getLine()); |
| |
| queueReplacement(node, exp, OriginalNode::IS_DROPPED); |
| |
| // If the x parameter also needs to be replaced, we need to do that in another traversal, |
| // since it's parent node will change in a way that's not handled correctly by updateTree(). |
| if (IsProblematicPow(x)) |
| { |
| mNeedAnotherIteration = true; |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| void RemovePow(TIntermNode *root) |
| { |
| RemovePowTraverser traverser; |
| // Iterate as necessary, and reset the traverser between iterations. |
| do |
| { |
| traverser.nextIteration(); |
| root->traverse(&traverser); |
| traverser.updateTree(); |
| } while (traverser.needAnotherIteration()); |
| } |
| |
| } // namespace sh |