• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright 2018 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // MulithreadingTest.cpp : Tests of multithreaded rendering
7 
8 #include "test_utils/ANGLETest.h"
9 
10 #include "test_utils/gl_raii.h"
11 #include "util/EGLWindow.h"
12 
13 #include <mutex>
14 #include <thread>
15 
16 namespace angle
17 {
18 
19 class MultithreadingTest : public ANGLETest
20 {
21   protected:
MultithreadingTest()22     MultithreadingTest()
23     {
24         setWindowWidth(128);
25         setWindowHeight(128);
26         setConfigRedBits(8);
27         setConfigGreenBits(8);
28         setConfigBlueBits(8);
29         setConfigAlphaBits(8);
30     }
31 
runMultithreadedGLTest(std::function<void (EGLSurface surface,size_t threadIndex)> testBody,size_t threadCount)32     void runMultithreadedGLTest(
33         std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
34         size_t threadCount)
35     {
36         std::mutex mutex;
37 
38         EGLWindow *window = getEGLWindow();
39         EGLDisplay dpy    = window->getDisplay();
40         EGLConfig config  = window->getConfig();
41 
42         constexpr EGLint kPBufferSize = 256;
43 
44         std::vector<std::thread> threads(threadCount);
45         for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
46         {
47             threads[threadIdx] = std::thread([&, threadIdx]() {
48                 EGLSurface surface = EGL_NO_SURFACE;
49                 EGLContext ctx     = EGL_NO_CONTEXT;
50 
51                 {
52                     std::lock_guard<decltype(mutex)> lock(mutex);
53 
54                     // Initialize the pbuffer and context
55                     EGLint pbufferAttributes[] = {
56                         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
57                     };
58                     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
59                     EXPECT_EGL_SUCCESS();
60 
61                     ctx = window->createContext(EGL_NO_CONTEXT);
62                     EXPECT_NE(EGL_NO_CONTEXT, ctx);
63 
64                     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
65                     EXPECT_EGL_SUCCESS();
66                 }
67 
68                 testBody(surface, threadIdx);
69 
70                 {
71                     std::lock_guard<decltype(mutex)> lock(mutex);
72 
73                     // Clean up
74                     EXPECT_EGL_TRUE(
75                         eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
76                     EXPECT_EGL_SUCCESS();
77 
78                     eglDestroySurface(dpy, surface);
79                     eglDestroyContext(dpy, ctx);
80                 }
81             });
82         }
83 
84         for (std::thread &thread : threads)
85         {
86             thread.join();
87         }
88     }
89 };
90 
91 // Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest,MakeCurrentSingleContext)92 TEST_P(MultithreadingTest, MakeCurrentSingleContext)
93 {
94     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
95 
96     std::mutex mutex;
97 
98     EGLWindow *window  = getEGLWindow();
99     EGLDisplay dpy     = window->getDisplay();
100     EGLContext ctx     = window->getContext();
101     EGLSurface surface = window->getSurface();
102 
103     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
104     EXPECT_EGL_SUCCESS();
105 
106     constexpr size_t kThreadCount = 16;
107     std::array<std::thread, kThreadCount> threads;
108     for (std::thread &thread : threads)
109     {
110         thread = std::thread([&]() {
111             std::lock_guard<decltype(mutex)> lock(mutex);
112 
113             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
114             EXPECT_EGL_SUCCESS();
115 
116             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
117             EXPECT_EGL_SUCCESS();
118 
119             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
120             EXPECT_EGL_SUCCESS();
121         });
122     }
123 
124     for (std::thread &thread : threads)
125     {
126         thread.join();
127     }
128 
129     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
130     EXPECT_EGL_SUCCESS();
131 }
132 
133 // Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextClear)134 TEST_P(MultithreadingTest, MultiContextClear)
135 {
136     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
137 
138     auto testBody = [](EGLSurface surface, size_t thread) {
139         constexpr size_t kIterationsPerThread = 32;
140         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
141         {
142             // Base the clear color on the thread and iteration indexes so every clear color is
143             // unique
144             const GLColor color(static_cast<GLubyte>(thread % 255),
145                                 static_cast<GLubyte>(iteration % 255), 0, 255);
146             const angle::Vector4 floatColor = color.toNormalizedVector();
147 
148             glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
149             EXPECT_GL_NO_ERROR();
150 
151             glClear(GL_COLOR_BUFFER_BIT);
152             EXPECT_GL_NO_ERROR();
153 
154             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
155         }
156     };
157     runMultithreadedGLTest(testBody, 72);
158 }
159 
160 // Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextDraw)161 TEST_P(MultithreadingTest, MultiContextDraw)
162 {
163     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
164 
165     auto testBody = [](EGLSurface surface, size_t thread) {
166         constexpr size_t kIterationsPerThread = 32;
167         constexpr size_t kDrawsPerIteration   = 500;
168 
169         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
170         glUseProgram(program);
171 
172         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
173 
174         auto quadVertices = GetQuadVertices();
175 
176         GLBuffer vertexBuffer;
177         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
178         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
179 
180         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
181         glEnableVertexAttribArray(positionLocation);
182         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
183 
184         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
185         {
186             // Base the clear color on the thread and iteration indexes so every clear color is
187             // unique
188             const GLColor color(static_cast<GLubyte>(thread % 255),
189                                 static_cast<GLubyte>(iteration % 255), 0, 255);
190             const angle::Vector4 floatColor = color.toNormalizedVector();
191             glUniform4fv(colorLocation, 1, floatColor.data());
192 
193             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
194             {
195                 glDrawArrays(GL_TRIANGLES, 0, 6);
196             }
197 
198             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
199         }
200     };
201     runMultithreadedGLTest(testBody, 4);
202 }
203 
204 // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
205 
206 ANGLE_INSTANTIATE_TEST(MultithreadingTest,
207                        WithNoVirtualContexts(ES2_OPENGL()),
208                        WithNoVirtualContexts(ES3_OPENGL()),
209                        WithNoVirtualContexts(ES2_OPENGLES()),
210                        WithNoVirtualContexts(ES3_OPENGLES()));
211 
212 }  // namespace angle
213