ES31: Implement Geometry Shader resource queries on OpenGL

This patch intends to implement all geometry shader related
resource queries on OpenGL back-ends.

This patch also fixes a memory leak by releasing the geometry
shader compiler handle in the destructor of the Compiler.

BUG=angleproject:1941, angleproject:2261
TEST=angle_end2end_tests

Change-Id: Ieb69c162d2fc6c6550e145d1ec7948c3d36d4d15
Reviewed-on: https://chromium-review.googlesource.com/784552
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 44da2bb..4a66a02 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -228,9 +228,7 @@
       robustResourceInitialization(false),
       programCacheControl(false),
       textureRectangle(false),
-      geometryShader(false),
-      maxGeometryOutputVertices(0),
-      maxGeometryShaderInvocations(0)
+      geometryShader(false)
 {
 }
 
@@ -849,7 +847,28 @@
       maxTransformFeedbackSeparateComponents(0),
 
       // Table 20.49
-      maxSamples(0)
+      maxSamples(0),
+
+      // Table 20.40 (cont.) (GL_EXT_geometry_shader)
+      maxFramebufferLayers(0),
+      layerProvokingVertex(0),
+
+      // Table 20.43gs (GL_EXT_geometry_shader)
+      maxGeometryUniformComponents(0),
+      maxGeometryUniformBlocks(0),
+      maxGeometryInputComponents(0),
+      maxGeometryOutputComponents(0),
+      maxGeometryOutputVertices(0),
+      maxGeometryTotalOutputComponents(0),
+      maxGeometryTextureImageUnits(0),
+      maxGeometryAtomicCounterBuffers(0),
+      maxGeometryAtomicCounters(0),
+      maxGeometryShaderStorageBlocks(0),
+      maxGeometryShaderInvocations(0),
+
+      // Table 20.46 (GL_EXT_geometry_shader)
+      maxGeometryImageUniforms(0),
+      maxCombinedGeometryUniformComponents(0)
 {
     for (size_t i = 0; i < 3; ++i)
     {
@@ -1053,6 +1072,37 @@
         caps.maxRectangleTextureSize = 64;
     }
 
+    if (extensions.geometryShader)
+    {
+        // Table 20.40 (GL_EXT_geometry_shader)
+        caps.maxFramebufferLayers = 256;
+        caps.layerProvokingVertex = GL_LAST_VERTEX_CONVENTION_EXT;
+
+        // Table 20.43gs (GL_EXT_geometry_shader)
+        caps.maxGeometryUniformComponents     = 1024;
+        caps.maxGeometryUniformBlocks         = 12;
+        caps.maxGeometryInputComponents       = 64;
+        caps.maxGeometryOutputComponents      = 64;
+        caps.maxGeometryOutputVertices        = 256;
+        caps.maxGeometryTotalOutputComponents = 1024;
+        caps.maxGeometryTextureImageUnits     = 16;
+        caps.maxGeometryAtomicCounterBuffers  = 0;
+        caps.maxGeometryAtomicCounters        = 0;
+        caps.maxGeometryShaderStorageBlocks   = 0;
+        caps.maxGeometryShaderInvocations     = 32;
+
+        // Table 20.46 (GL_EXT_geometry_shader)
+        caps.maxGeometryImageUniforms = 0;
+        caps.maxCombinedGeometryUniformComponents =
+            caps.maxGeometryUniformBlocks * static_cast<GLuint>(caps.maxUniformBlockSize / 4) +
+            caps.maxGeometryUniformComponents;
+
+        // Table 20.46 (GL_EXT_geometry_shader)
+        caps.maxUniformBufferBindings     = 48;
+        caps.maxCombinedUniformBlocks     = 36;
+        caps.maxCombinedTextureImageUnits = 64;
+    }
+
     return caps;
 }
 }
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index 64bdf97..a8453a5 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -373,11 +373,6 @@
 
     // GL_EXT_geometry_shader
     bool geometryShader;
