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);
 }