Edit

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

Branch :

  • Show log

    Commit

  • Author : Yuly Novikov
    Date : 2021-03-02 19:04:57
    Hash : a6b16d29
    Message : Suppress UNINSTANTIATED_PARAMETERIZED_TEST failures on Ozone We only support ES2 on Ozone, so tests that depend on ES3 or ES31 support are not instantiated there. Bug: chromium:1183147 Change-Id: Id58bcd9b44a5b9a70b5ae8115e27c44f5dc81226 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2726550 Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Commit-Queue: Yuly Novikov <ynovikov@chromium.org>

  • src/tests/gl_tests/ParallelShaderCompileTest.cpp
  • //
    // Copyright 2018 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.
    //
    
    // ParallelShaderCompileTest.cpp : Tests of the GL_KHR_parallel_shader_compile extension.
    
    #include "test_utils/ANGLETest.h"
    #include "test_utils/gl_raii.h"
    
    #include "util/random_utils.h"
    #include "util/test_utils.h"
    
    using namespace angle;
    
    namespace
    {
    
    namespace
    {
    
    constexpr int kTaskCount             = 32;
    constexpr unsigned int kPollInterval = 100;
    
    }  // anonymous namespace
    
    class ParallelShaderCompileTest : public ANGLETest
    {
      protected:
        ParallelShaderCompileTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        bool ensureParallelShaderCompileExtensionAvailable()
        {
            if (IsGLExtensionRequestable("GL_KHR_parallel_shader_compile"))
            {
                glRequestExtensionANGLE("GL_KHR_parallel_shader_compile");
            }
    
            if (!IsGLExtensionEnabled("GL_KHR_parallel_shader_compile"))
            {
                return false;
            }
            return true;
        }
    
        class Task
        {
          public:
            Task(int id) : mID(id) {}
            virtual ~Task() {}
    
            virtual bool compile()                                     = 0;
            virtual bool isCompileCompleted()                          = 0;
            virtual bool link()                                        = 0;
            virtual void runAndVerify(ParallelShaderCompileTest *test) = 0;
    
            bool isLinkCompleted()
            {
                GLint status;
                glGetProgramiv(mProgram, GL_COMPLETION_STATUS_KHR, &status);
                return (status == GL_TRUE);
            }
    
          protected:
            std::string insertRandomString(const std::string &source)
            {
                RNG rng;
                std::ostringstream ostream;
                ostream << source << "\n// Random string to fool program cache: " << rng.randomInt()
                        << "\n";
                return ostream.str();
            }
    
            GLuint CompileShader(GLenum type, const std::string &source)
            {
                GLuint shader = glCreateShader(type);
    
                const char *sourceArray[1] = {source.c_str()};
                glShaderSource(shader, 1, sourceArray, nullptr);
                glCompileShader(shader);
                return shader;
            }
    
            bool checkShader(GLuint shader)
            {
                GLint compileResult;
                glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
    
                if (compileResult == 0)
                {
                    GLint infoLogLength;
                    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
    
                    // Info log length includes the null terminator, so 1 means that the info log is an
                    // empty string.
                    if (infoLogLength > 1)
                    {
                        std::vector<GLchar> infoLog(infoLogLength);
                        glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr,
                                           &infoLog[0]);
                        std::cerr << "shader compilation failed: " << &infoLog[0];
                    }
                    else
                    {
                        std::cerr << "shader compilation failed. <Empty log message>";
                    }
                    std::cerr << std::endl;
                }
                return (compileResult == GL_TRUE);
            }
    
            GLuint mProgram;
            int mID;
        };
    
        template <typename T>
        class TaskRunner
        {
          public:
            TaskRunner() {}
            ~TaskRunner() {}
    
            void run(ParallelShaderCompileTest *test)
            {
    
                std::vector<std::unique_ptr<T>> compileTasks;
                for (int i = 0; i < kTaskCount; ++i)
                {
                    std::unique_ptr<T> task(new T(i));
                    bool isCompiling = task->compile();
                    ASSERT_TRUE(isCompiling);
                    compileTasks.push_back(std::move(task));
                }
    
                std::vector<std::unique_ptr<T>> linkTasks;
                while (!compileTasks.empty())
                {
                    for (unsigned int i = 0; i < compileTasks.size();)
                    {
                        auto &task = compileTasks[i];
    
                        if (task->isCompileCompleted())
                        {
                            bool isLinking = task->link();
                            ASSERT_TRUE(isLinking);
                            linkTasks.push_back(std::move(task));
                            compileTasks.erase(compileTasks.begin() + i);
                            continue;
                        }
                        ++i;
                    }
                    angle::Sleep(kPollInterval);
                }
    
                while (!linkTasks.empty())
                {
                    for (unsigned int i = 0; i < linkTasks.size();)
                    {
                        auto &task = linkTasks[i];
    
                        if (task->isLinkCompleted())
                        {
                            task->runAndVerify(test);
                            linkTasks.erase(linkTasks.begin() + i);
                            continue;
                        }
                        ++i;
                    }
                    angle::Sleep(kPollInterval);
                }
            }
        };
    
        class ClearColorWithDraw : public Task
        {
          public:
            ClearColorWithDraw(int taskID) : Task(taskID)
            {
                auto color = static_cast<GLubyte>(taskID * 255 / kTaskCount);
                mColor     = {color, color, color, 255};
            }
    
            bool compile() override
            {
                mVertexShader =
                    CompileShader(GL_VERTEX_SHADER, insertRandomString(essl1_shaders::vs::Simple()));
                mFragmentShader = CompileShader(GL_FRAGMENT_SHADER,
                                                insertRandomString(essl1_shaders::fs::UniformColor()));
                return (mVertexShader != 0 && mFragmentShader != 0);
            }
    
            bool isCompileCompleted() override
            {
                GLint status;
                glGetShaderiv(mVertexShader, GL_COMPLETION_STATUS_KHR, &status);
                if (status == GL_TRUE)
                {
                    glGetShaderiv(mFragmentShader, GL_COMPLETION_STATUS_KHR, &status);
                    return (status == GL_TRUE);
                }
                return false;
            }
    
            bool link() override
            {
                mProgram = 0;
                if (checkShader(mVertexShader) && checkShader(mFragmentShader))
                {
                    mProgram = glCreateProgram();
                    glAttachShader(mProgram, mVertexShader);
                    glAttachShader(mProgram, mFragmentShader);
                    glLinkProgram(mProgram);
                }
                glDeleteShader(mVertexShader);
                glDeleteShader(mFragmentShader);
                return (mProgram != 0);
            }
    
            void runAndVerify(ParallelShaderCompileTest *test) override
            {
                glClearColor(0, 0, 0, 0);
                glClear(GL_COLOR_BUFFER_BIT);
                glDisable(GL_DEPTH_TEST);
                glUseProgram(mProgram);
                ASSERT_GL_NO_ERROR();
                GLint colorUniformLocation =
                    glGetUniformLocation(mProgram, essl1_shaders::ColorUniform());
                ASSERT_NE(colorUniformLocation, -1);
                auto normalizeColor = mColor.toNormalizedVector();
                glUniform4fv(colorUniformLocation, 1, normalizeColor.data());
                test->drawQuad(mProgram, essl1_shaders::PositionAttrib(), 0.5f);
                EXPECT_PIXEL_COLOR_EQ(test->getWindowWidth() / 2, test->getWindowHeight() / 2, mColor);
                glUseProgram(0);
                glDeleteProgram(mProgram);
                ASSERT_GL_NO_ERROR();
            }
    
          private:
            GLColor mColor;
            GLuint mVertexShader;
            GLuint mFragmentShader;
        };
    
        class ImageLoadStore : public Task
        {
          public:
            ImageLoadStore(int taskID) : Task(taskID) {}
            ~ImageLoadStore() {}
    
            bool compile() override
            {
                const char kCSSource[] = R"(#version 310 es
    layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
    layout(r32ui, binding = 0) readonly uniform highp uimage2D uImage_1;
    layout(r32ui, binding = 1) writeonly uniform highp uimage2D uImage_2;
    void main()
    {
        uvec4 value = imageLoad(uImage_1, ivec2(gl_LocalInvocationID.xy));
        imageStore(uImage_2, ivec2(gl_LocalInvocationID.xy), value);
    })";
    
                mShader = CompileShader(GL_COMPUTE_SHADER, insertRandomString(kCSSource));
                return mShader != 0;
            }
    
            bool isCompileCompleted() override
            {
                GLint status;
                glGetShaderiv(mShader, GL_COMPLETION_STATUS_KHR, &status);
                return status == GL_TRUE;
            }
    
            bool link() override
            {
                mProgram = 0;
                if (checkShader(mShader))
                {
                    mProgram = glCreateProgram();
                    glAttachShader(mProgram, mShader);
                    glLinkProgram(mProgram);
                }
                glDeleteShader(mShader);
                return mProgram != 0;
            }
    
            void runAndVerify(ParallelShaderCompileTest *test) override
            {
                // Taken from ComputeShaderTest.StoreImageThenLoad.
                constexpr GLuint kInputValues[3][1] = {{300}, {200}, {100}};
                GLTexture texture[3];
                glBindTexture(GL_TEXTURE_2D, texture[0]);
                glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
                                kInputValues[0]);
                EXPECT_GL_NO_ERROR();
    
                glBindTexture(GL_TEXTURE_2D, texture[1]);
                glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
                                kInputValues[1]);
                EXPECT_GL_NO_ERROR();
    
                glBindTexture(GL_TEXTURE_2D, texture[2]);
                glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32UI, 1, 1);
                glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT,
                                kInputValues[2]);
                EXPECT_GL_NO_ERROR();
    
                glUseProgram(mProgram);
    
                glBindImageTexture(0, texture[0], 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
                glBindImageTexture(1, texture[1], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
    
                glDispatchCompute(1, 1, 1);
                glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
                EXPECT_GL_NO_ERROR();
    
                glBindImageTexture(0, texture[1], 0, GL_FALSE, 0, GL_READ_ONLY, GL_R32UI);
                glBindImageTexture(1, texture[2], 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32UI);
    
                glDispatchCompute(1, 1, 1);
                glMemoryBarrier(GL_FRAMEBUFFER_BARRIER_BIT);
                EXPECT_GL_NO_ERROR();
    
                GLuint outputValue;
                GLFramebuffer framebuffer;
                glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
                glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
                                       texture[2], 0);
                glReadPixels(0, 0, 1, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, &outputValue);
                EXPECT_GL_NO_ERROR();
    
                EXPECT_EQ(300u, outputValue);
    
                glUseProgram(0);
                glDeleteProgram(mProgram);
                ASSERT_GL_NO_ERROR();
            }
    
          private:
            GLuint mShader;
        };
    };
    
    // Test basic functionality of GL_KHR_parallel_shader_compile
    TEST_P(ParallelShaderCompileTest, Basic)
    {
        ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
    
        GLint count = 0;
        glMaxShaderCompilerThreadsKHR(8);
        EXPECT_GL_NO_ERROR();
        glGetIntegerv(GL_MAX_SHADER_COMPILER_THREADS_KHR, &count);
        EXPECT_GL_NO_ERROR();
        EXPECT_EQ(8, count);
    }
    
    // Test to compile and link many programs in parallel.
    TEST_P(ParallelShaderCompileTest, LinkAndDrawManyPrograms)
    {
        ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
    
        TaskRunner<ClearColorWithDraw> runner;
        runner.run(this);
    }
    
    class ParallelShaderCompileTestES31 : public ParallelShaderCompileTest
    {};
    
    // Test to compile and link many computing programs in parallel.
    TEST_P(ParallelShaderCompileTestES31, LinkAndDispatchManyPrograms)
    {
        // Flaky on Win NVIDIA D3D11. http://anglebug.com/3359
        // Suspectable to the flakyness of http://anglebug.com/3349.
        ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
    
        // TODO(http://anglebug.com/5656): Fails on Linux+Intel+OpenGL
        ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsOpenGL());
    
        ANGLE_SKIP_TEST_IF(!ensureParallelShaderCompileExtensionAvailable());
    
        TaskRunner<ImageLoadStore> runner;
        runner.run(this);
    }
    
    ANGLE_INSTANTIATE_TEST_ES2(ParallelShaderCompileTest);
    
    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ParallelShaderCompileTestES31);
    ANGLE_INSTANTIATE_TEST_ES31(ParallelShaderCompileTestES31);
    
    }  // namespace