-    // GL_EXT_geometry_shader (May 31, 2016) Table 20.43gs: Implementation dependent geometry shader
-    // limits
-    // TODO(jiawei.shao@intel.com): add all implementation dependent geometry shader limits.
-    GLuint maxGeometryOutputVertices;
-    GLuint maxGeometryShaderInvocations;
 };
 
 struct ExtensionInfo
@@ -562,6 +557,29 @@
 
     // ES 3.1 (April 29, 2015) Table 20.49: Framebuffer Dependent Values
     GLuint maxSamples;
+
+    // GL_EXT_geometry_shader (May 31, 2016) Table 20.40: Implementation-Dependent Values (cont.)
+    GLuint maxFramebufferLayers;
+    GLuint layerProvokingVertex;
+
+    // GL_EXT_geometry_shader (May 31, 2016) Table 20.43gs: Implementation dependent geometry shader
+    // limits
+    GLuint maxGeometryUniformComponents;
+    GLuint maxGeometryUniformBlocks;
+    GLuint maxGeometryInputComponents;
+    GLuint maxGeometryOutputComponents;
+    GLuint maxGeometryOutputVertices;
+    GLuint maxGeometryTotalOutputComponents;
+    GLuint maxGeometryTextureImageUnits;
+    GLuint maxGeometryAtomicCounterBuffers;
+    GLuint maxGeometryAtomicCounters;
+    GLuint maxGeometryShaderStorageBlocks;
+    GLuint maxGeometryShaderInvocations;
+
+    // GL_EXT_geometry_shader (May 31, 2016) Table 20.46: Implementation dependent aggregate shader
+    // limits
+    GLuint maxGeometryImageUniforms;
+    GLuint maxCombinedGeometryUniformComponents;
 };
 
 Caps GenerateMinimumCaps(const Version &clientVersion, const Extensions &extensions);
diff --git a/src/libANGLE/Compiler.cpp b/src/libANGLE/Compiler.cpp
index 236c7e1..b524520 100644
--- a/src/libANGLE/Compiler.cpp
+++ b/src/libANGLE/Compiler.cpp
@@ -132,10 +132,20 @@
     }
 
     // Geometry Shader constants
+    // TODO(jiawei.shao@intel.com): use EXT_geometry_shader everywhere in compiler.
     mResources.OES_geometry_shader = extensions.geometryShader;
-    // TODO(jiawei.shao@intel.com): initialize all implementation dependent geometry shader limits.
-    mResources.MaxGeometryOutputVertices    = extensions.maxGeometryOutputVertices;
-    mResources.MaxGeometryShaderInvocations = extensions.maxGeometryShaderInvocations;
+    mResources.MaxGeometryUniformComponents     = caps.maxGeometryUniformComponents;
+    mResources.MaxGeometryUniformBlocks         = caps.maxGeometryUniformBlocks;
+    mResources.MaxGeometryInputComponents       = caps.maxGeometryInputComponents;
+    mResources.MaxGeometryOutputComponents      = caps.maxGeometryOutputComponents;
+    mResources.MaxGeometryOutputVertices        = caps.maxGeometryOutputVertices;
+    mResources.MaxGeometryTotalOutputComponents = caps.maxGeometryTotalOutputComponents;
+    mResources.MaxGeometryTextureImageUnits     = caps.maxGeometryTextureImageUnits;
+    mResources.MaxGeometryAtomicCounterBuffers  = caps.maxGeometryAtomicCounterBuffers;
+    mResources.MaxGeometryAtomicCounters        = caps.maxGeometryAtomicCounters;
+    mResources.MaxGeometryShaderStorageBlocks   = caps.maxGeometryShaderStorageBlocks;
+    mResources.MaxGeometryShaderInvocations     = caps.maxGeometryShaderInvocations;
+    mResources.MaxGeometryImageUniforms         = caps.maxGeometryImageUniforms;
 }
 
 Compiler::~Compiler()
