Commit 5c407bb7 by Olli Etuaho

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/274279Tested-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Reviewed-by: 's avatarZhenyao Mo <zmo@chromium.org>
parent 6bef4ff7
...@@ -48,7 +48,7 @@ typedef unsigned int GLenum; ...@@ -48,7 +48,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 136 #define ANGLE_SH_VERSION 137
typedef enum { typedef enum {
SH_GLES2_SPEC = 0x8B40, SH_GLES2_SPEC = 0x8B40,
...@@ -188,6 +188,10 @@ typedef enum { ...@@ -188,6 +188,10 @@ typedef enum {
// compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH // compilation process. Pruning coupled with SH_LIMIT_CALL_STACK_DEPTH
// helps avoid bad shaders causing stack overflows. // helps avoid bad shaders causing stack overflows.
SH_DONT_PRUNE_UNUSED_FUNCTIONS = 0x100000, SH_DONT_PRUNE_UNUSED_FUNCTIONS = 0x100000,
// This flag works around a bug in NVIDIA 331 series drivers related
// to pow(x, y) where y is a constant vector.
SH_REMOVE_POW_WITH_CONSTANT_EXPONENT = 0x200000,
} ShCompileOptions; } ShCompileOptions;
// Defines alternate strategies for implementing array index clamping. // Defines alternate strategies for implementing array index clamping.
......
...@@ -84,6 +84,8 @@ ...@@ -84,6 +84,8 @@
'compiler/translator/QualifierAlive.h', 'compiler/translator/QualifierAlive.h',
'compiler/translator/RegenerateStructNames.cpp', 'compiler/translator/RegenerateStructNames.cpp',
'compiler/translator/RegenerateStructNames.h', 'compiler/translator/RegenerateStructNames.h',
'compiler/translator/RemovePow.cpp',
'compiler/translator/RemovePow.h',
'compiler/translator/RenameFunction.h', 'compiler/translator/RenameFunction.h',
'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp',
'compiler/translator/ScalarizeVecAndMatConstructorArgs.h', 'compiler/translator/ScalarizeVecAndMatConstructorArgs.h',
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include "compiler/translator/ParseContext.h" #include "compiler/translator/ParseContext.h"
#include "compiler/translator/PruneEmptyDeclarations.h" #include "compiler/translator/PruneEmptyDeclarations.h"
#include "compiler/translator/RegenerateStructNames.h" #include "compiler/translator/RegenerateStructNames.h"
#include "compiler/translator/RemovePow.h"
#include "compiler/translator/RenameFunction.h" #include "compiler/translator/RenameFunction.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h" #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
#include "compiler/translator/UnfoldShortCircuitAST.h" #include "compiler/translator/UnfoldShortCircuitAST.h"
...@@ -312,6 +313,11 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[], ...@@ -312,6 +313,11 @@ TIntermNode *TCompiler::compileTreeImpl(const char* const shaderStrings[],
unfoldShortCircuit.updateTree(); unfoldShortCircuit.updateTree();
} }
if (success && (compileOptions & SH_REMOVE_POW_WITH_CONSTANT_EXPONENT))
{
RemovePow(root);
}
if (success && (compileOptions & SH_VARIABLES)) if (success && (compileOptions & SH_VARIABLES))
{ {
collectVariables(root); collectVariables(root);
......
//
// 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());
}
//
// 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.
//
#ifndef COMPILER_TRANSLATOR_REMOVEPOW_H_
#define COMPILER_TRANSLATOR_REMOVEPOW_H_
class TIntermNode;
void RemovePow(TIntermNode *root);
#endif // COMPILER_TRANSLATOR_REMOVEPOW_H_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment