Implement gl_FragDepth for GLES SL 3.0

Makes it an error to access gl_FragDepthEXT  in #version 300 es shader.

TODO:
Lacks the feature to make "#extension GL_EXT_frag_depth : require" an
error for #version 300 es.

Reland of: https://chromium-review.googlesource.com/#/c/287570

BUG=angleproject:1102
TEST=angle_unittest

Change-Id: I064d918d65f37539cb1e14f12173ca5591a4ea3f
Reviewed-on: https://chromium-review.googlesource.com/301711
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Tested-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/compiler/translator/BaseTypes.h b/src/compiler/translator/BaseTypes.h
index 2adb1d3..0ed6d0e 100644
--- a/src/compiler/translator/BaseTypes.h
+++ b/src/compiler/translator/BaseTypes.h
@@ -324,7 +324,10 @@
     // built-ins written by fragment shader
     EvqFragColor,
     EvqFragData,
-    EvqFragDepth,
+
+    EvqFragDepth,     // gl_FragDepth for ESSL300.
+    EvqFragDepthEXT,  // gl_FragDepthEXT for ESSL100, EXT_frag_depth.
+
     EvqSecondaryFragColorEXT,  // EXT_blend_func_extended
     EvqSecondaryFragDataEXT,   // EXT_blend_func_extended
 
@@ -389,48 +392,47 @@
 //
 inline const char* getQualifierString(TQualifier q)
 {
+    // clang-format off
     switch(q)
     {
-    case EvqTemporary:      return "Temporary";      break;
-    case EvqGlobal:         return "Global";         break;
-    case EvqConst:          return "const";          break;
-    case EvqAttribute:      return "attribute";      break;
-    case EvqVaryingIn:      return "varying";        break;
-    case EvqVaryingOut:     return "varying";        break;
-    case EvqUniform:        return "uniform";        break;
-    case EvqVertexIn:       return "in";             break;
-    case EvqFragmentOut:    return "out";            break;
-    case EvqVertexOut:      return "out";            break;
-    case EvqFragmentIn:     return "in";             break;
-    case EvqIn:             return "in";             break;
-    case EvqOut:            return "out";            break;
-    case EvqInOut:          return "inout";          break;
-    case EvqConstReadOnly:  return "const";          break;
-    case EvqInstanceID:     return "InstanceID";     break;
-    case EvqPosition:       return "Position";       break;
-    case EvqPointSize:      return "PointSize";      break;
-    case EvqFragCoord:      return "FragCoord";      break;
-    case EvqFrontFacing:    return "FrontFacing";    break;
-    case EvqPointCoord:     return "PointCoord";     break;
-    case EvqFragColor:      return "FragColor";      break;
-    case EvqFragData:       return "FragData";       break;
-    case EvqFragDepth:      return "FragDepth";      break;
-    case EvqSecondaryFragColorEXT:
-        return "SecondaryFragColorEXT";
-        break;
-    case EvqSecondaryFragDataEXT:
-        return "SecondaryFragDataEXT";
-        break;
-    case EvqLastFragColor:  return "LastFragColor";  break;
-    case EvqLastFragData:   return "LastFragData";   break;
-    case EvqSmoothOut:      return "smooth out";     break;
-    case EvqCentroidOut:    return "centroid out";   break;
-    case EvqFlatOut:        return "flat out";       break;
-    case EvqSmoothIn:       return "smooth in";      break;
-    case EvqFlatIn:         return "flat in";        break;
-    case EvqCentroidIn:     return "centroid in";    break;
-    default: UNREACHABLE(); return "unknown qualifier";
+    case EvqTemporary:              return "Temporary";
+    case EvqGlobal:                 return "Global";
+    case EvqConst:                  return "const";
+    case EvqAttribute:              return "attribute";
+    case EvqVaryingIn:              return "varying";
+    case EvqVaryingOut:             return "varying";
+    case EvqUniform:                return "uniform";
+    case EvqVertexIn:               return "in";
+    case EvqFragmentOut:            return "out";
+    case EvqVertexOut:              return "out";
+    case EvqFragmentIn:             return "in";
+    case EvqIn:                     return "in";
+    case EvqOut:                    return "out";
+    case EvqInOut:                  return "inout";
+    case EvqConstReadOnly:          return "const";
+    case EvqInstanceID:             return "InstanceID";
+    case EvqPosition:               return "Position";
+    case EvqPointSize:              return "PointSize";
+    case EvqFragCoord:              return "FragCoord";
+    case EvqFrontFacing:            return "FrontFacing";
+    case EvqPointCoord:             return "PointCoord";
+    case EvqFragColor:              return "FragColor";
+    case EvqFragData:               return "FragData";
+    case EvqFragDepthEXT:           return "FragDepth";
+    case EvqFragDepth:              return "FragDepth";
+    case EvqSecondaryFragColorEXT:  return "SecondaryFragColorEXT";
+    case EvqSecondaryFragDataEXT:   return "SecondaryFragDataEXT";
+    case EvqLastFragColor:          return "LastFragColor";
+    case EvqLastFragData:           return "LastFragData";
+    case EvqSmoothOut:              return "smooth out";
+    case EvqCentroidOut:            return "centroid out";
+    case EvqFlatOut:                return "flat out";
+    case EvqSmoothIn:               return "smooth in";
+    case EvqFlatIn:                 return "flat in";
+    case EvqCentroidIn:             return "centroid in";
+    default: UNREACHABLE();         return "unknown qualifier";
     }
+    // clang-format on
 }
 
 inline const char* getMatrixPackingString(TLayoutMatrixPacking mpq)
