Refactor transform feedback EPs.

BUG=angleproject:747

Change-Id: I4891966cd7b3d478980202e795742e15dd1dcb01
Reviewed-on: https://chromium-review.googlesource.com/637125
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 9b112a4..6d9fddc 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -374,7 +374,7 @@
         // In the initial state, a default transform feedback object is bound and treated as
         // a transform feedback object with a name of zero. That object is bound any time
         // BindTransformFeedback is called with id of zero
-        bindTransformFeedback(0);
+        bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
     }
 
     // Initialize dirty bit masks
@@ -629,13 +629,6 @@
     return mState.mSamplers->createSampler();
 }
 
-GLuint Context::createTransformFeedback()
-{
-    GLuint transformFeedback = mTransformFeedbackHandleAllocator.allocate();
-    mTransformFeedbackMap.assign(transformFeedback, nullptr);
-    return transformFeedback;
-}
-
 // Returns an unused framebuffer name
 GLuint Context::createFramebuffer()
 {
@@ -799,26 +792,6 @@
     mState.mSamplers->deleteObject(this, sampler);
 }
 
-void Context::deleteTransformFeedback(GLuint transformFeedback)
-{
-    if (transformFeedback == 0)
-    {
-        return;
-    }
-
-    TransformFeedback *transformFeedbackObject = nullptr;
-    if (mTransformFeedbackMap.erase(transformFeedback, &transformFeedbackObject))
-    {
-        if (transformFeedbackObject != nullptr)
-        {
-            detachTransformFeedback(transformFeedback);
-            transformFeedbackObject->release(this);
-        }
-
-        mTransformFeedbackHandleAllocator.release(transformFeedback);
-    }
-}
-
 void Context::deleteFramebuffer(GLuint framebuffer)
 {
     if (mState.mFramebuffers->getFramebuffer(framebuffer))
@@ -1131,8 +1104,9 @@
     mGLState.setProgram(this, getProgram(program));
 }
 
-void Context::bindTransformFeedback(GLuint transformFeedbackHandle)
+void Context::bindTransformFeedback(GLenum target, GLuint transformFeedbackHandle)
 {
+    ASSERT(target == GL_TRANSFORM_FEEDBACK);
     TransformFeedback *transformFeedback =
         checkTransformFeedbackAllocation(transformFeedbackHandle);
     mGLState.setTransformFeedbackBinding(this, transformFeedback);
@@ -2383,7 +2357,7 @@
     // VAOs and FBOs and set the current bound transform feedback back to 0.
     if (mGLState.removeTransformFeedbackBinding(this, transformFeedback))
     {
-        bindTransformFeedback(0);
+        bindTransformFeedback(GL_TRANSFORM_FEEDBACK, 0);
     }
 }
 
@@ -5032,4 +5006,92 @@
     return (vao != nullptr ? GL_TRUE : GL_FALSE);
 }
 
