| // |
| // 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 <mutex> |
| #include <thread> |
| |
| namespace angle |
| { |
| |
| class MultithreadingTest : public ANGLETest |
| { |
| protected: |
| MultithreadingTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| setContextVirtualization(false); |
| } |
| |
| bool platformSupportsMultithreading() const { return (IsOpenGLES() && IsAndroid()); } |
| }; |
| |
| // 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 it's possible to make one multiple contexts current on different threads simultaneously |
| TEST_P(MultithreadingTest, MakeCurrentMultiContext) |
| { |
| ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading()); |
| ANGLE_SKIP_TEST_IF(IsWindows() && IsOpenGL() && IsAMD()); |
| |
| std::mutex mutex; |
| |
| EGLWindow *window = getEGLWindow(); |
| EGLDisplay dpy = window->getDisplay(); |
| EGLConfig config = window->getConfig(); |
| |
| constexpr size_t kThreadCount = 16; |
| constexpr size_t kIterationsPerThread = 16; |
| |
| constexpr EGLint kPBufferSize = 256; |
| |
| std::array<std::thread, kThreadCount> threads; |
| for (size_t thread = 0; thread < kThreadCount; thread++) |
| { |
| threads[thread] = std::thread([&, thread]() { |
| EGLSurface pbuffer = 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, |
| }; |
| pbuffer = eglCreatePbufferSurface(dpy, config, pbufferAttributes); |
| EXPECT_EGL_SUCCESS(); |
| |
| ctx = window->createContext(EGL_NO_CONTEXT); |
| EXPECT_NE(EGL_NO_CONTEXT, ctx); |
| |
| EXPECT_EGL_TRUE(eglMakeCurrent(dpy, pbuffer, pbuffer, ctx)); |
| EXPECT_EGL_SUCCESS(); |
| } |
| |
| for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++) |
| { |
| std::lock_guard<decltype(mutex)> lock(mutex); |
| |
| // Base the clear color on the thread and iteration indexes so every clear color is |
| // unique |
| const GLColor color(static_cast<GLubyte>(thread), static_cast<GLubyte>(iteration), |
| 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); |
| } |
| |
| { |
| 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, pbuffer); |
| eglDestroyContext(dpy, ctx); |
| } |
| }); |
| } |
| |
| for (std::thread &thread : threads) |
| { |
| thread.join(); |
| } |
| } |
| |
| // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads |
| |
| ANGLE_INSTANTIATE_TEST(MultithreadingTest, |
| ES2_OPENGL(), |
| ES3_OPENGL(), |
| ES2_OPENGLES(), |
| ES3_OPENGLES(), |
| ES2_VULKAN()); |
| |
| } // namespace angle |