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