diff options
Diffstat (limited to 'gfx/angle/src/compiler/translator/RemovePow.cpp')
-rwxr-xr-x | gfx/angle/src/compiler/translator/RemovePow.cpp | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/gfx/angle/src/compiler/translator/RemovePow.cpp b/gfx/angle/src/compiler/translator/RemovePow.cpp new file mode 100755 index 000000000..192084c36 --- /dev/null +++ b/gfx/angle/src/compiler/translator/RemovePow.cpp @@ -0,0 +1,99 @@ +// +// 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 |