ES31: Add extra transform feedback rules in EXT_geometry_shader

This patch adds the additional transform feedback rules required in
EXT_geometry_shader. In this extension, more draw commands and render
primitives are allowed for transform feedback.

BUG=angleproject:1941
TEST=dEQP-GLES31.functional.geometry_shading.vertex_transform_feedback.*
     angle_end2end_tests

Change-Id: Iedc27dca5c24ca45cd4226a1a0066107c0b40e1d
Reviewed-on: https://chromium-review.googlesource.com/1055192
Reviewed-by: Jiajia Qin <jiajia.qin@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Commit-Queue: Jiawei Shao <jiawei.shao@intel.com>
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index b5fc320..81a88d2 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -211,6 +211,8 @@
        "It is undefined behavior to use an uniform buffer that is bound for transform feedback.");
 ERRMSG(UniformSizeMismatch, "Uniform size does not match uniform method.");
 ERRMSG(UnknownParameter, "Unknown parameter value.");
+ERRMSG(UnsupportedDrawModeForTransformFeedback,
+       "The draw command is unsupported when transform feedback is active and not paused.");
 ERRMSG(VertexArrayNoBuffer, "An enabled vertex array has no buffer.");
 ERRMSG(VertexArrayNoBufferPointer, "An enabled vertex array has no buffer and no pointer.");
 ERRMSG(VertexBufferBoundForTransformFeedback,
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index f8d213b..9f25a53 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -634,6 +634,38 @@
     }
 }
 
+bool ValidateTransformFeedbackPrimitiveMode(const Context *context,
+                                            GLenum transformFeedbackPrimitiveMode,
+                                            GLenum renderPrimitiveMode)
+{
+    ASSERT(context);
+
+    if (!context->getExtensions().geometryShader)
+    {
+        // It is an invalid operation to call DrawArrays or DrawArraysInstanced with a draw mode
+        // that does not match the current transform feedback object's draw mode (if transform
+        // feedback is active), (3.0.2, section 2.14, pg 86)
+        return transformFeedbackPrimitiveMode == renderPrimitiveMode;
+    }
+
+    // [GL_EXT_geometry_shader] Table 12.1gs
+    switch (transformFeedbackPrimitiveMode)
+    {
+        case GL_POINTS:
+            return renderPrimitiveMode == GL_POINTS;
+        case GL_TRIANGLES:
+            return renderPrimitiveMode == GL_TRIANGLES ||
+                   renderPrimitiveMode == GL_TRIANGLE_STRIP ||
+                   renderPrimitiveMode == GL_TRIANGLE_FAN;
+        case GL_LINES:
+            return renderPrimitiveMode == GL_LINES || renderPrimitiveMode == GL_LINE_LOOP ||
+                   renderPrimitiveMode == GL_LINE_STRIP;
+        default:
+            UNREACHABLE();
+            return false;
+    }
+}
+
 bool ValidateDrawElementsInstancedBase(Context *context,
                                        GLenum mode,
                                        GLsizei count,
@@ -2845,12 +2877,9 @@
     if (curTransformFeedback && curTransformFeedback->isActive() &&
         !curTransformFeedback->isPaused())
     {
-        if (curTransformFeedback->getPrimitiveMode() != mode)
+        if (!ValidateTransformFeedbackPrimitiveMode(context,
+                                                    curTransformFeedback->getPrimitiveMode(), mode))
         {
-            // It is an invalid operation to call DrawArrays or DrawArraysInstanced with a draw mode
-            // that does not match the current transform feedback object's draw mode (if transform
-            // feedback
-            // is active), (3.0.2, section 2.14, pg 86)
             ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDrawModeTransformFeedback);
             return false;
         }
@@ -2910,7 +2939,7 @@
     return ValidateDrawInstancedANGLE(context);
 }
 
-bool ValidateDrawElementsBase(Context *context, GLenum type)
+bool ValidateDrawElementsBase(Context *context, GLenum mode, GLenum type)
 {
     switch (type)
     {
@@ -2935,11 +2964,26 @@
     if (curTransformFeedback && curTransformFeedback->isActive() &&
         !curTransformFeedback->isPaused())
     {
-        // It is an invalid operation to call DrawElements, DrawRangeElements or
-        // DrawElementsInstanced
-        // while transform feedback is active, (3.0.2, section 2.14, pg 86)
-        context->handleError(InvalidOperation());
-        return false;
+        // EXT_geometry_shader allows transform feedback to work with all draw commands.
+        // [EXT_geometry_shader] Section 12.1, "Transform Feedback"
+        if (context->getExtensions().geometryShader)
+        {
+            if (!ValidateTransformFeedbackPrimitiveMode(
+                    context, curTransformFeedback->getPrimitiveMode(), mode))
+            {
+                ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDrawModeTransformFeedback);
+                return false;
+            }
+        }
+        else
+        {
+            // It is an invalid operation to call DrawElements, DrawRangeElements or
+            // DrawElementsInstanced while transform feedback is active, (3.0.2, section 2.14, pg
+            // 86)
+            ANGLE_VALIDATION_ERR(context, InvalidOperation(),
+                                 UnsupportedDrawModeForTransformFeedback);
+            return false;
+        }
     }
 
     return true;
@@ -2952,7 +2996,7 @@
                                 const void *indices,
                                 GLsizei primcount)
 {
-    if (!ValidateDrawElementsBase(context, type))
+    if (!ValidateDrawElementsBase(context, mode, type))
         return false;
 
     const State &state = context->getGLState();
diff --git a/src/libANGLE/validationES.h b/src/libANGLE/validationES.h
index f66484f..83d8281 100644
--- a/src/libANGLE/validationES.h
+++ b/src/libANGLE/validationES.h
@@ -289,7 +289,7 @@
                                       GLsizei count,
                                       GLsizei primcount);
 