@@ -167,6 +177,15 @@
         activeCompilerHandles--;
     }
 
+    if (mGeometryCompiler)
+    {
+        sh::Destruct(mGeometryCompiler);
+        mGeometryCompiler = nullptr;
+
+        ASSERT(activeCompilerHandles > 0);
+        activeCompilerHandles--;
+    }
+
     if (activeCompilerHandles == 0)
     {
         sh::Finalize();
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index f638bed..caebc20 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -1508,6 +1508,53 @@
         case GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT:
             *params = mCaps.shaderStorageBufferOffsetAlignment;
             break;
+
+        // GL_EXT_geometry_shader
+        case GL_MAX_FRAMEBUFFER_LAYERS_EXT:
+            *params = mCaps.maxFramebufferLayers;
+            break;
+        case GL_LAYER_PROVOKING_VERTEX_EXT:
+            *params = mCaps.layerProvokingVertex;
+            break;
+        case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT:
+            *params = mCaps.maxGeometryUniformComponents;
+            break;
+        case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT:
+            *params = mCaps.maxGeometryUniformBlocks;
+            break;
+        case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT:
+            *params = mCaps.maxCombinedGeometryUniformComponents;
+            break;
+        case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT:
+            *params = mCaps.maxGeometryInputComponents;
+            break;
+        case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT:
+            *params = mCaps.maxGeometryOutputComponents;
+            break;
+        case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT:
+            *params = mCaps.maxGeometryOutputVertices;
+            break;
+        case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT:
+            *params = mCaps.maxGeometryTotalOutputComponents;
+            break;
+        case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT:
+            *params = mCaps.maxGeometryShaderInvocations;
+            break;
+        case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT:
+            *params = mCaps.maxGeometryTextureImageUnits;
+            break;
+        case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT:
+            *params = mCaps.maxGeometryAtomicCounterBuffers;
+            break;
+        case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT:
+            *params = mCaps.maxGeometryAtomicCounters;
+            break;
+        case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT:
+            *params = mCaps.maxGeometryImageUniforms;
+            break;
+        case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT:
+            *params = mCaps.maxGeometryShaderStorageBlocks;
+            break;
         default:
             mGLState.getIntegerv(this, pname, params);
             break;
diff --git a/src/libANGLE/ContextState.cpp b/src/libANGLE/ContextState.cpp
index d109cca..5321ab2 100644
--- a/src/libANGLE/ContextState.cpp
+++ b/src/libANGLE/ContextState.cpp
@@ -721,6 +721,31 @@
             return true;
     }
 
+    if (getExtensions().geometryShader)
+    {
+        switch (pname)
+        {
+            case GL_MAX_FRAMEBUFFER_LAYERS_EXT:
+            case GL_LAYER_PROVOKING_VERTEX_EXT:
+            case GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT:
+            case GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT:
+            case GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT:
+            case GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT:
+            case GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT:
+            case GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT:
+            case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT:
+            case GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT:
+            case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT:
+            case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT:
+            case GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT:
+            case GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT:
+            case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT:
+                *type      = GL_INT;
+                *numParams = 1;
+                return true;
+        }
+    }
+
     return false;
 }
 
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 7b7a680..7878eea 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -1017,20 +1017,69 @@
             QuerySingleGLInt(functions, GL_MAX_RECTANGLE_TEXTURE_SIZE_ANGLE);
     }
 
