Hash :
5c407bb7
Author :
Date :
2015-06-01T12:20:39
Work around pow() issue in NVIDIA 331.x drivers pow(x, y) when y is a certain kind of a constant vector can cause issues on NVIDIA 331 series drivers. Add an option to replace pow(x, y) with exp2(y * log2(x)) when y is a constant to work around this issue. This is done with an AST traverser instead of BuiltInFunctionEmulator, since there's no mechanism in BuiltInFunctionEmulator to apply the replacements only to calls where the second parameter is constant. TEST=WebGL conformance tests BUG=chromium:477306 Change-Id: Ifb327d72659fca36868439f24705203014b3ce53 Reviewed-on: https://chromium-review.googlesource.com/274279 Tested-by: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Zhenyao Mo <zmo@chromium.org>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 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());
}