blob: 64cd70439a1411ce26d338cd1d67576543622c7f [file] [log] [blame]
//
// 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 <vector>
#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