Commit ece12535 by Yunchao He Committed by Commit Bot

ES31: Fix the issue for relink rendering/compute program.

When link or relink fails, if we 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 has been installed in pipeline before. If a valid program has been installed, it should be OK to use the old executable residing in the GL state to start rendering or dispatch compute. No error should be reported. This change also add unit tests for unsuccessfully linked/relinked program for rendering pipeline to avoid potential error. 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. BUG=angleproject:2266 Change-Id: I4726112af2bc74f5beef25e35d2fcaa9f31e0768 Reviewed-on: https://chromium-review.googlesource.com/784273Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org> Commit-Queue: Corentin Wallez <cwallez@chromium.org>
parent 5f19810d
......@@ -977,6 +977,8 @@ Error Program::link(const gl::Context *context)
setUniformValuesFromBindingQualifiers();
// According to GLES 3.0/3.1 spec for LinkProgram and UseProgram,
// Only successfully linked program can replace the executables.
ASSERT(mLinked);
updateLinkedShaderStages();
......@@ -999,6 +1001,8 @@ Error Program::link(const gl::Context *context)
void Program::updateLinkedShaderStages()
{
mState.mLinkedShaderStages.reset();
if (mState.mAttachedVertexShader)
{
mState.mLinkedShaderStages.set(SHADER_VERTEX);
......@@ -1036,7 +1040,6 @@ void Program::unlink()
mState.mSamplerBindings.clear();
mState.mImageBindings.clear();
mState.mNumViews = -1;
mState.mLinkedShaderStages.reset();
mValidated = false;
......
......@@ -2505,8 +2505,7 @@ bool ValidateDrawBase(ValidationContext *context, GLenum mode, GLsizei count)
// vertex shader stage or fragment shader stage is a undefined behaviour.
// But ANGLE should clearly generate an INVALID_OPERATION error instead of
// produce undefined result.
if (program->isLinked() &&
(!program->hasLinkedVertexShader() || !program->hasLinkedFragmentShader()))
if (!program->hasLinkedVertexShader() || !program->hasLinkedFragmentShader())
{
context->handleError(InvalidOperation() << "It is a undefined behaviour to render without "
"vertex shader stage or fragment shader stage.");
......
......@@ -1391,11 +1391,9 @@ bool ValidateDispatchCompute(Context *context,
return false;
}
if (!program->isLinked() || !program->hasLinkedComputeShader())
if (!program->hasLinkedComputeShader())
{
context->handleError(
InvalidOperation()
<< "Program has not been successfully linked, or program contains no compute shaders.");
context->handleError(InvalidOperation() << "Program contains no compute shaders.");
return false;
}
......
......@@ -53,6 +53,7 @@
'<(angle_path)/src/tests/gl_tests/IndexedPointsTest.cpp',
'<(angle_path)/src/tests/gl_tests/InstancingTest.cpp',
'<(angle_path)/src/tests/gl_tests/LineLoopTest.cpp',
'<(angle_path)/src/tests/gl_tests/LinkAndRelinkTest.cpp',
'<(angle_path)/src/tests/gl_tests/MaxTextureSizeTest.cpp',
'<(angle_path)/src/tests/gl_tests/MipmapTest.cpp',
'<(angle_path)/src/tests/gl_tests/MultisampleCompatibilityTest.cpp',
......
//
// 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 LinkAndRelinkTestES31 : public ANGLETest
{
protected:
LinkAndRelinkTestES31() {}
};
// 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 and
// DrawArrays/DrawElements.
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_ERROR(GL_INVALID_OPERATION);
}
// 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.
const std::string vsSource =
R"(void main()
{
})";
const std::string fsSource =
R"(void main()
{
})";
GLuint program = glCreateProgram();
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
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);
}
// 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.
// However, starting rendering always fails.
TEST_P(LinkAndRelinkTestES31, ComputeProgramFailsWithProgramInstalled)
{
// Install a compute program in the GL state via UseProgram, then dispatch
// compute. It should succeed.
const std::string csSource =
R"(#version 310 es
layout(local_size_x=1) in;
void main()
{
})";
GLuint program = glCreateProgram();
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
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_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);
// Dispatching compute should succeed.
glDispatchCompute(8, 4, 2);
EXPECT_GL_NO_ERROR();
glDrawArrays(GL_POINTS, 0, 1);
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, dispatching compute should succeed.
glDispatchCompute(8, 4, 2);
EXPECT_GL_NO_ERROR();
glDrawArrays(GL_POINTS, 0, 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
// 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_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, dispatching compute should succeed.
glDispatchCompute(8, 4, 2);
EXPECT_GL_NO_ERROR();
glDrawArrays(GL_POINTS, 0, 1);
EXPECT_GL_ERROR(GL_INVALID_OPERATION);
}
// If you compile and link a compute program successfully and use the program,
// then dispatching compute can succeed, while starting rendering will fail.
// 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)
{
const std::string csSource =
R"(#version 310 es
layout(local_size_x=1) in;
void main()
{
})";
GLuint program = glCreateProgram();
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
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_ERROR(GL_INVALID_OPERATION);
const std::string vsSource =
R"(void main()
{
})";
const std::string fsSource =
R"(void main()
{
})";
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
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)
{
const std::string vsSource =
R"(void main()
{
})";
const std::string fsSource =
R"(void main()
{
})";
GLuint program = glCreateProgram();
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
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);
const std::string csSource =
R"(#version 310 es
layout(local_size_x=1) in;
void main()
{
})";
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
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_ERROR(GL_INVALID_OPERATION);
}
ANGLE_INSTANTIATE_TEST(LinkAndRelinkTest,
ES2_OPENGL(),
ES2_OPENGLES(),
ES2_D3D9(),
ES2_D3D11(),
ES3_OPENGL(),
ES3_OPENGLES(),
ES3_D3D11());
ANGLE_INSTANTIATE_TEST(LinkAndRelinkTestES31, ES31_OPENGL(), ES31_OPENGLES());
} // namespace
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment