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/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());