+void Context::endTransformFeedback()
+{
+    TransformFeedback *transformFeedback = mGLState.getCurrentTransformFeedback();
+    transformFeedback->end(this);
+}
+
+void Context::transformFeedbackVaryings(GLuint program,
+                                        GLsizei count,
+                                        const GLchar *const *varyings,
+                                        GLenum bufferMode)
+{
+    Program *programObject = getProgram(program);
+    ASSERT(programObject);
+    programObject->setTransformFeedbackVaryings(count, varyings, bufferMode);
+}
+
+void Context::getTransformFeedbackVarying(GLuint program,
+                                          GLuint index,
+                                          GLsizei bufSize,
+                                          GLsizei *length,
+                                          GLsizei *size,
+                                          GLenum *type,
+                                          GLchar *name)
+{
+    Program *programObject = getProgram(program);
+    ASSERT(programObject);
+    programObject->getTransformFeedbackVarying(index, bufSize, length, size, type, name);
+}
+
+void Context::deleteTransformFeedbacks(GLsizei n, const GLuint *ids)
+{
+    for (int i = 0; i < n; i++)
+    {
+        GLuint transformFeedback = ids[i];
+        if (transformFeedback == 0)
+        {
+            continue;
+        }
+
+        TransformFeedback *transformFeedbackObject = nullptr;
+        if (mTransformFeedbackMap.erase(transformFeedback, &transformFeedbackObject))
+        {
+            if (transformFeedbackObject != nullptr)
+            {
+                detachTransformFeedback(transformFeedback);
+                transformFeedbackObject->release(this);
+            }
+
+            mTransformFeedbackHandleAllocator.release(transformFeedback);
+        }
+    }
+}
+
+void Context::genTransformFeedbacks(GLsizei n, GLuint *ids)
+{
+    for (int i = 0; i < n; i++)
+    {
+        GLuint transformFeedback = mTransformFeedbackHandleAllocator.allocate();
+        mTransformFeedbackMap.assign(transformFeedback, nullptr);
+        ids[i] = transformFeedback;
+    }
+}
+
+bool Context::isTransformFeedback(GLuint id)
+{
+    if (id == 0)
+    {
+        // The 3.0.4 spec [section 6.1.11] states that if ID is zero, IsTransformFeedback
+        // returns FALSE
+        return GL_FALSE;
+    }
+
+    const TransformFeedback *transformFeedback = getTransformFeedback(id);
+    return ((transformFeedback != nullptr) ? GL_TRUE : GL_FALSE);
+}
+
+void Context::pauseTransformFeedback()
+{
+    TransformFeedback *transformFeedback = mGLState.getCurrentTransformFeedback();
+    transformFeedback->pause();
+}
+
+void Context::resumeTransformFeedback()
+{
+    TransformFeedback *transformFeedback = mGLState.getCurrentTransformFeedback();
+    transformFeedback->resume();
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/Context.h b/src/libANGLE/Context.h
index 2dd7736..6250bed 100644
--- a/src/libANGLE/Context.h
+++ b/src/libANGLE/Context.h
@@ -85,7 +85,6 @@
     GLuint createTexture();
     GLuint createRenderbuffer();
     GLuint createSampler();
-    GLuint createTransformFeedback();
     GLsync createFenceSync();
     GLuint createPaths(GLsizei range);
 
@@ -95,7 +94,6 @@
     void deleteTexture(GLuint texture);
     void deleteRenderbuffer(GLuint renderbuffer);
     void deleteSampler(GLuint sampler);
-    void deleteTransformFeedback(GLuint transformFeedback);
     void deleteFenceSync(GLsync fenceSync);
     void deletePaths(GLuint first, GLsizei range);
 
@@ -163,7 +161,7 @@
     void bindPixelPackBuffer(GLuint bufferHandle);
     void bindPixelUnpackBuffer(GLuint bufferHandle);
     void useProgram(GLuint program);
-    void bindTransformFeedback(GLuint transformFeedbackHandle);
+    void bindTransformFeedback(GLenum target, GLuint transformFeedbackHandle);
     void bindDrawIndirectBuffer(GLuint bufferHandle);
 
     void beginQuery(GLenum target, GLuint query);
@@ -814,6 +812,25 @@
     void genVertexArrays(GLsizei n, GLuint *arrays);
     bool isVertexArray(GLuint array);
 
+    void endTransformFeedback();
+    void transformFeedbackVaryings(GLuint program,
+                                   GLsizei count,
+                                   const GLchar *const *varyings,
+                                   GLenum bufferMode);
+    void getTransformFeedbackVarying(GLuint program,
+                                     GLuint index,
+                                     GLsizei bufSize,
+                                     GLsizei *length,
+                                     GLsizei *size,
+                                     GLenum *type,
+                                     GLchar *name);
+
+    void deleteTransformFeedbacks(GLsizei n, const GLuint *ids);
+    void genTransformFeedbacks(GLsizei n, GLuint *ids);
+    bool isTransformFeedback(GLuint id);
+    void pauseTransformFeedback();
+    void resumeTransformFeedback();
+
     void getProgramBinary(GLuint program,
                           GLsizei bufSize,
                           GLsizei *length,
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index b9a662c..d2c37c5 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -147,6 +147,7 @@
 ERRMSG(StrideMustBeMultipleOfType, "Stride must be a multiple of the passed in datatype.");
 ERRMSG(TextureNotBound, "A texture must be bound.");
 ERRMSG(TextureNotPow2, "The texture is a non-power-of-two texture.");
+ERRMSG(TransformFeedbackDoesNotExist, "Transform feedback object that does not exist.");
 ERRMSG(TypeMismatch,
        "Passed in texture target and format must match the one originally used to define the "
        "texture.");
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 4463108..f07d29f 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -2914,4 +2914,200 @@
     return ValidateUniformMatrixES3(context, GL_FLOAT_MAT4x3, location, count, transpose);
 }
 
+bool ValidateEndTransformFeedback(Context *context)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+    ASSERT(transformFeedback != nullptr);
+
+    if (!transformFeedback->isActive())
+    {
+        context->handleError(InvalidOperation());
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateTransformFeedbackVaryings(Context *context,
+                                       GLuint program,
+                                       GLsizei count,
+                                       const GLchar *const *varyings,
+                                       GLenum bufferMode)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    if (count < 0)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeCount);
+        return false;
+    }
+
+    switch (bufferMode)
+    {
+        case GL_INTERLEAVED_ATTRIBS:
+            break;
+        case GL_SEPARATE_ATTRIBS:
+        {
+            const Caps &caps = context->getCaps();
+            if (static_cast<GLuint>(count) > caps.maxTransformFeedbackSeparateAttributes)
+            {
+                context->handleError(InvalidValue());
+                return false;
+            }
+            break;
+        }
+        default:
+            context->handleError(InvalidEnum());
+            return false;
+    }
+
+    Program *programObject = GetValidProgram(context, program);
+    if (!programObject)
+    {
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateGetTransformFeedbackVarying(Context *context,
+                                         GLuint program,
+                                         GLuint index,
+                                         GLsizei bufSize,
+                                         GLsizei *length,
+                                         GLsizei *size,
+                                         GLenum *type,
+                                         GLchar *name)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    if (bufSize < 0)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidValue(), NegativeBufferSize);
+        return false;
+    }
+
+    Program *programObject = GetValidProgram(context, program);
+    if (!programObject)
+    {
+        return false;
+    }
+
+    if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
+    {
+        context->handleError(InvalidValue());
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateBindTransformFeedback(Context *context, GLenum target, GLuint id)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    switch (target)
+    {
+        case GL_TRANSFORM_FEEDBACK:
+        {
+            // Cannot bind a transform feedback object if the current one is started and not
+            // paused (3.0.2 pg 85 section 2.14.1)
+            TransformFeedback *curTransformFeedback =
+                context->getGLState().getCurrentTransformFeedback();
+            if (curTransformFeedback && curTransformFeedback->isActive() &&
+                !curTransformFeedback->isPaused())
+            {
+                context->handleError(InvalidOperation());
+                return false;
+            }
+
+            // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section
+            // 2.14.1)
+            if (!context->isTransformFeedbackGenerated(id))
+            {
+                ANGLE_VALIDATION_ERR(context, InvalidOperation(), TransformFeedbackDoesNotExist);
+                return false;
+            }
+        }
+        break;
+
+        default:
+            context->handleError(InvalidEnum());
+            return false;
+    }
+
+    return true;
+}
+
+bool ValidateIsTransformFeedback(Context *context, GLuint id)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidatePauseTransformFeedback(Context *context)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+    ASSERT(transformFeedback != nullptr);
+
+    // Current transform feedback must be active and not paused in order to pause (3.0.2 pg 86)
+    if (!transformFeedback->isActive() || transformFeedback->isPaused())
+    {
+        context->handleError(InvalidOperation());
+        return false;
+    }
+
+    return true;
+}
+
+bool ValidateResumeTransformFeedback(Context *context)
+{
+    if (context->getClientMajorVersion() < 3)
+    {
+        ANGLE_VALIDATION_ERR(context, InvalidOperation(), ES3Required);
+        return false;
+    }
+
+    TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
+    ASSERT(transformFeedback != nullptr);
+
+    // Current transform feedback must be active and paused in order to resume (3.0.2 pg 86)
+    if (!transformFeedback->isActive() || !transformFeedback->isPaused())
+    {
+        context->handleError(InvalidOperation());
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace gl
diff --git a/src/libANGLE/validationES3.h b/src/libANGLE/validationES3.h
index 4a9b34c..7f62f8c 100644
--- a/src/libANGLE/validationES3.h
+++ b/src/libANGLE/validationES3.h
@@ -491,6 +491,25 @@
                                 GLboolean transpose,
                                 const GLfloat *value);
 
+bool ValidateEndTransformFeedback(Context *context);
+bool ValidateTransformFeedbackVaryings(Context *context,
+                                       GLuint program,
+                                       GLsizei count,
+                                       const GLchar *const *varyings,
+                                       GLenum bufferMode);
+bool ValidateGetTransformFeedbackVarying(Context *context,
+                                         GLuint program,
+                                         GLuint index,
+                                         GLsizei bufSize,
+                                         GLsizei *length,
+                                         GLsizei *size,
+                                         GLenum *type,
+                                         GLchar *name);
+bool ValidateBindTransformFeedback(Context *context, GLenum target, GLuint id);
+bool ValidateIsTransformFeedback(Context *context, GLuint id);
+bool ValidatePauseTransformFeedback(Context *context);
+bool ValidateResumeTransformFeedback(Context *context);
+
 }  // namespace gl
 
 #endif  // LIBANGLE_VALIDATION_ES3_H_