-    // We need at least OpenGL 4.3 to support all features and constants defined in
+    // OpenGL 4.3 (and above) can support all features and constants defined in
     // GL_EXT_geometry_shader.
     if (functions->isAtLeastGL(gl::Version(4, 3)) || functions->isAtLeastGLES(gl::Version(3, 2)) ||
         functions->hasGLESExtension("GL_OES_geometry_shader") ||
-        functions->hasGLESExtension("GL_EXT_geometry_shader"))
+        functions->hasGLESExtension("GL_EXT_geometry_shader") ||
+        // OpenGL 4.0 adds the support for instanced geometry shader
+        // GL_ARB_shader_atomic_counters adds atomic counters to geometry shader
+        // GL_ARB_shader_storage_buffer_object adds shader storage buffers to geometry shader
+        // GL_ARB_shader_image_load_store adds images to geometry shader
+        (functions->isAtLeastGL(gl::Version(4, 0)) &&
+         functions->hasGLExtension("GL_ARB_shader_atomic_counters") &&
+         functions->hasGLExtension("GL_ARB_shader_storage_buffer_object") &&
+         functions->hasGLExtension("GL_ARB_shader_image_load_store")))
     {
         extensions->geometryShader = true;
 
-        // TODO(jiawei.shao@intel.com): initialize all implementation dependent geometry shader
-        // limits.
-        extensions->maxGeometryOutputVertices =
+        caps->maxFramebufferLayers = QuerySingleGLInt(functions, GL_MAX_FRAMEBUFFER_LAYERS_EXT);
+
+        // GL_PROVOKING_VERTEX isn't a valid return value of GL_LAYER_PROVOKING_VERTEX_EXT in
+        // GL_EXT_geometry_shader SPEC, however it is legal in desktop OpenGL, which means the value
+        // follows the one set by glProvokingVertex.
+        // [OpenGL 4.3] Chapter 11.3.4.6
+        // The vertex conventions followed for gl_Layer and gl_ViewportIndex may be determined by
+        // calling GetIntegerv with the symbolic constants LAYER_PROVOKING_VERTEX and
+        // VIEWPORT_INDEX_PROVOKING_VERTEX, respectively. For either query, if the value returned is
+        // PROVOKING_VERTEX, then vertex selection follows the convention specified by
+        // ProvokingVertex.
+        caps->layerProvokingVertex = QuerySingleGLInt(functions, GL_LAYER_PROVOKING_VERTEX_EXT);
+        if (caps->layerProvokingVertex == GL_PROVOKING_VERTEX)
+        {
+            // We should use GL_LAST_VERTEX_CONVENTION_EXT instead because desktop OpenGL SPEC
+            // requires the initial value of provoking vertex mode is LAST_VERTEX_CONVENTION.
+            // [OpenGL 4.3] Chapter 13.4
+            // The initial value of the provoking vertex mode is LAST_VERTEX_CONVENTION.
+            caps->layerProvokingVertex = GL_LAST_VERTEX_CONVENTION_EXT;
+        }
+
+        caps->maxGeometryUniformComponents =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT);
+        caps->maxGeometryUniformBlocks =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT);
+        caps->maxCombinedGeometryUniformComponents =
+            QuerySingleGLInt(functions, GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_EXT);
+        caps->maxGeometryInputComponents =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT);
+        caps->maxGeometryOutputComponents =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT);
+        caps->maxGeometryOutputVertices =
             QuerySingleGLInt(functions, GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT);
-        extensions->maxGeometryShaderInvocations =
+        caps->maxGeometryTotalOutputComponents =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT);
+        caps->maxGeometryShaderInvocations =
             QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT);
+        caps->maxGeometryTextureImageUnits =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT);
+        caps->maxGeometryAtomicCounterBuffers =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT);
+        caps->maxGeometryAtomicCounters =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT);
+        caps->maxGeometryImageUniforms =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT);
+        caps->maxGeometryShaderStorageBlocks =
+            QuerySingleGLInt(functions, GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT);
     }
 }
 
diff --git a/src/libANGLE/renderer/null/ContextNULL.cpp b/src/libANGLE/renderer/null/ContextNULL.cpp
index a8da191..818eb82 100644
--- a/src/libANGLE/renderer/null/ContextNULL.cpp
+++ b/src/libANGLE/renderer/null/ContextNULL.cpp
@@ -87,6 +87,7 @@
     mExtensions.textureCompressionASTCLDR  = true;
     mExtensions.compressedETC1RGB8Texture  = true;
     mExtensions.lossyETCDecode             = true;
