Edit

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

Branch :

  • Show log

    Commit

  • Author : Jonah Ryan-Davis
    Date : 2020-07-08 13:48:54
    Hash : 514c8f64
    Message : Skip MultithreadingTest.MultiCreateContext on Ozone This tests crashes on Ozone, skip for now. Bug: chromium:1103009 Change-Id: Ie686e69d937710b43b615372251801e83b7bde20 TBR=geofflang@chromium.org Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2288334 Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Commit-Queue: Jonah Ryan-Davis <jonahr@google.com>

  • 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 <atomic>
    #include <mutex>
    #include <thread>
    
    namespace angle
    {
    
    class MultithreadingTest : public ANGLETest
    {
      protected:
        MultithreadingTest()
        {
            setWindowWidth(128);
            setWindowHeight(128);
            setConfigRedBits(8);
            setConfigGreenBits(8);
            setConfigBlueBits(8);
            setConfigAlphaBits(8);
        }
    
        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;
                    EGLContext 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);
    }
    
    TEST_P(MultithreadingTest, MultiCreateContext)
    {
        // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
        // Not supported on Ozone (https://crbug.com/1103009)
        ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsOSX()) || IsOzone());
    
        EGLWindow *window  = getEGLWindow();
        EGLDisplay dpy     = window->getDisplay();
        EGLContext ctx     = window->getContext();
        EGLSurface surface = window->getSurface();
    
        // Un-makeCurrent the test window's context
        EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
        EXPECT_EGL_SUCCESS();
    
        constexpr size_t kThreadCount = 16;
        std::atomic<uint32_t> barrier(0);
        std::vector<std::thread> threads(kThreadCount);
        std::vector<EGLContext> contexts(kThreadCount);
        for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
        {
            threads[threadIdx] = std::thread([&, threadIdx]() {
                contexts[threadIdx] = EGL_NO_CONTEXT;
                {
                    contexts[threadIdx] = window->createContext(EGL_NO_CONTEXT);
                    EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
    
                    barrier++;
                }
    
                while (barrier < kThreadCount)
                {
                }
    
                {
                    EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
                }
            });
        }
    
        for (std::thread &thread : threads)
        {
            thread.join();
        }
    
        // Re-make current the test window's context for teardown.
        EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
        EXPECT_EGL_SUCCESS();
    }
    
    // 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