diff --git a/src/libGLESv2/entry_points_gles_3_0.cpp b/src/libGLESv2/entry_points_gles_3_0.cpp
index e194cca..e89796c 100644
--- a/src/libGLESv2/entry_points_gles_3_0.cpp
+++ b/src/libGLESv2/entry_points_gles_3_0.cpp
@@ -735,6 +735,7 @@
         {
             return;
         }
+
         context->getIntegeri_v(target, index, data);
     }
 }
@@ -762,22 +763,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
+        if (!context->skipValidation() && !ValidateEndTransformFeedback(context))
         {
-            context->handleError(InvalidOperation());
             return;
         }
 
-        TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
-        ASSERT(transformFeedback != nullptr);
-
-        if (!transformFeedback->isActive())
-        {
-            context->handleError(InvalidOperation());
-            return;
-        }
-
-        transformFeedback->end(context);
+        context->endTransformFeedback();
     }
 }
 
@@ -797,6 +788,7 @@
         {
             return;
         }
+
         context->bindBufferRange(target, index, buffer, offset, size);
     }
 }
@@ -812,6 +804,7 @@
         {
             return;
         }
+
         context->bindBufferBase(target, index, buffer);
     }
 }
@@ -829,42 +822,13 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
-        {
-            context->handleError(InvalidOperation());
-            return;
-        }
-
-        if (count < 0)
-        {
-            context->handleError(InvalidValue());
-            return;
-        }
-
-        const Caps &caps = context->getCaps();
-        switch (bufferMode)
-        {
-            case GL_INTERLEAVED_ATTRIBS:
-                break;
-            case GL_SEPARATE_ATTRIBS:
-                if (static_cast<GLuint>(count) > caps.maxTransformFeedbackSeparateAttributes)
-                {
-                    context->handleError(InvalidValue());
-                    return;
-                }
-                break;
-            default:
-                context->handleError(InvalidEnum());
-                return;
-        }
-
-        Program *programObject = GetValidProgram(context, program);
-        if (!programObject)
+        if (!context->skipValidation() &&
+            !ValidateTransformFeedbackVaryings(context, program, count, varyings, bufferMode))
         {
             return;
         }
 
