Cache valid draw modes with transform feedback.

Enabling transform feedback can affect which draw modes are valid. We
can use the exiting draw modes cache to save having to check the draw
modes twice. We update the cached draw modes on any change to the
transform feedback activity state. e.g. when transform feedback is
started, or resumed.

There are also spec changes that comes into effect in ES 3.2 or when
EXT_geometry_shader is enabled. Again we cache these draw modes in the
packed valid draw modes map.

Will allow for faster validation for draw calls once the other checks
for transform feedback are optimized. Also adds a new regression test.

Bug: angleproject:2966
Change-Id: Iab901e45aab70980b9e631ec8383fdeadbd32368
Reviewed-on: https://chromium-review.googlesource.com/c/1357149
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 772af30..390b58d 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -8355,6 +8355,7 @@
 void StateCache::onActiveTransformFeedbackChange(Context *context)
 {
     updateBasicDrawStatesError();
+    updateValidDrawModes(context);
 }
 
 void StateCache::onUniformBufferStateChange(Context *context)
@@ -8367,33 +8368,79 @@
     updateBasicDrawStatesError();
 }
 
+void StateCache::setValidDrawModes(bool pointsOK,
+                                   bool linesOK,
+                                   bool trisOK,
+                                   bool lineAdjOK,
+                                   bool triAdjOK)
+{
+    mCachedValidDrawModes[PrimitiveMode::Points]                 = pointsOK;
+    mCachedValidDrawModes[PrimitiveMode::Lines]                  = linesOK;
+    mCachedValidDrawModes[PrimitiveMode::LineStrip]              = linesOK;
+    mCachedValidDrawModes[PrimitiveMode::LineLoop]               = linesOK;
+    mCachedValidDrawModes[PrimitiveMode::Triangles]              = trisOK;
+    mCachedValidDrawModes[PrimitiveMode::TriangleFan]            = trisOK;
+    mCachedValidDrawModes[PrimitiveMode::TriangleStrip]          = trisOK;
+    mCachedValidDrawModes[PrimitiveMode::LinesAdjacency]         = lineAdjOK;
+    mCachedValidDrawModes[PrimitiveMode::LineStripAdjacency]     = lineAdjOK;
+    mCachedValidDrawModes[PrimitiveMode::TrianglesAdjacency]     = triAdjOK;
+    mCachedValidDrawModes[PrimitiveMode::TriangleStripAdjacency] = triAdjOK;
+}
+
 void StateCache::updateValidDrawModes(Context *context)
 {
-    Program *program = context->getGLState().getProgram();
+    const State &state = context->getGLState();
+    Program *program   = state.getProgram();
+
+    TransformFeedback *curTransformFeedback = state.getCurrentTransformFeedback();
+    if (curTransformFeedback && curTransformFeedback->isActive() &&
+        !curTransformFeedback->isPaused())
+    {
+        // ES Spec 3.0 validation text:
+        // When transform feedback is active and not paused, all geometric primitives generated must
+        // match the value of primitiveMode passed to BeginTransformFeedback. The error
+        // INVALID_OPERATION is generated by DrawArrays and DrawArraysInstanced if mode is not
+        // identical to primitiveMode. The error INVALID_OPERATION is also generated by
+        // DrawElements, DrawElementsInstanced, and DrawRangeElements while transform feedback is
+        // active and not paused, regardless of mode. Any primitive type may be used while transform
+        // feedback is paused.
+        if (!context->getExtensions().geometryShader)
+        {
+            mCachedValidDrawModes.fill(false);
+            mCachedValidDrawModes[curTransformFeedback->getPrimitiveMode()] = true;
+            return;
+        }
+
+        // EXT_geometry_shader validation text:
+        // When transform feedback is active and not paused, all geometric primitives generated must
+        // be compatible with the value of <primitiveMode> passed to BeginTransformFeedback. If a
+        // geometry shader is active, the type of primitive emitted by that shader is used instead
+        // of the <mode> parameter passed to drawing commands for the purposes of this error check.
+        // Any primitive type may be used while transform feedback is paused.
+        bool pointsOK = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Points;
+        bool linesOK  = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Lines;
+        bool trisOK   = curTransformFeedback->getPrimitiveMode() == PrimitiveMode::Triangles;
+
+        setValidDrawModes(pointsOK, linesOK, trisOK, false, false);
+        return;
+    }
+
     if (!program || !program->hasLinkedShaderStage(ShaderType::Geometry))
     {
         mCachedValidDrawModes = kValidBasicDrawModes;
+        return;
     }
-    else
-    {
-        ASSERT(program && program->hasLinkedShaderStage(ShaderType::Geometry));
 
-        PrimitiveMode gsMode = program->getGeometryShaderInputPrimitiveType();
+    ASSERT(program && program->hasLinkedShaderStage(ShaderType::Geometry));
+    PrimitiveMode gsMode = program->getGeometryShaderInputPrimitiveType();
 
-        mCachedValidDrawModes = {{
-            {PrimitiveMode::Points, gsMode == PrimitiveMode::Points},
-            {PrimitiveMode::Lines, gsMode == PrimitiveMode::Lines},
-            {PrimitiveMode::LineLoop, gsMode == PrimitiveMode::Lines},
-            {PrimitiveMode::LineStrip, gsMode == PrimitiveMode::Lines},
-            {PrimitiveMode::Triangles, gsMode == PrimitiveMode::Triangles},
-            {PrimitiveMode::TriangleStrip, gsMode == PrimitiveMode::Triangles},
-            {PrimitiveMode::TriangleFan, gsMode == PrimitiveMode::Triangles},
-            {PrimitiveMode::LinesAdjacency, gsMode == PrimitiveMode::LinesAdjacency},
-            {PrimitiveMode::LineStripAdjacency, gsMode == PrimitiveMode::LinesAdjacency},
-            {PrimitiveMode::TrianglesAdjacency, gsMode == PrimitiveMode::TrianglesAdjacency},
-            {PrimitiveMode::TriangleStripAdjacency, gsMode == PrimitiveMode::TrianglesAdjacency},
-        }};
-    }
+    bool pointsOK  = gsMode == PrimitiveMode::Points;
+    bool linesOK   = gsMode == PrimitiveMode::Lines;
+    bool trisOK    = gsMode == PrimitiveMode::Triangles;
+    bool lineAdjOK = gsMode == PrimitiveMode::LinesAdjacency;
+    bool triAdjOK  = gsMode == PrimitiveMode::TrianglesAdjacency;
+
+    setValidDrawModes(pointsOK, linesOK, trisOK, lineAdjOK, triAdjOK);
 }
 
 void StateCache::updateValidBindTextureTypes(Context *context)