// // Copyright 2017 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // LinkAndRelinkFailureTest: // Link and relink failure tests for rendering pipeline and compute pipeline. #include #include "test_utils/ANGLETest.h" #include "test_utils/gl_raii.h" using namespace angle; namespace { class LinkAndRelinkTest : public ANGLETest<> { protected: LinkAndRelinkTest() {} }; class LinkAndRelinkTestES3 : public ANGLETest<> { protected: LinkAndRelinkTestES3() {} }; class LinkAndRelinkTestES31 : public ANGLETest<> { protected: LinkAndRelinkTestES31() {} }; // Test destruction of a context with a pending relink of the current in-use // program. TEST_P(LinkAndRelinkTest, DestructionWithPendingRelink) { constexpr char kVS[] = "void main() {}"; constexpr char kFS[] = "void main() {}"; GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); GLuint program = glCreateProgram(); glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); glUseProgram(program); glLinkProgram(program); EXPECT_GL_NO_ERROR(); } // When a program link or relink fails, if you try to install the unsuccessfully // linked program (via UseProgram) and start rendering or dispatch compute, // We can not always report INVALID_OPERATION for rendering/compute pipeline. // The result depends on the previous state: Whether a valid program is // installed in current GL state before the link. // If a program successfully relinks when it is in use, the program might // change from a rendering program to a compute program in theory, // or vice versa. // When program link fails and no valid rendering program is installed in the GL // state before the link, it should report an error for UseProgram TEST_P(LinkAndRelinkTest, RenderingProgramFailsWithoutProgramInstalled) { glUseProgram(0); GLuint program = glCreateProgram(); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); glUseProgram(program); EXPECT_GL_ERROR(GL_INVALID_OPERATION); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); } // When program link or relink fails and a valid rendering program is installed // in the GL state before the link, using the failed program via UseProgram // should report an error, but starting rendering should succeed. // However, dispatching compute always fails. TEST_P(LinkAndRelinkTest, RenderingProgramFailsWithProgramInstalled) { // Install a render program in current GL state via UseProgram, then render. // It should succeed. constexpr char kVS[] = "void main() {}"; constexpr char kFS[] = "void main() {}"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glUseProgram(program); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Link failure, and a valid program has been installed in the GL state. GLuint programNull = glCreateProgram(); glLinkProgram(programNull); glGetProgramiv(programNull, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); // Starting rendering should succeed. glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully linked program should report an error. glUseProgram(programNull); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully linked program, that program should not // replace the program binary residing in the GL state. It will not make // the installed program invalid either, like what UseProgram(0) can do. // So, starting rendering should succeed. glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // We try to relink the installed program, but make it fail. // No vertex shader, relink fails. glDetachShader(program, vs); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); EXPECT_GL_NO_ERROR(); // Starting rendering should succeed. glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully relinked program should report an error. glUseProgram(program); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully relinked program, that program should not // replace the program binary residing in the GL state. It will not make // the installed program invalid either, like what UseProgram(0) can do. // So, starting rendering should succeed. glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); } // Tests uniform default values. TEST_P(LinkAndRelinkTest, UniformDefaultValues) { // TODO(anglebug.com/42262609): Understand why rectangle texture CLs made this fail. ANGLE_SKIP_TEST_IF(IsOzone() && IsIntel()); constexpr char kFS[] = R"(precision mediump float; uniform vec4 u_uniform; bool isZero(vec4 value) { return value == vec4(0,0,0,0); } void main() { gl_FragColor = isZero(u_uniform) ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1); })"; ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFS); glUseProgram(program); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); GLint loc = glGetUniformLocation(program, "u_uniform"); ASSERT_NE(-1, loc); glUniform4f(loc, 0.1f, 0.2f, 0.3f, 0.4f); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); glLinkProgram(program); ASSERT_GL_NO_ERROR(); drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true); ASSERT_GL_NO_ERROR(); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); } // When program link fails and no valid compute program is installed in the GL // state before the link, it should report an error for UseProgram and // DispatchCompute. TEST_P(LinkAndRelinkTestES31, ComputeProgramFailsWithoutProgramInstalled) { glUseProgram(0); GLuint program = glCreateProgram(); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); glUseProgram(program); EXPECT_GL_ERROR(GL_INVALID_OPERATION); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); } // When program link or relink fails and a valid compute program is installed in // the GL state before the link, using the failed program via UseProgram should // report an error, but dispatching compute should succeed. TEST_P(LinkAndRelinkTestES31, ComputeProgramFailsWithProgramInstalled) { // Install a compute program in the GL state via UseProgram, then dispatch // compute. It should succeed. constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1) in; void main() { })"; GLuint program = glCreateProgram(); GLuint cs = CompileShader(GL_COMPUTE_SHADER, kCS); EXPECT_NE(0u, cs); glAttachShader(program, cs); glDeleteShader(cs); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glUseProgram(program); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); // Link failure, and a valid program has been installed in the GL state. GLuint programNull = glCreateProgram(); glLinkProgram(programNull); glGetProgramiv(programNull, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); // Dispatching compute should succeed. glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); // Using the unsuccessfully linked program should report an error. glUseProgram(programNull); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully linked program, that program should not // replace the program binary residing in the GL state. It will not make // the installed program invalid either, like what UseProgram(0) can do. // So, dispatching compute should succeed. glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); // We try to relink the installed program, but make it fail. // No compute shader, relink fails. glDetachShader(program, cs); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); EXPECT_GL_NO_ERROR(); // Dispatching compute should succeed. glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); // Using the unsuccessfully relinked program should report an error. glUseProgram(program); EXPECT_GL_ERROR(GL_INVALID_OPERATION); // Using the unsuccessfully relinked program, that program should not // replace the program binary residing in the GL state. It will not make // the installed program invalid either, like what UseProgram(0) can do. // So, dispatching compute should succeed. glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); } // If you compile and link a compute program successfully and use the program, // then dispatching compute and rendering can succeed (with undefined behavior). // If you relink the compute program to a rendering program when it is in use, // then dispatching compute will fail, but starting rendering can succeed. TEST_P(LinkAndRelinkTestES31, RelinkProgramSucceedsFromComputeToRendering) { constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1) in; void main() { })"; GLuint program = glCreateProgram(); GLuint cs = CompileShader(GL_COMPUTE_SHADER, kCS); EXPECT_NE(0u, cs); glAttachShader(program, cs); glDeleteShader(cs); glLinkProgram(program); glDetachShader(program, cs); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glUseProgram(program); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); constexpr char kVS[] = "void main() {}"; constexpr char kFS[] = "void main() {}"; GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); glLinkProgram(program); glDetachShader(program, vs); glDetachShader(program, fs); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); } // If you compile and link a rendering program successfully and use the program, // then starting rendering can succeed, while dispatching compute will fail. // If you relink the rendering program to a compute program when it is in use, // then starting rendering will fail, but dispatching compute can succeed. TEST_P(LinkAndRelinkTestES31, RelinkProgramSucceedsFromRenderingToCompute) { // http://anglebug.com/42263641 ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL()); constexpr char kVS[] = "void main() {}"; constexpr char kFS[] = "void main() {}"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); glAttachShader(program, vs); glDeleteShader(vs); glAttachShader(program, fs); glDeleteShader(fs); glLinkProgram(program); glDetachShader(program, vs); glDetachShader(program, fs); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glUseProgram(program); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_ERROR(GL_INVALID_OPERATION); constexpr char kCS[] = R"(#version 310 es layout(local_size_x=1) in; void main() { })"; GLuint cs = CompileShader(GL_COMPUTE_SHADER, kCS); EXPECT_NE(0u, cs); glAttachShader(program, cs); glDeleteShader(cs); glLinkProgram(program); glDetachShader(program, cs); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); glDispatchCompute(8, 4, 2); EXPECT_GL_NO_ERROR(); glDrawArrays(GL_POINTS, 0, 1); EXPECT_GL_NO_ERROR(); } // Parallel link should continue unscathed even if the attached shaders to the program are modified. TEST_P(LinkAndRelinkTestES31, ReattachShadersWhileParallelLinking) { constexpr char kVS[] = R"(#version 300 es void main() { vec2 position = vec2(-1, -1); if (gl_VertexID == 1) position = vec2(3, -1); else if (gl_VertexID == 2) position = vec2(-1, 3); gl_Position = vec4(position, 0, 1); })"; constexpr char kFSGreen[] = R"(#version 300 es out mediump vec4 color; void main() { color = vec4(0, 1, 0, 1); })"; constexpr char kFSRed[] = R"(#version 300 es out mediump vec4 color; void main() { color = vec4(1, 0, 0, 1); })"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint green = CompileShader(GL_FRAGMENT_SHADER, kFSGreen); GLuint red = CompileShader(GL_FRAGMENT_SHADER, kFSRed); EXPECT_NE(0u, vs); EXPECT_NE(0u, green); EXPECT_NE(0u, red); glAttachShader(program, vs); glAttachShader(program, green); glLinkProgram(program); ASSERT_GL_NO_ERROR(); // Immediately reattach another shader glDetachShader(program, green); glAttachShader(program, red); ASSERT_GL_NO_ERROR(); // Make sure the linked program draws with green glUseProgram(program); ASSERT_GL_NO_ERROR(); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); ASSERT_GL_NO_ERROR(); glDeleteShader(vs); glDeleteShader(green); glDeleteShader(red); ASSERT_GL_NO_ERROR(); } // Parallel link should continue unscathed even if new shaders are attached to the program. TEST_P(LinkAndRelinkTestES31, AttachNewShadersWhileParallelLinking) { ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); constexpr char kVS[] = R"(#version 310 es #extension GL_EXT_geometry_shader : require void main() { vec2 position = vec2(-1, -1); if (gl_VertexID == 1) position = vec2(3, -1); else if (gl_VertexID == 2) position = vec2(-1, 3); gl_Position = vec4(position, 0, 1); })"; constexpr char kFS[] = R"(#version 310 es #extension GL_EXT_geometry_shader : require out mediump vec4 color; void main() { color = vec4(0, 1, 0, 1); })"; constexpr char kGS[] = R"(#version 310 es #extension GL_EXT_geometry_shader : require layout (invocations = 3, triangles) in; layout (triangle_strip, max_vertices = 3) out; void main() { })"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kFS); GLuint gs = CompileShader(GL_GEOMETRY_SHADER, kGS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); EXPECT_NE(0u, gs); glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); ASSERT_GL_NO_ERROR(); // Immediately attach another shader glAttachShader(program, gs); ASSERT_GL_NO_ERROR(); // Make sure the linked program draws with green glUseProgram(program); ASSERT_GL_NO_ERROR(); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); ASSERT_GL_NO_ERROR(); glDeleteShader(vs); glDeleteShader(fs); glDeleteShader(gs); ASSERT_GL_NO_ERROR(); } // Make sure the shader can be compiled in between attach and link TEST_P(LinkAndRelinkTest, AttachShaderThenCompile) { GLuint program = glCreateProgram(); GLShader vs(GL_VERTEX_SHADER); GLShader fs(GL_FRAGMENT_SHADER); // Attach the shaders to the program first. This makes sure the program doesn't prematurely // attempt to look into the shader's compilation result. glAttachShader(program, vs); glAttachShader(program, fs); // Compile the shaders after that. const char *kVS = essl1_shaders::vs::Simple(); const char *kFS = essl1_shaders::fs::Green(); glShaderSource(vs, 1, &kVS, nullptr); glShaderSource(fs, 1, &kFS, nullptr); EXPECT_GL_NO_ERROR(); glCompileShader(vs); glCompileShader(fs); // Then link glLinkProgram(program); ASSERT_GL_NO_ERROR(); // Make sure it works drawQuad(program, essl1_shaders::PositionAttrib(), 0.5); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); ASSERT_GL_NO_ERROR(); glDeleteProgram(program); ASSERT_GL_NO_ERROR(); } // If a program is linked successfully once, it should retain its executable if a relink fails. TEST_P(LinkAndRelinkTestES3, SuccessfulLinkThenFailingRelink) { // Install a render program in current GL state via UseProgram, then render. // It should succeed. constexpr char kVS[] = R"(#version 300 es out vec4 color; void main() { vec2 position = vec2(-1, -1); if (gl_VertexID == 1) position = vec2(3, -1); else if (gl_VertexID == 2) position = vec2(-1, 3); gl_Position = vec4(position, 0, 1); color = vec4(0, 1, 0, 1); })"; constexpr char kBadFS[] = R"(#version 300 es flat in uvec2 color; out mediump vec4 colorOut; void main() { colorOut = vec4(1, color, 1); })"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, essl3_shaders::fs::Green()); GLuint badfs = CompileShader(GL_FRAGMENT_SHADER, kBadFS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); EXPECT_NE(0u, badfs); glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); const int w = getWindowWidth(); const int h = getWindowHeight(); glViewport(0, 0, w, h); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w / 2, h / 2); glUseProgram(program); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); // Cause the program to fail linking glDetachShader(program, fs); glAttachShader(program, badfs); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); // Link failed, but the program should still be usable. glScissor(w / 2, h / 2, w / 2, h / 2); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h / 2, GLColor::green); EXPECT_PIXEL_RECT_EQ(w / 2, 0, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(0, h / 2, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(w / 2, h / 2, w / 2, h / 2, GLColor::green); glDeleteShader(vs); glDeleteShader(fs); glDeleteShader(badfs); glDeleteProgram(program); } // Similar to SuccessfulLinkThenFailingRelink, but with a more complicated mix of resources. TEST_P(LinkAndRelinkTestES31, SuccessfulLinkThenFailingRelink2) { GLint maxFragmentShaderStorageBlocks; glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks); ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks < 1); // Install a render program in current GL state via UseProgram, then render. // It should succeed. constexpr char kVS[] = R"(#version 310 es out vec4 color; void main() { vec2 position = vec2(-1, -1); if (gl_VertexID == 1) position = vec2(3, -1); else if (gl_VertexID == 2) position = vec2(-1, 3); gl_Position = vec4(position, 0, 1); color = vec4(0, 1, 0, 1); })"; constexpr char kGoodFS[] = R"(#version 310 es in mediump vec4 color; out mediump vec4 colorOut; mediump uniform float u; // should be 0.5; uniform UBO { highp float b; // should be 1.75 }; layout(std140, binding = 1) buffer SSBO { uint s; // should be 0x12345678 }; void main() { if (abs(u - 0.5) > 0.01) colorOut = vec4(1, 0, 0, 1); else if (abs(b - 1.75) > 0.01) colorOut = vec4(0, 0, 1, 1); else if (s != 0x12345678u) colorOut = vec4(1, 0, 1, 1); else colorOut = color; })"; constexpr char kBadFS[] = R"(#version 310 es flat in uvec2 color; layout(location = 0) out mediump vec4 colorOut; layout(location = 1) out mediump vec4 colorOut2; void main() { colorOut = vec4(1, color, 1); colorOut2 = vec4(color, 0, 1); })"; GLuint program = glCreateProgram(); GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, kGoodFS); GLuint badfs = CompileShader(GL_FRAGMENT_SHADER, kBadFS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); EXPECT_NE(0u, badfs); glAttachShader(program, vs); glAttachShader(program, fs); glLinkProgram(program); GLint linkStatus; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_TRUE(linkStatus); EXPECT_GL_NO_ERROR(); constexpr float kUBOValue = 1.75; GLBuffer ubo; glBindBuffer(GL_UNIFORM_BUFFER, ubo); glBufferData(GL_UNIFORM_BUFFER, sizeof(kUBOValue), &kUBOValue, GL_STATIC_DRAW); glBindBufferBase(GL_UNIFORM_BUFFER, 0, ubo); constexpr uint32_t kSSBOValue = 0x12345678; GLBuffer ssbo; glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(kSSBOValue), &kSSBOValue, GL_STATIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo); EXPECT_GL_NO_ERROR(); const int w = getWindowWidth(); const int h = getWindowHeight(); glViewport(0, 0, w, h); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w / 2, h / 2); glUseProgram(program); const GLint uniLoc = glGetUniformLocation(program, "u"); ASSERT_NE(uniLoc, -1); glUniform1f(uniLoc, 0.5); const GLint uboIndex = glGetUniformBlockIndex(program, "UBO"); ASSERT_NE(uboIndex, -1); glUniformBlockBinding(program, uboIndex, 0); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); // Cause the program to fail linking glDetachShader(program, fs); glAttachShader(program, badfs); glLinkProgram(program); glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); // Link failed, but the program should still be usable. glScissor(w / 2, h / 2, w / 2, h / 2); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h / 2, GLColor::green); EXPECT_PIXEL_RECT_EQ(w / 2, 0, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(0, h / 2, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(w / 2, h / 2, w / 2, h / 2, GLColor::green); glDeleteShader(vs); glDeleteShader(fs); glDeleteShader(badfs); glDeleteProgram(program); } // Same as SuccessfulLinkThenFailingRelink, but with PPOs. TEST_P(LinkAndRelinkTestES31, SuccessfulLinkThenFailingRelinkWithPPO) { // Only the Vulkan backend supports PPOs. ANGLE_SKIP_TEST_IF(!IsVulkan()); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); // Install a render program in current GL state via UseProgram, then render. // It should succeed. constexpr char kVS[] = R"(#version 300 es void main() { vec2 position = vec2(-1, -1); if (gl_VertexID == 1) position = vec2(3, -1); else if (gl_VertexID == 2) position = vec2(-1, 3); gl_Position = vec4(position, 0, 1); })"; constexpr char kBadGS[] = R"(#version 310 es #extension GL_EXT_geometry_shader : require layout (invocations = 3, triangles) in; layout (triangle_strip, max_vertices = 3) out; void main() { })"; GLuint vs = CompileShader(GL_VERTEX_SHADER, kVS); GLuint fs = CompileShader(GL_FRAGMENT_SHADER, essl3_shaders::fs::Green()); GLuint badgs = CompileShader(GL_GEOMETRY_SHADER, kBadGS); EXPECT_NE(0u, vs); EXPECT_NE(0u, fs); EXPECT_NE(0u, badgs); GLuint vsProg = glCreateProgram(); GLuint fsProg = glCreateProgram(); glProgramParameteri(vsProg, GL_PROGRAM_SEPARABLE, GL_TRUE); glAttachShader(vsProg, vs); glLinkProgram(vsProg); EXPECT_GL_NO_ERROR(); glProgramParameteri(fsProg, GL_PROGRAM_SEPARABLE, GL_TRUE); glAttachShader(fsProg, fs); glLinkProgram(fsProg); EXPECT_GL_NO_ERROR(); GLuint pipeline; glGenProgramPipelines(1, &pipeline); glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, vsProg); glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, fsProg); EXPECT_GL_NO_ERROR(); const int w = getWindowWidth(); const int h = getWindowHeight(); glViewport(0, 0, w, h); glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); glEnable(GL_SCISSOR_TEST); glScissor(0, 0, w / 2, h / 2); glUseProgram(0); glBindProgramPipeline(pipeline); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); // Cause the fs program to fail linking glAttachShader(fsProg, badgs); glLinkProgram(fsProg); glUseProgramStages(pipeline, GL_GEOMETRY_SHADER_BIT, fsProg); EXPECT_GL_ERROR(GL_INVALID_OPERATION); GLint linkStatus; glGetProgramiv(fsProg, GL_LINK_STATUS, &linkStatus); EXPECT_GL_FALSE(linkStatus); // Program link failed, but the program pipeline should still be usable. glScissor(w / 2, h / 2, w / 2, h / 2); glDrawArrays(GL_TRIANGLES, 0, 3); EXPECT_GL_NO_ERROR(); EXPECT_PIXEL_RECT_EQ(0, 0, w / 2, h / 2, GLColor::green); EXPECT_PIXEL_RECT_EQ(w / 2, 0, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(0, h / 2, w / 2, h / 2, GLColor::black); EXPECT_PIXEL_RECT_EQ(w / 2, h / 2, w / 2, h / 2, GLColor::green); glDeleteShader(vs); glDeleteShader(fs); glDeleteShader(badgs); glDeleteProgram(vsProg); glDeleteProgram(fsProg); glDeleteProgramPipelines(1, &pipeline); } ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(LinkAndRelinkTest); ANGLE_INSTANTIATE_TEST_ES3(LinkAndRelinkTestES3); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LinkAndRelinkTestES31); ANGLE_INSTANTIATE_TEST_ES31(LinkAndRelinkTestES31); } // namespace