Refactor CompileProgram utility

Tests often need to call gl functions to set up program related state
after glCreateProgram has been called but prior to glLinkProgram is
called. Add a callback function to the CompileProgram utility function
to fulfill this need. This reduces code duplication considerably in
several tests.

An alternative way to improve CompileProgram would be to split it into
several different utility functions. This might be slightly easier to
read, but would also be a larger refactoring and require more checks
at the call site.

This will make it easier to implement EXT_blend_func_extended tests,
which need to bind fragment outputs to different slots.

BUG=angleproject:1085
TEST=angle_end2end_tests

Change-Id: I3ac8b7bdc21c6a1f14517bc7df0cf6f35abd7612
Reviewed-on: https://chromium-review.googlesource.com/1254062
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/util/shader_utils.cpp b/util/shader_utils.cpp
index 4b41677..55baa5b 100644
--- a/util/shader_utils.cpp
+++ b/util/shader_utils.cpp
@@ -116,24 +116,11 @@
     return program;
 }
 
-GLuint CompileProgramWithTransformFeedback(
-    const std::string &vsSource,
-    const std::string &fsSource,
-    const std::vector<std::string> &transformFeedbackVaryings,
-    GLenum bufferMode)
+static GLuint CompileProgramInternal(const std::string &vsSource,
+                                     const std::string &gsSource,
+                                     const std::string &fsSource,
+                                     const std::function<void(GLuint)> &preLinkCallback)
 {
-    return CompileProgramWithGSAndTransformFeedback(vsSource, "", fsSource,
-                                                    transformFeedbackVaryings, bufferMode);
-}
-
-static GLuint CompileAndLinkProgram(const std::string &vsSource,
-                                    const std::string &gsSource,
-                                    const std::string &fsSource,
-                                    const std::vector<std::string> &transformFeedbackVaryings,
-                                    GLenum bufferMode)
-{
-    GLuint program = glCreateProgram();
-
     GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
     GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
 
@@ -141,19 +128,22 @@
     {
         glDeleteShader(fs);
         glDeleteShader(vs);
-        glDeleteProgram(program);
         return 0;
     }
 
+    GLuint program = glCreateProgram();
+
     glAttachShader(program, vs);
     glDeleteShader(vs);
 
     glAttachShader(program, fs);
     glDeleteShader(fs);
 
+    GLuint gs = 0;
+
     if (!gsSource.empty())
     {
-        GLuint gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
+        gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
         if (gs == 0)
         {
             glDeleteShader(vs);
@@ -166,52 +156,58 @@
         glDeleteShader(gs);
     }
 
-    if (transformFeedbackVaryings.size() > 0)
+    if (preLinkCallback)
     {
-        std::vector<const char *> constCharTFVaryings;
-
-        for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
-        {
-            constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
-        }
-
-        glTransformFeedbackVaryings(program, static_cast<GLsizei>(transformFeedbackVaryings.size()),
-                                    &constCharTFVaryings[0], bufferMode);
+        preLinkCallback(program);
     }
 
     glLinkProgram(program);
 
-    return program;
+    return CheckLinkStatusAndReturnProgram(program, true);
 }
 
-GLuint CompileProgramWithGSAndTransformFeedback(
+GLuint CompileProgramWithTransformFeedback(
     const std::string &vsSource,
-    const std::string &gsSource,
     const std::string &fsSource,
     const std::vector<std::string> &transformFeedbackVaryings,
     GLenum bufferMode)
 {
-    GLuint program =
-        CompileAndLinkProgram(vsSource, gsSource, fsSource, transformFeedbackVaryings, bufferMode);
-    if (program == 0)
-    {
-        return 0;
-    }
-    return CheckLinkStatusAndReturnProgram(program, true);
+    auto preLink = [&](GLuint program) {
+        if (transformFeedbackVaryings.size() > 0)
+        {
+            std::vector<const char *> constCharTFVaryings;
+
+            for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
+            {
+                constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
+            }
+
+            glTransformFeedbackVaryings(program,
+                                        static_cast<GLsizei>(transformFeedbackVaryings.size()),
+                                        &constCharTFVaryings[0], bufferMode);
+        }
+    };
+
+    return CompileProgramInternal(vsSource, "", fsSource, preLink);
 }
 
 GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource)
 {
-    return CompileProgramWithGS(vsSource, "", fsSource);
+    return CompileProgramInternal(vsSource, "", fsSource, nullptr);
+}
+
+GLuint CompileProgram(const std::string &vsSource,
+                      const std::string &fsSource,
+                      const std::function<void(GLuint)> &preLinkCallback)
+{
+    return CompileProgramInternal(vsSource, "", fsSource, preLinkCallback);
 }
 
 GLuint CompileProgramWithGS(const std::string &vsSource,
                             const std::string &gsSource,
                             const std::string &fsSource)
 {
-    std::vector<std::string> emptyVector;
-    return CompileProgramWithGSAndTransformFeedback(vsSource, gsSource, fsSource, emptyVector,
-                                                    GL_NONE);
+    return CompileProgramInternal(vsSource, gsSource, fsSource, nullptr);
 }
 
 GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)