-        programObject->setTransformFeedbackVaryings(count, varyings, bufferMode);
+        context->transformFeedbackVaryings(program, count, varyings, bufferMode);
     }
 }
 
@@ -884,31 +848,14 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
-        {
-            context->handleError(InvalidOperation());
-            return;
-        }
-
-        if (bufSize < 0)
-        {
-            context->handleError(InvalidValue());
-            return;
-        }
-
-        Program *programObject = GetValidProgram(context, program);
-        if (!programObject)
+        if (!context->skipValidation() &&
+            !ValidateGetTransformFeedbackVarying(context, program, index, bufSize, length, size,
+                                                 type, name))
         {
             return;
         }
 
-        if (index >= static_cast<GLuint>(programObject->getTransformFeedbackVaryingCount()))
-        {
-            context->handleError(InvalidValue());
-            return;
-        }
-
-        programObject->getTransformFeedbackVarying(index, bufSize, length, size, type, name);
+        context->getTransformFeedbackVarying(program, index, bufSize, length, size, type, name);
     }
 }
 
@@ -2143,45 +2090,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
+        if (!context->skipValidation() && !ValidateBindTransformFeedback(context, target, id))
         {
-            context->handleError(InvalidOperation());
             return;
         }
 
-        switch (target)
-        {
-            case GL_TRANSFORM_FEEDBACK:
-            {
-                // Cannot bind a transform feedback object if the current one is started and not
-                // paused (3.0.2 pg 85 section 2.14.1)
-                TransformFeedback *curTransformFeedback =
-                    context->getGLState().getCurrentTransformFeedback();
-                if (curTransformFeedback && curTransformFeedback->isActive() &&
-                    !curTransformFeedback->isPaused())
-                {
-                    context->handleError(InvalidOperation());
-                    return;
-                }
-
-                // Cannot bind a transform feedback object that does not exist (3.0.2 pg 85 section
-                // 2.14.1)
-                if (!context->isTransformFeedbackGenerated(id))
-                {
-                    context->handleError(
-                        InvalidOperation()
-                        << "Cannot bind a transform feedback object that does not exist.");
-                    return;
-                }
-
-                context->bindTransformFeedback(id);
-            }
-            break;
-
-            default:
-                context->handleError(InvalidEnum());
-                return;
-        }
+        context->bindTransformFeedback(target, id);
     }
 }
 
