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