diff --git a/src/compiler/translator/Initialize.cpp b/src/compiler/translator/Initialize.cpp
index a5e51dc..2f51aad 100644
--- a/src/compiler/translator/Initialize.cpp
+++ b/src/compiler/translator/Initialize.cpp
@@ -523,10 +523,18 @@
 
             if (resources.EXT_frag_depth)
             {
-                symbolTable.insert(ESSL1_BUILTINS, "GL_EXT_frag_depth", new TVariable(NewPoolTString("gl_FragDepthEXT"),
-                    TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium, EvqFragDepth, 1)));
+                symbolTable.insert(
+                    ESSL1_BUILTINS, "GL_EXT_frag_depth",
+                    new TVariable(
+                        NewPoolTString("gl_FragDepthEXT"),
+                        TType(EbtFloat, resources.FragmentPrecisionHigh ? EbpHigh : EbpMedium,
+                              EvqFragDepthEXT, 1)));
             }
 
+            symbolTable.insert(ESSL3_BUILTINS,
+                               new TVariable(NewPoolTString("gl_FragDepth"),
+                                             TType(EbtFloat, EbpHigh, EvqFragDepth, 1)));
+
             if (resources.EXT_shader_framebuffer_fetch || resources.NV_shader_framebuffer_fetch)
             {
                 TType lastFragData(EbtFloat, EbpMedium, EvqLastFragData, 4, 1, true);
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index d6b9a55..117c34a 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -1386,7 +1386,7 @@
             mUsesInstanceID = true;
             out << name;
         }
-        else if (name == "gl_FragDepthEXT")
+        else if (name == "gl_FragDepthEXT" || name == "gl_FragDepth")
         {
             mUsesFragDepth = true;
             out << "gl_Depth";
diff --git a/src/compiler/translator/VariableInfo.cpp b/src/compiler/translator/VariableInfo.cpp
index 07a9dcd..7413dc3 100644
--- a/src/compiler/translator/VariableInfo.cpp
+++ b/src/compiler/translator/VariableInfo.cpp
@@ -151,6 +151,7 @@
       mLastFragDataAdded(false),
       mFragColorAdded(false),
       mFragDataAdded(false),
+      mFragDepthEXTAdded(false),
       mFragDepthAdded(false),
       mSecondaryFragColorEXTAdded(false),
       mSecondaryFragDataEXTAdded(false),
@@ -405,8 +406,8 @@
                   mFragDataAdded = true;
               }
               return;
-          case EvqFragDepth:
-              if (!mFragDepthAdded)
+          case EvqFragDepthEXT:
+              if (!mFragDepthEXTAdded)
               {
                   OutputVariable info;
                   const char kName[] = "gl_FragDepthEXT";
@@ -420,6 +421,21 @@
                                               ->getType());
                   info.staticUse = true;
                   mOutputVariables->push_back(info);
