| // | |
| // 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); | |
| } | |
| 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); | |
| } | |
| // 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 |