-bool ValidateDrawElementsBase(Context *context, GLenum type);
+bool ValidateDrawElementsBase(Context *context, GLenum mode, GLenum type);
 bool ValidateDrawElementsCommon(Context *context,
                                 GLenum mode,
                                 GLsizei count,
@@ -679,6 +679,10 @@
 
 bool ValidateMultitextureUnit(Context *context, GLenum texture);
 
+bool ValidateTransformFeedbackPrimitiveMode(const Context *context,
+                                            GLenum transformFeedbackPrimitiveMode,
+                                            GLenum renderPrimitiveMode);
+
 // Utility macro for handling implementation methods inside Validation.
 #define ANGLE_HANDLE_VALIDATION_ERR(X) \
     context->handleError(X);           \
diff --git a/src/libANGLE/validationES31.cpp b/src/libANGLE/validationES31.cpp
index b85b7a6..5069df8 100644
--- a/src/libANGLE/validationES31.cpp
+++ b/src/libANGLE/validationES31.cpp
@@ -475,9 +475,25 @@
     if (curTransformFeedback && curTransformFeedback->isActive() &&
         !curTransformFeedback->isPaused())
     {
-        // An INVALID_OPERATION error is generated if transform feedback is active and not paused.
-        context->handleError(InvalidOperation() << "transform feedback is active and not paused.");
-        return false;
+        // EXT_geometry_shader allows transform feedback to work with all draw commands.
+        // [EXT_geometry_shader] Section 12.1, "Transform Feedback"
+        if (context->getExtensions().geometryShader)
+        {
+            if (!ValidateTransformFeedbackPrimitiveMode(
+                    context, curTransformFeedback->getPrimitiveMode(), mode))
+            {
+                ANGLE_VALIDATION_ERR(context, InvalidOperation(), InvalidDrawModeTransformFeedback);
+                return false;
+            }
+        }
+        else
+        {
+            // An INVALID_OPERATION error is generated if transform feedback is active and not
+            // paused.
+            ANGLE_VALIDATION_ERR(context, InvalidOperation(),
+                                 UnsupportedDrawModeForTransformFeedback);
+            return false;
+        }
     }
 
     if (!ValidateDrawIndirectBase(context, mode, indirect))
@@ -502,8 +518,10 @@
 
 bool ValidateDrawElementsIndirect(Context *context, GLenum mode, GLenum type, const void *indirect)
 {
-    if (!ValidateDrawElementsBase(context, type))
+    if (!ValidateDrawElementsBase(context, mode, type))
+    {
         return false;
+    }
 
     const State &state             = context->getGLState();
     const VertexArray *vao         = state.getVertexArray();
diff --git a/src/tests/deqp_support/deqp_gles31_test_expectations.txt b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
index 87ef95a..b7b5d4d 100644
--- a/src/tests/deqp_support/deqp_gles31_test_expectations.txt
+++ b/src/tests/deqp_support/deqp_gles31_test_expectations.txt
@@ -53,8 +53,6 @@
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.layered.* = SKIP
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.invocation_per_layer_* = SKIP
 1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.multiple_layers_per_invocation_* = SKIP
-// TODO(jiawei.shao@intel.com): Implement validations on transform feedback required in OpenGL ES 3.1 extension GL_EXT_geometry_shader
-1941 OPENGL D3D11 : dEQP-GLES31.functional.geometry_shading.vertex_transform_feedback.* = SKIP
 
 // D3D11 Failing Tests
 1442 D3D11 : dEQP-GLES31.functional.state_query.integer.max_compute_shared_memory_size_* = FAIL
@@ -1576,6 +1574,7 @@
 1941 D3D11 : dEQP-GLES31.functional.geometry_shading.instanced.draw_* = FAIL
 1941 D3D11 : dEQP-GLES31.functional.geometry_shading.input.* = FAIL
 1941 D3D11 : dEQP-GLES31.functional.geometry_shading.negative.* = FAIL
+1941 D3D11 : dEQP-GLES31.functional.geometry_shading.vertex_transform_feedback.* = FAIL
 1941 D3D11 : dEQP-GLES31.functional.shaders.linkage.es31.geometry.* = FAIL
 1941 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.callbacks.shader_directive.geometry_shader = FAIL
 1941 D3D11 : dEQP-GLES31.functional.debug.negative_coverage.log.shader_directive.geometry_shader = FAIL