+                  mFragDepthEXTAdded = true;
+              }
+              return;
+          case EvqFragDepth:
+              if (!mFragDepthAdded)
+              {
+                  OutputVariable info;
+                  const char kName[] = "gl_FragDepth";
+                  info.name          = kName;
+                  info.mappedName    = kName;
+                  info.type          = GL_FLOAT;
+                  info.arraySize     = 0;
+                  info.precision     = GL_HIGH_FLOAT;
+                  info.staticUse = true;
+                  mOutputVariables->push_back(info);
                   mFragDepthAdded = true;
               }
               return;
diff --git a/src/compiler/translator/VariableInfo.h b/src/compiler/translator/VariableInfo.h
index 7fbcec4..9498e9b 100644
--- a/src/compiler/translator/VariableInfo.h
+++ b/src/compiler/translator/VariableInfo.h
@@ -58,6 +58,7 @@
     bool mLastFragDataAdded;
     bool mFragColorAdded;
     bool mFragDataAdded;
+    bool mFragDepthEXTAdded;
     bool mFragDepthAdded;
     bool mSecondaryFragColorEXTAdded;
     bool mSecondaryFragDataEXTAdded;
diff --git a/src/tests/angle_unittests.gypi b/src/tests/angle_unittests.gypi
index eb257e6..af33d67 100644
--- a/src/tests/angle_unittests.gypi
+++ b/src/tests/angle_unittests.gypi
@@ -42,6 +42,7 @@
             '<(angle_path)/src/tests/compiler_tests/DebugShaderPrecision_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/ExpressionLimit_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/EXT_blend_func_extended_test.cpp',
+            '<(angle_path)/src/tests/compiler_tests/FragDepth_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/IntermNode_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/MalformedShader_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/NV_draw_buffers_test.cpp',
diff --git a/src/tests/compiler_tests/CollectVariables_test.cpp b/src/tests/compiler_tests/CollectVariables_test.cpp
index bc6a810..0005790 100644
--- a/src/tests/compiler_tests/CollectVariables_test.cpp
+++ b/src/tests/compiler_tests/CollectVariables_test.cpp
@@ -557,6 +557,30 @@
     EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
     EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision);
 }
+
+// Test that gl_FragData built-in usage in ESSL3 fragment shader is reflected in the output
+// variables list. Also test that the precision is highp.
+TEST_F(CollectFragmentVariablesTest, OutputVarESSL3FragDepthHighp)
+{
+    const std::string &fragDepthHighShader =
+        "#version 300 es\n"
+        "precision mediump float;\n"
+        "void main() {\n"
+        "   gl_FragDepth = 0.7;"
+        "}\n";
+
+    ShBuiltInResources resources = mTranslator->getResources();
+    resources.EXT_frag_depth = 1;
+    initTranslator(resources);
+
+    const sh::OutputVariable *outputVariable = nullptr;
+    validateOutputVariableForShader(fragDepthHighShader, 0u, "gl_FragDepth", &outputVariable);
+    ASSERT_NE(outputVariable, nullptr);
+    EXPECT_EQ(0u, outputVariable->arraySize);
+    EXPECT_GLENUM_EQ(GL_FLOAT, outputVariable->type);
+    EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, outputVariable->precision);
+}
+
 // Test that gl_SecondaryFragColorEXT built-in usage in ESSL1 fragment shader is reflected in the
 // output variables list.
 TEST_F(CollectFragmentVariablesTest, OutputVarESSL1EXTBlendFuncExtendedSecondaryFragColor)