+    mExtensions.geometryShader             = true;
 
     const gl::Version maxClientVersion(3, 1);
     mCaps = GenerateMinimumCaps(maxClientVersion, mExtensions);
diff --git a/src/tests/gl_tests/GeometryShaderTest.cpp b/src/tests/gl_tests/GeometryShaderTest.cpp
index 62bf205..bf55d06 100644
--- a/src/tests/gl_tests/GeometryShaderTest.cpp
+++ b/src/tests/gl_tests/GeometryShaderTest.cpp
@@ -69,6 +69,64 @@
     EXPECT_GL_NO_ERROR();
 }
 
+// Verify that all the implementation dependent geometry shader related resource limits meet the
+// requirement of GL_EXT_geometry_shader SPEC.
+TEST_P(GeometryShaderTest, GeometryShaderImplementationDependentLimits)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    const std::map<GLenum, int> limits = {{GL_MAX_FRAMEBUFFER_LAYERS_EXT, 256},
+                                          {GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_EXT, 1024},
+                                          {GL_MAX_GEOMETRY_UNIFORM_BLOCKS_EXT, 12},
+                                          {GL_MAX_GEOMETRY_INPUT_COMPONENTS_EXT, 64},
+                                          {GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_EXT, 64},
+                                          {GL_MAX_GEOMETRY_OUTPUT_VERTICES_EXT, 256},
+                                          {GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_EXT, 1024},
+                                          {GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_EXT, 16},
+                                          {GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_EXT, 0},
+                                          {GL_MAX_GEOMETRY_ATOMIC_COUNTERS_EXT, 0},
+                                          {GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_EXT, 0},
+                                          {GL_MAX_GEOMETRY_IMAGE_UNIFORMS_EXT, 0},
+                                          {GL_MAX_GEOMETRY_SHADER_INVOCATIONS_EXT, 32}};
+
+    GLint value;
+    for (const auto &limit : limits)
+    {
+        value = 0;
+        glGetIntegerv(limit.first, &value);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GE(value, limit.second);
+    }
+
+    value = 0;
+    glGetIntegerv(GL_LAYER_PROVOKING_VERTEX_EXT, &value);
+    EXPECT_GL_NO_ERROR();
+    EXPECT_TRUE(value == GL_FIRST_VERTEX_CONVENTION_EXT || value == GL_LAST_VERTEX_CONVENTION_EXT ||
+                value == GL_UNDEFINED_VERTEX_EXT);
+}
+
+// Verify that all the combined resource limits meet the requirement of GL_EXT_geometry_shader SPEC.
+TEST_P(GeometryShaderTest, CombinedResourceLimits)
+{
+    ANGLE_SKIP_TEST_IF(!extensionEnabled("GL_EXT_geometry_shader"));
+
+    // See http://anglebug.com/2261.
+    ANGLE_SKIP_TEST_IF(IsAndroid());
+
+    const std::map<GLenum, int> limits = {{GL_MAX_UNIFORM_BUFFER_BINDINGS, 48},
+                                          {GL_MAX_COMBINED_UNIFORM_BLOCKS, 36},
+                                          {GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, 64}};
+
+    GLint value;
+    for (const auto &limit : limits)
+    {
+        value = 0;
+        glGetIntegerv(limit.first, &value);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_GE(value, limit.second);
+    }
+}
+
 ANGLE_INSTANTIATE_TEST(GeometryShaderTestES3, ES3_OPENGL(), ES3_OPENGLES(), ES3_D3D11());
 ANGLE_INSTANTIATE_TEST(GeometryShaderTest, ES31_OPENGL(), ES31_OPENGLES(), ES31_D3D11());
 }