diff options
Diffstat (limited to 'src/3rdparty/angle/src/compiler/translator/RemovePow.cpp')
-rw-r--r-- | src/3rdparty/angle/src/compiler/translator/RemovePow.cpp | 105 |
1 files changed, 105 insertions, 0 deletions
diff --git a/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp new file mode 100644 index 0000000000..6dbb48da9c --- /dev/null +++ b/src/3rdparty/angle/src/compiler/translator/RemovePow.cpp @@ -0,0 +1,105 @@ +// +// 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 +{ + +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)) + { + TInfoSink nullSink; + + TIntermTyped *x = node->getSequence()->at(0)->getAsTyped(); + TIntermTyped *y = node->getSequence()->at(1)->getAsTyped(); + + TIntermUnary *log = new TIntermUnary(EOpLog2); + log->setOperand(x); + log->setLine(node->getLine()); + log->setType(x->getType()); + + TIntermBinary *mul = new TIntermBinary(EOpMul); + mul->setLeft(y); + mul->setRight(log); + mul->setLine(node->getLine()); + bool valid = mul->promote(nullSink); + UNUSED_ASSERTION_VARIABLE(valid); + ASSERT(valid); + + TIntermUnary *exp = new TIntermUnary(EOpExp2); + exp->setOperand(mul); + exp->setLine(node->getLine()); + exp->setType(node->getType()); + + NodeUpdateEntry replacePow(getParentNode(), node, exp, false); + mReplacements.push_back(replacePow); + + // 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()); +} |