@@ -2197,10 +2111,7 @@
             return;
         }
 
-        for (int i = 0; i < n; i++)
-        {
-            context->deleteTransformFeedback(ids[i]);
-        }
+        context->deleteTransformFeedbacks(n, ids);
     }
 }
 
@@ -2216,10 +2127,7 @@
             return;
         }
 
-        for (int i = 0; i < n; i++)
-        {
-            ids[i] = context->createTransformFeedback();
-        }
+        context->genTransformFeedbacks(n, ids);
     }
 }
 
@@ -2230,21 +2138,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
+        if (!context->skipValidation() && !ValidateIsTransformFeedback(context, id))
         {
-            context->handleError(InvalidOperation());
             return GL_FALSE;
         }
 
-        if (id == 0)
-        {
-            // The 3.0.4 spec [section 6.1.11] states that if ID is zero, IsTransformFeedback
-            // returns FALSE
-            return GL_FALSE;
-        }
-
-        const TransformFeedback *transformFeedback = context->getTransformFeedback(id);
-        return ((transformFeedback != nullptr) ? GL_TRUE : GL_FALSE);
+        return context->isTransformFeedback(id);
     }
 
     return GL_FALSE;
@@ -2257,23 +2156,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
+        if (!context->skipValidation() && !ValidatePauseTransformFeedback(context))
         {
-            context->handleError(InvalidOperation());
             return;
         }
 
-        TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
-        ASSERT(transformFeedback != nullptr);
-
-        // Current transform feedback must be active and not paused in order to pause (3.0.2 pg 86)
-        if (!transformFeedback->isActive() || transformFeedback->isPaused())
-        {
-            context->handleError(InvalidOperation());
-            return;
-        }
-
-        transformFeedback->pause();
+        context->pauseTransformFeedback();
     }
 }
 
@@ -2284,23 +2172,12 @@
     Context *context = GetValidGlobalContext();
     if (context)
     {
-        if (context->getClientMajorVersion() < 3)
+        if (!context->skipValidation() && !ValidateResumeTransformFeedback(context))
         {
-            context->handleError(InvalidOperation());
             return;
         }
 
-        TransformFeedback *transformFeedback = context->getGLState().getCurrentTransformFeedback();
-        ASSERT(transformFeedback != nullptr);
-
-        // Current transform feedback must be active and paused in order to resume (3.0.2 pg 86)
-        if (!transformFeedback->isActive() || !transformFeedback->isPaused())
-        {
-            context->handleError(InvalidOperation());
-            return;
-        }
-
-        transformFeedback->resume();
+        context->resumeTransformFeedback();
     }
 }