Add a workaround to clamp gl_FragDepth
NVIDIA OpenGL drivers at least up to version 388.59 don't clamp
gl_FragDepth when it is written to a floating point depth buffer.
This bug is now worked around by clamping gl_FragDepth in the shader
if it is statically used.
BUG=angleproject:2299
TEST=angle_end2end_tests on NVIDIA
Change-Id: I61589b2b0dd2813c4901a157c8d37e470063773c
Reviewed-on: https://chromium-review.googlesource.com/840842
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h
index b4f7c84..adbb82b 100644
--- a/include/GLSLANG/ShaderLang.h
+++ b/include/GLSLANG/ShaderLang.h
@@ -25,7 +25,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
-#define ANGLE_SH_VERSION 194
+#define ANGLE_SH_VERSION 195
enum ShShaderSpec
{
@@ -258,6 +258,9 @@
// not handle constant register zero correctly. Only has an effect on HLSL translation.
const ShCompileOptions SH_SKIP_D3D_CONSTANT_REGISTER_ZERO = UINT64_C(1) << 37;
+// Clamp gl_FragDepth to the range [0.0, 1.0] in case it is statically used.
+const ShCompileOptions SH_CLAMP_FRAG_DEPTH = UINT64_C(1) << 38;
+
// Defines alternate strategies for implementing array index clamping.
enum ShArrayIndexClampingStrategy
{
diff --git a/src/compiler.gypi b/src/compiler.gypi
index 74462f5..8e57423 100644
--- a/src/compiler.gypi
+++ b/src/compiler.gypi
@@ -31,6 +31,8 @@
'compiler/translator/BreakVariableAliasingInInnerLoops.h',
'compiler/translator/CallDAG.cpp',
'compiler/translator/CallDAG.h',
+ 'compiler/translator/ClampFragDepth.cpp',
+ 'compiler/translator/ClampFragDepth.h',
'compiler/translator/ClampPointSize.cpp',
'compiler/translator/ClampPointSize.h',
'compiler/translator/CodeGen.cpp',
diff --git a/src/compiler/translator/ClampFragDepth.cpp b/src/compiler/translator/ClampFragDepth.cpp
new file mode 100644
index 0000000..70852aa
--- /dev/null
+++ b/src/compiler/translator/ClampFragDepth.cpp
@@ -0,0 +1,52 @@
+//
+// Copyright (c) 2017 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.
+//
+// ClampFragDepth.cpp: Limit the value that is written to gl_FragDepth to the range [0.0, 1.0].
+// The clamping is run at the very end of shader execution, and is only performed if the shader
+// statically accesses gl_FragDepth.
+//
+
+#include "compiler/translator/ClampFragDepth.h"
+
+#include "compiler/translator/FindSymbolNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/RunAtTheEndOfShader.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+void ClampFragDepth(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ // Only clamp gl_FragDepth if it's used in the shader.
+ if (!FindSymbolNode(root, TString("gl_FragDepth")))
+ {
+ return;
+ }
+
+ TIntermSymbol *fragDepthNode = ReferenceBuiltInVariable("gl_FragDepth", *symbolTable, 300);
+
+ TIntermTyped *minFragDepthNode = CreateZeroNode(TType(EbtFloat, EbpHigh, EvqConst));
+
+ TConstantUnion *maxFragDepthConstant = new TConstantUnion();
+ maxFragDepthConstant->setFConst(1.0);
+ TIntermConstantUnion *maxFragDepthNode =
+ new TIntermConstantUnion(maxFragDepthConstant, TType(EbtFloat, EbpHigh, EvqConst));
+
+ // clamp(gl_FragDepth, 0.0, 1.0)
+ TIntermSequence *clampArguments = new TIntermSequence();
+ clampArguments->push_back(fragDepthNode->deepCopy());
+ clampArguments->push_back(minFragDepthNode);
+ clampArguments->push_back(maxFragDepthNode);
+ TIntermTyped *clampedFragDepth =
+ CreateBuiltInFunctionCallNode("clamp", clampArguments, *symbolTable, 100);
+
+ // gl_FragDepth = clamp(gl_FragDepth, 0.0, 1.0)
+ TIntermBinary *assignFragDepth = new TIntermBinary(EOpAssign, fragDepthNode, clampedFragDepth);
+
+ RunAtTheEndOfShader(root, assignFragDepth, symbolTable);
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/ClampFragDepth.h b/src/compiler/translator/ClampFragDepth.h
new file mode 100644
index 0000000..aa0ed1a
--- /dev/null
+++ b/src/compiler/translator/ClampFragDepth.h
@@ -0,0 +1,24 @@
+//
+// Copyright (c) 2017 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.
+//
+// ClampFragDepth.h: Limit the value that is written to gl_FragDepth to the range [0.0, 1.0].
+// The clamping is run at the very end of shader execution, and is only performed if the shader
+// statically accesses gl_FragDepth.
+//
+
+#ifndef COMPILER_TRANSLATOR_CLAMPFRAGDEPTH_H_
+#define COMPILER_TRANSLATOR_CLAMPFRAGDEPTH_H_
+
+namespace sh
+{
+
+class TIntermBlock;
+class TSymbolTable;
+
+void ClampFragDepth(TIntermBlock *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_CLAMPFRAGDEPTH_H_
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 2d58a28..669365b 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -12,6 +12,7 @@
#include "common/utilities.h"
#include "compiler/translator/AddAndTrueToLoopCondition.h"
#include "compiler/translator/CallDAG.h"
+#include "compiler/translator/ClampFragDepth.h"
#include "compiler/translator/ClampPointSize.h"
#include "compiler/translator/CollectVariables.h"
#include "compiler/translator/DeclareAndInitBuiltinsForInstancedMultiview.h"
@@ -641,6 +642,11 @@
ClampPointSize(root, compileResources.MaxPointSize, &getSymbolTable());
}
+ if (getShaderType() == GL_FRAGMENT_SHADER && (compileOptions & SH_CLAMP_FRAG_DEPTH))
+ {
+ ClampFragDepth(root, &getSymbolTable());
+ }
+
if (compileOptions & SH_REWRITE_VECTOR_SCALAR_ARITHMETIC)
{
VectorizeVectorScalarArithmetic(root, &getSymbolTable());
diff --git a/src/libANGLE/renderer/gl/ShaderGL.cpp b/src/libANGLE/renderer/gl/ShaderGL.cpp
index cc94f37..112f83b 100644
--- a/src/libANGLE/renderer/gl/ShaderGL.cpp
+++ b/src/libANGLE/renderer/gl/ShaderGL.cpp
@@ -127,6 +127,11 @@
options |= SH_DONT_USE_LOOPS_TO_INITIALIZE_VARIABLES;
}
+ if (mWorkarounds.clampFragDepth)
+ {
+ options |= SH_CLAMP_FRAG_DEPTH;
+ }
+
if (mMultiviewImplementationType == MultiviewImplementationTypeGL::NV_VIEWPORT_ARRAY2)
{
options |= SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW;
diff --git a/src/libANGLE/renderer/gl/WorkaroundsGL.h b/src/libANGLE/renderer/gl/WorkaroundsGL.h
index ab50022..07ac6f3 100644
--- a/src/libANGLE/renderer/gl/WorkaroundsGL.h
+++ b/src/libANGLE/renderer/gl/WorkaroundsGL.h
@@ -143,6 +143,10 @@
// On some Android devices for loops used to initialize variables hit native GLSL compiler bugs.
bool dontUseLoopsToInitializeVariables = false;
+
+ // On some NVIDIA drivers gl_FragDepth is not clamped correctly when rendering to a floating
+ // point depth buffer. Clamp it in the translated shader to fix this.
+ bool clampFragDepth = false;
};
inline WorkaroundsGL::WorkaroundsGL() = default;
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 7878eea..a375742 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -1157,6 +1157,10 @@
workarounds->rewriteVectorScalarArithmetic = IsNvidia(vendor);
+ // TODO(oetuaho): Make this specific to the affected driver versions. Versions at least up to
+ // 390 are known to be affected. Versions after that are expected not to be affected.
+ workarounds->clampFragDepth = IsNvidia(vendor);
+
#if defined(ANGLE_PLATFORM_ANDROID)
// TODO(jmadill): Narrow workaround range for specific devices.
workarounds->reapplyUBOBindingsAfterUsingBinaryProgram = true;
diff --git a/src/tests/gl_tests/BuiltinVariableTest.cpp b/src/tests/gl_tests/BuiltinVariableTest.cpp
index 7dbf930..36cf0aa 100644
--- a/src/tests/gl_tests/BuiltinVariableTest.cpp
+++ b/src/tests/gl_tests/BuiltinVariableTest.cpp
@@ -301,14 +301,12 @@
// Test that gl_FragDepth is clamped above 0
TEST_P(BuiltinVariableFragDepthClampingFloatRBOTest, Above0)
{
- ANGLE_SKIP_TEST_IF(IsNVIDIA());
CheckDepthWritten(0.0f, -1.0f);
}
// Test that gl_FragDepth is clamped below 1
TEST_P(BuiltinVariableFragDepthClampingFloatRBOTest, Below1)
{
- ANGLE_SKIP_TEST_IF(IsNVIDIA());
CheckDepthWritten(1.0f, 42.0f);
}