diff --git a/src/tests/compiler_tests/FragDepth_test.cpp b/src/tests/compiler_tests/FragDepth_test.cpp
new file mode 100644
index 0000000..f0bc01a
--- /dev/null
+++ b/src/tests/compiler_tests/FragDepth_test.cpp
@@ -0,0 +1,119 @@
+//
+// Copyright (c) 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.
+//
+// FragDepth_test.cpp:
+//   Test for GLES SL 3.0 gl_FragDepth variable implementation.
+//
+
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "GLSLANG/ShaderLang.h"
+
+namespace
+{
+const char ESSLVersion100[] = "#version 100\n";
+const char ESSLVersion300[] = "#version 300 es\n";
+const char EXTFDPragma[]    = "#extension GL_EXT_frag_depth : require\n";
+}  // namespace
+
+class FragDepthTest : public testing::TestWithParam<bool>
+{
+  protected:
+    void SetUp() override
+    {
+        ShInitBuiltInResources(&mResources);
+        mCompiler                 = nullptr;
+        mResources.EXT_frag_depth = GetParam();
+    }
+
+    void TearDown() override { DestroyCompiler(); }
+    void DestroyCompiler()
+    {
+        if (mCompiler)
+        {
+            ShDestruct(mCompiler);
+            mCompiler = nullptr;
+        }
+    }
+
+    void InitializeCompiler()
+    {
+        DestroyCompiler();
+        mCompiler =
+            ShConstructCompiler(GL_FRAGMENT_SHADER, SH_GLES3_SPEC, SH_GLSL_OUTPUT, &mResources);
+        ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
+    }
+
+    testing::AssertionResult TestShaderCompile(const char *version,
+                                               const char *pragma,
+                                               const char *shader)
+    {
+        const char *shaderStrings[] = {version, pragma, shader};
+        bool success = ShCompile(mCompiler, shaderStrings, 3, 0);
+        if (success)
+        {
+            return ::testing::AssertionSuccess() << "Compilation success";
+        }
+        return ::testing::AssertionFailure() << ShGetInfoLog(mCompiler);
+    }
+
+  protected:
+    ShBuiltInResources mResources;
+    ShHandle mCompiler;
+};
+
+// The GLES SL 3.0 built-in variable gl_FragDepth fails to compile with GLES SL 1.0.
+TEST_P(FragDepthTest, CompileFailsESSL100)
+{
+    static const char shaderString[] =
+        "precision mediump float;\n"
+        "void main() { \n"
+        "    gl_FragDepth = 1.0;\n"
+        "}\n";
+
+    InitializeCompiler();
+    EXPECT_FALSE(TestShaderCompile(ESSLVersion100, "", shaderString));
+    EXPECT_FALSE(TestShaderCompile("", "", shaderString));
+    EXPECT_FALSE(TestShaderCompile("", EXTFDPragma, shaderString));
+}
+
+// The GLES SL 3.0 built-in variable gl_FragDepth compiles with GLES SL 3.0.
+TEST_P(FragDepthTest, CompileSucceedsESSL300)
+{
+    static const char shaderString[] =
+        "precision mediump float;\n"
+        "void main() { \n"
+        "    gl_FragDepth = 1.0;\n"
+        "}\n";
+    InitializeCompiler();
+    EXPECT_TRUE(TestShaderCompile(ESSLVersion300, "", shaderString));
+}
+
+// Using #extension GL_EXT_frag_depth in GLSL ES 3.0 shader fails to compile.
+TEST_P(FragDepthTest, ExtensionFDFailsESSL300)
+{
+    static const char shaderString[] =
+        "precision mediump float;\n"
+        "out vec4 fragColor;\n"
+        "void main() { \n"
+        "    fragColor = vec4(1.0);\n"
+        "}\n";
+    InitializeCompiler();
+    if (mResources.EXT_frag_depth == 1)
+    {
+        // TODO(kkinnunen, geofflang): this should fail. Extensions need to have similar level
+        // system to SymbolTable.  The biggest task is to implement version-aware preprocessor, so
+        // that the extension defines can be defined depending on the version that the preprocessor
+        // saw or did not see.
+        EXPECT_TRUE(TestShaderCompile(ESSLVersion300, EXTFDPragma, shaderString));
+    }
+    else
+    {
+        EXPECT_FALSE(TestShaderCompile(ESSLVersion300, EXTFDPragma, shaderString));
+    }
+}
+
+// The tests should pass regardless whether the EXT_frag_depth is on or not.
+INSTANTIATE_TEST_CASE_P(FragDepthTests, FragDepthTest, testing::Values(false, true));