Edit

kc3-lang/angle/src/tests/gl_tests/LinkAndRelinkTest.cpp

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2020-04-03 18:31:22
    Hash : 12b6a82e
    Message : No-Op draws when no active VS and/or FS is present Re-land CL with WebGL fixes: This required some extra pointer checking during validation to handle the fact that a Program and/or ProgramExecutable may not be present when attempting to draw. This isn't an error, just undefined behavior, which we (eventually) treat as a no-op. According to the OpenGL ES 3.1 spec: 7.3. PROGRAM OBJECTS If there is no active program for the vertex or fragment shader stages, the results of vertex and fragment shader execution will respectively be undefined. However, this is not an error. To handle this, if no VS or FS is present in the active Program/PPO, we will no-op the draw command. Bug: angleproject:3570 Test: KHR-GLES31.core.sepshaderobjs.StateInteraction Change-Id: I70d688bf344a78cf3b4fd66c995ae03ce4b9b807 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2185156 Reviewed-by: Courtney Goeltzenleuchter <courtneygo@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/tests/gl_tests/LinkAndRelinkTest.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
    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/3969): 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)
    {
        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();
    }
    
    ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(LinkAndRelinkTest);
    ANGLE_INSTANTIATE_TEST_ES31(LinkAndRelinkTestES31);
    
    }  // namespace