Clear error logs when starting a new link process

This patch intends to fix a bug in logging link errors.

OpenGL ES requires glGetProgramInfoLog return a string
that contains information about last link or validation
attempt on a program object (GLES 2.0 Chapter 6.1.8,
GLES 3.0 Chapter 6.1.12). So all the link error logs
should be cleared when a new link process is begun.

This patch also removes several redundant allocations
of mLazyStream in ProgramD3D::compileProgramExecutables.
Calling "<<" on InfoLog objects will initialize its
member mLazyStream from heap, so we will skip using "<<"
if there is actually nothing to output.

BUG=angleproject:2295
TEST=angle_end2end_tests
Change-Id: Ib81fffd3d05919a8ebccd9145ff780548ca86a70
Reviewed-on: https://chromium-review.googlesource.com/848324
Commit-Queue: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp
index 535e093..7ccd470 100644
--- a/src/libANGLE/Program.cpp
+++ b/src/libANGLE/Program.cpp
@@ -414,6 +414,20 @@
 
 void InfoLog::reset()
 {
+    if (mLazyStream)
+    {
+        mLazyStream.reset(nullptr);
+    }
+}
+
+bool InfoLog::empty() const
+{
+    if (!mLazyStream)
+    {
+        return true;
+    }
+
+    return mLazyStream->rdbuf()->in_avail() == 0;
 }
 
 // VariableLocation implementation.
diff --git a/src/libANGLE/Program.h b/src/libANGLE/Program.h
index 0dd8fb9..a2e53cf 100644
--- a/src/libANGLE/Program.h
+++ b/src/libANGLE/Program.h
@@ -119,6 +119,8 @@
 
     std::string str() const { return mLazyStream ? mLazyStream->str() : ""; }
 
+    bool empty() const;
+
   private:
     void ensureInitialized()
     {
diff --git a/src/libANGLE/renderer/d3d/ProgramD3D.cpp b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
index 9699e7e..80ae30b 100644
--- a/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -1600,9 +1600,18 @@
 
     WaitableEvent::WaitMany(&waitEvents);
 
-    infoLog << vertexTask.getInfoLog().str();
-    infoLog << pixelTask.getInfoLog().str();
-    infoLog << geometryTask.getInfoLog().str();
+    if (!vertexTask.getInfoLog().empty())
+    {
+        infoLog << vertexTask.getInfoLog().str();
+    }
+    if (!pixelTask.getInfoLog().empty())
+    {
+        infoLog << pixelTask.getInfoLog().str();
+    }
+    if (!geometryTask.getInfoLog().empty())
+    {
+        infoLog << geometryTask.getInfoLog().str();
+    }
 
     ANGLE_TRY(vertexTask.getError());
     ANGLE_TRY(pixelTask.getError());
diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp
index 2cacd3e..888249b 100644
--- a/src/tests/gl_tests/GLSLTest.cpp
+++ b/src/tests/gl_tests/GLSLTest.cpp
@@ -431,6 +431,24 @@
         }
     }
 
+    std::string QueryErrorMessage(GLuint program)
+    {
+        GLint infoLogLength;
+        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
+        EXPECT_GL_NO_ERROR();
+
+        if (infoLogLength >= 1)
+        {
+            std::vector<GLchar> infoLog(infoLogLength);
+            glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
+                                infoLog.data());
+            EXPECT_GL_NO_ERROR();
+            return infoLog.data();
+        }
+
+        return "";
+    }
+
     std::string mSimpleVSSource;
 };
 
@@ -3916,6 +3934,65 @@
     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
 }
 
+// Verify that the link error message from last link failure is cleared when the new link is
+// finished.
+TEST_P(GLSLTest, ClearLinkErrorLog)
+{
+    const std::string &vertexShader =
+        R"(
+
+        attribute vec4 vert_in;
+        varying vec4 vert_out;
+        void main()
+        {
+            gl_Position = vert_in;
+            vert_out = vert_in;
+        })";
+
+    const std::string &fragmentShader =
+        R"(
+
+        precision mediump float;
+        varying vec4 frag_in;
+        void main()
+        {
+            gl_FragColor = frag_in;
+        })";
+
+    GLuint vs = CompileShader(GL_VERTEX_SHADER, vertexShader);
+    GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fragmentShader);
+
+    GLuint program = glCreateProgram();
+
+    // The first time the program link fails because of lack of fragment shader.
+    glAttachShader(program, vs);
+    glLinkProgram(program);
+    GLint linkStatus = GL_TRUE;
+    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+    ASSERT_FALSE(linkStatus);
+
+    const std::string &lackOfFragmentShader = QueryErrorMessage(program);
+
+    // The second time the program link fails because of the mismatch of the varying types.
+    glAttachShader(program, fs);
+    glLinkProgram(program);
+    linkStatus = GL_TRUE;
+    glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
+    ASSERT_FALSE(linkStatus);
+
+    const std::string &varyingTypeMismatch = QueryErrorMessage(program);
+
+    EXPECT_EQ(std::string::npos, varyingTypeMismatch.find(lackOfFragmentShader));
+
+    glDetachShader(program, vs);
+    glDetachShader(program, fs);
+    glDeleteShader(vs);
+    glDeleteShader(fs);
+    glDeleteProgram(program);
+
+    ASSERT_GL_NO_ERROR();
+}
+
 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
 ANGLE_INSTANTIATE_TEST(GLSLTest,
                        ES2_D3D9(),