Edit

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

Branch :

  • Show log

    Commit

  • Author : Geoff Lang
    Date : 2019-05-01 13:58:27
    Hash : ded5f903
    Message : Vulkan: Make the Vulkan renderer thread safe. Gate all access to the queue and caches with mutexes. Does not handle sharing of resources in share groups across threads yet. BUG=angleproject:2464 Change-Id: I297f8f1a535b99efca663cf72bac3d90df8b5d97 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1592253 Commit-Queue: Geoff Lang <geofflang@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>

  • src/tests/gl_tests/MultithreadingTest.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.
    //
    // MulithreadingTest.cpp : Tests of multithreaded rendering
    
    #include "test_utils/ANGLETest.h"
    
    #include "test_utils/gl_raii.h"
    #include "util/EGLWindow.h"
    
    #include <mutex>
    #include <thread>
    
    namespace angle
    {
    
    class MultithreadingTest : public ANGLETest
    {
      protected:
        MultithreadingTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        bool platformSupportsMultithreading() const
        {
            return (IsOpenGLES() && IsAndroid()) || IsVulkan();
        }
    
        void runMultithreadedGLTest(
            std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
            size_t threadCount)
        {
            std::mutex mutex;
    
            EGLWindow *window = getEGLWindow();
            EGLDisplay dpy    = window->getDisplay();
            EGLConfig config  = window->getConfig();
    
            constexpr EGLint kPBufferSize = 256;
    
            std::vector<std::thread> threads(threadCount);
            for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
            {
                threads[threadIdx] = std::thread([&, threadIdx]() {
                    EGLSurface surface = EGL_NO_SURFACE;
                    EGLConfig ctx      = EGL_NO_CONTEXT;
    
                    {
                        std::lock_guard<decltype(mutex)> lock(mutex);
    
                        // Initialize the pbuffer and context
                        EGLint pbufferAttributes[] = {
                            EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
                        };
                        surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
                        EXPECT_EGL_SUCCESS();
    
                        ctx = window->createContext(EGL_NO_CONTEXT);
                        EXPECT_NE(EGL_NO_CONTEXT, ctx);
    
                        EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
                        EXPECT_EGL_SUCCESS();
                    }
    
                    testBody(surface, threadIdx);
    
                    {
                        std::lock_guard<decltype(mutex)> lock(mutex);
    
                        // Clean up
                        EXPECT_EGL_TRUE(
                            eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
                        EXPECT_EGL_SUCCESS();
    
                        eglDestroySurface(dpy, surface);
                        eglDestroyContext(dpy, ctx);
                    }
                });
            }
    
            for (std::thread &thread : threads)
            {
                thread.join();
            }
        }
    };
    
    // Test that it's possible to make one context current on different threads
    TEST_P(MultithreadingTest, MakeCurrentSingleContext)
    {
        ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
    
        std::mutex mutex;
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay dpy     = window->getDisplay();
        EGLContext ctx     = window->getContext();
        EGLSurface surface = window->getSurface();
    
        EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
        EXPECT_EGL_SUCCESS();
    
        constexpr size_t kThreadCount = 16;
        std::array<std::thread, kThreadCount> threads;
        for (std::thread &thread : threads)
        {
            thread = std::thread([&]() {
                std::lock_guard<decltype(mutex)> lock(mutex);
    
                EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
                EXPECT_EGL_SUCCESS();
    
                EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
                EXPECT_EGL_SUCCESS();
    
                EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
                EXPECT_EGL_SUCCESS();
            });
        }
    
        for (std::thread &thread : threads)
        {
            thread.join();
        }
    
        EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
        EXPECT_EGL_SUCCESS();
    }
    
    // Test that multiple threads can clear and readback pixels successfully at the same time
    TEST_P(MultithreadingTest, MultiContextClear)
    {
        ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
    
        auto testBody = [](EGLSurface surface, size_t thread) {
            constexpr size_t kIterationsPerThread = 32;
            for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
            {
                // Base the clear color on the thread and iteration indexes so every clear color is
                // unique
                const GLColor color(static_cast<GLubyte>(thread % 255),
                                    static_cast<GLubyte>(iteration % 255), 0, 255);
                const angle::Vector4 floatColor = color.toNormalizedVector();
    
                glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
                EXPECT_GL_NO_ERROR();
    
                glClear(GL_COLOR_BUFFER_BIT);
                EXPECT_GL_NO_ERROR();
    
                EXPECT_PIXEL_COLOR_EQ(0, 0, color);
            }
        };
        runMultithreadedGLTest(testBody, 72);
    }
    
    // Test that multiple threads can draw and readback pixels successfully at the same time
    TEST_P(MultithreadingTest, MultiContextDraw)
    {
        ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
    
        auto testBody = [](EGLSurface surface, size_t thread) {
            constexpr size_t kIterationsPerThread = 32;
            constexpr size_t kDrawsPerIteration   = 500;
    
            ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
            glUseProgram(program);
    
            GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
    
            auto quadVertices = GetQuadVertices();
    
            GLBuffer vertexBuffer;
            glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
            glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
    
            GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
            glEnableVertexAttribArray(positionLocation);
            glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
    
            for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
            {
                // Base the clear color on the thread and iteration indexes so every clear color is
                // unique
                const GLColor color(static_cast<GLubyte>(thread % 255),
                                    static_cast<GLubyte>(iteration % 255), 0, 255);
                const angle::Vector4 floatColor = color.toNormalizedVector();
                glUniform4fv(colorLocation, 1, floatColor.data());
    
                for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
                {
                    glDrawArrays(GL_TRIANGLES, 0, 6);
                }
    
                EXPECT_PIXEL_COLOR_EQ(0, 0, color);
            }
        };
        runMultithreadedGLTest(testBody, 4);
    }
    
    // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
    
    ANGLE_INSTANTIATE_TEST(MultithreadingTest,
                           WithNoVirtualContexts(ES2_OPENGL()),
                           WithNoVirtualContexts(ES3_OPENGL()),
                           WithNoVirtualContexts(ES2_OPENGLES()),
                           WithNoVirtualContexts(ES3_OPENGLES()));
    
    }  // namespace angle