diff --git a/src/libANGLE/Program.cpp b/src/libANGLE/Program.cpp index 692f75488..a1edeeaf2 100644 --- a/src/libANGLE/Program.cpp +++ b/src/libANGLE/Program.cpp @@ -1419,7 +1419,10 @@ angle::Result Program::link(const Context *context) auto *platform = ANGLEPlatformCurrent(); double startTime = platform->currentTime(platform); - unlink(); + // Unlink the program, but do not clear the validation-related caching yet, since we can still + // use the previously linked program if linking the shaders fails + mLinked = false; + infoLog.reset(); // Validate we have properly attached shaders before checking the cache. diff --git a/src/tests/gl_tests/GLSLTest.cpp b/src/tests/gl_tests/GLSLTest.cpp index 9ddbeb0f9..2e43bf3a4 100644 --- a/src/tests/gl_tests/GLSLTest.cpp +++ b/src/tests/gl_tests/GLSLTest.cpp @@ -5991,6 +5991,71 @@ void main() ASSERT_GL_NO_ERROR(); } +// Verify that a valid program still draws correctly after a shader link error +TEST_P(GLSLTest, DrawAfterShaderLinkError) +{ + constexpr char kVS[] = R"(attribute vec4 position; + varying vec4 vColor; + void main() + { + vColor = vec4(0.0, 1.0, 0.0, 1.0); + gl_Position = position; + })"; + constexpr char kFS[] = R"(precision mediump float; + varying vec4 vColor; + void main() + { + gl_FragColor = vColor; + })"; + constexpr char kBadFS[] = R"(WILL NOT COMPILE;)"; + + GLuint fsBad = glCreateShader(GL_FRAGMENT_SHADER); + + // Create bad fragment shader + { + const char *sourceArray[1] = {kBadFS}; + glShaderSource(fsBad, 1, sourceArray, nullptr); + glCompileShader(fsBad); + + GLint compileResult; + glGetShaderiv(fsBad, GL_COMPILE_STATUS, &compileResult); + ASSERT_FALSE(compileResult); + } + + ANGLE_GL_PROGRAM(program, kVS, kFS); + GLuint fs = GetProgramShader(program.get(), GL_FRAGMENT_SHADER); + + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); + + glUseProgram(program.get()); + GLint positionLocation = glGetAttribLocation(program.get(), "position"); + std::array quadVertices = GetQuadVertices(); + for (Vector3 &vertex : quadVertices) + { + vertex.z() = 0.5f; + } + glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, quadVertices.data()); + glEnableVertexAttribArray(positionLocation); + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); + + glDetachShader(program.get(), fs); + glAttachShader(program.get(), fsBad); + glLinkProgram(program.get()); + GLint linkStatus = GL_TRUE; + glGetProgramiv(program.get(), GL_LINK_STATUS, &linkStatus); + ASSERT_FALSE(linkStatus); + + glClearColor(1.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); + + glDrawArrays(GL_TRIANGLES, 0, 6); + EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); +} + // Validate error messages when the link mismatch occurs on the type of a non-struct varying. TEST_P(GLSLTest, ErrorMessageOfVaryingMismatch) { diff --git a/util/shader_utils.cpp b/util/shader_utils.cpp index 9c73414d7..6d94734a7 100644 --- a/util/shader_utils.cpp +++ b/util/shader_utils.cpp @@ -164,6 +164,25 @@ GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages) return program; } +GLuint GetProgramShader(GLuint program, GLint requestedType) +{ + static constexpr GLsizei kMaxShaderCount = 16; + GLuint attachedShaders[kMaxShaderCount] = {0u}; + GLsizei count = 0; + glGetAttachedShaders(program, kMaxShaderCount, &count, attachedShaders); + for (int i = 0; i < count; ++i) + { + GLint type = 0; + glGetShaderiv(attachedShaders[i], GL_SHADER_TYPE, &type); + if (type == requestedType) + { + return attachedShaders[i]; + } + } + + return 0; +} + GLuint CompileProgramWithTransformFeedback( const char *vsSource, const char *fsSource, diff --git a/util/shader_utils.h b/util/shader_utils.h index 3d2e7f49d..982338887 100644 --- a/util/shader_utils.h +++ b/util/shader_utils.h @@ -15,6 +15,7 @@ #include "util/util_gl.h" ANGLE_UTIL_EXPORT GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages); +ANGLE_UTIL_EXPORT GLuint GetProgramShader(GLuint program, GLint requestedType); ANGLE_UTIL_EXPORT GLuint CompileShader(GLenum type, const char *source); ANGLE_UTIL_EXPORT GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath);