@@ -29,7 +29,68 @@ class MultithreadingTest : public ANGLETest
29
29
setConfigAlphaBits (8 );
30
30
}
31
31
32
- bool platformSupportsMultithreading () const { return (IsOpenGLES () && IsAndroid ()); }
32
+ bool platformSupportsMultithreading () const
33
+ {
34
+ return (IsOpenGLES () && IsAndroid ()) || IsVulkan ();
35
+ }
36
+
37
+ void runMultithreadedGLTest (
38
+ std::function<void (EGLSurface surface, size_t threadIndex)> testBody,
39
+ size_t threadCount)
40
+ {
41
+ std::mutex mutex;
42
+
43
+ EGLWindow *window = getEGLWindow ();
44
+ EGLDisplay dpy = window->getDisplay ();
45
+ EGLConfig config = window->getConfig ();
46
+
47
+ constexpr EGLint kPBufferSize = 256 ;
48
+
49
+ std::vector<std::thread> threads (threadCount);
50
+ for (size_t threadIdx = 0 ; threadIdx < threadCount; threadIdx++)
51
+ {
52
+ threads[threadIdx] = std::thread ([&, threadIdx]() {
53
+ EGLSurface surface = EGL_NO_SURFACE;
54
+ EGLConfig ctx = EGL_NO_CONTEXT;
55
+
56
+ {
57
+ std::lock_guard<decltype (mutex)> lock (mutex);
58
+
59
+ // Initialize the pbuffer and context
60
+ EGLint pbufferAttributes[] = {
61
+ EGL_WIDTH, kPBufferSize , EGL_HEIGHT, kPBufferSize , EGL_NONE, EGL_NONE,
62
+ };
63
+ surface = eglCreatePbufferSurface (dpy, config, pbufferAttributes);
64
+ EXPECT_EGL_SUCCESS ();
65
+
66
+ ctx = window->createContext (EGL_NO_CONTEXT);
67
+ EXPECT_NE (EGL_NO_CONTEXT, ctx);
68
+
69
+ EXPECT_EGL_TRUE (eglMakeCurrent (dpy, surface, surface, ctx));
70
+ EXPECT_EGL_SUCCESS ();
71
+ }
72
+
73
+ testBody (surface, threadIdx);
74
+
75
+ {
76
+ std::lock_guard<decltype (mutex)> lock (mutex);
77
+
78
+ // Clean up
79
+ EXPECT_EGL_TRUE (
80
+ eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
81
+ EXPECT_EGL_SUCCESS ();
82
+
83
+ eglDestroySurface (dpy, surface);
84
+ eglDestroyContext (dpy, ctx);
85
+ }
86
+ });
87
+ }
88
+
89
+ for (std::thread &thread : threads)
90
+ {
91
+ thread.join ();
92
+ }
93
+ }
33
94
};
34
95
35
96
// Test that it's possible to make one context current on different threads
@@ -74,84 +135,75 @@ TEST_P(MultithreadingTest, MakeCurrentSingleContext)
74
135
EXPECT_EGL_SUCCESS ();
75
136
}
76
137
77
- // Test that it's possible to make one multiple contexts current on different threads simultaneously
78
- TEST_P (MultithreadingTest, MakeCurrentMultiContext )
138
+ // Test that multiple threads can clear and readback pixels successfully at the same time
139
+ TEST_P (MultithreadingTest, MultiContextClear )
79
140
{
80
141
ANGLE_SKIP_TEST_IF (!platformSupportsMultithreading ());
81
- ANGLE_SKIP_TEST_IF (IsWindows () && IsOpenGL () && IsAMD ());
82
-
83
- std::mutex mutex;
84
142
85
- EGLWindow *window = getEGLWindow ();
86
- EGLDisplay dpy = window->getDisplay ();
87
- EGLConfig config = window->getConfig ();
88
-
89
- constexpr size_t kThreadCount = 16 ;
90
- constexpr size_t kIterationsPerThread = 16 ;
91
-
92
- constexpr EGLint kPBufferSize = 256 ;
93
-
94
- std::array<std::thread, kThreadCount > threads;
95
- for (size_t thread = 0 ; thread < kThreadCount ; thread++)
96
- {
97
- threads[thread] = std::thread ([&, thread]() {
98
- EGLSurface pbuffer = EGL_NO_SURFACE;
99
- EGLConfig ctx = EGL_NO_CONTEXT;
100
-
101
- {
102
- std::lock_guard<decltype (mutex)> lock (mutex);
143
+ auto testBody = [](EGLSurface surface, size_t thread) {
144
+ constexpr size_t kIterationsPerThread = 32 ;
145
+ for (size_t iteration = 0 ; iteration < kIterationsPerThread ; iteration++)
146
+ {
147
+ // Base the clear color on the thread and iteration indexes so every clear color is
148
+ // unique
149
+ const GLColor color (static_cast <GLubyte>(thread % 255 ),
150
+ static_cast <GLubyte>(iteration % 255 ), 0 , 255 );
151
+ const angle::Vector4 floatColor = color.toNormalizedVector ();
152
+
153
+ glClearColor (floatColor[0 ], floatColor[1 ], floatColor[2 ], floatColor[3 ]);
154
+ EXPECT_GL_NO_ERROR ();
155
+
156
+ glClear (GL_COLOR_BUFFER_BIT);
157
+ EXPECT_GL_NO_ERROR ();
158
+
159
+ EXPECT_PIXEL_COLOR_EQ (0 , 0 , color);
160
+ }
161
+ };
162
+ runMultithreadedGLTest (testBody, 72 );
163
+ }
103
164
104
- // Initialize the pbuffer and context
105
- EGLint pbufferAttributes[] = {
106
- EGL_WIDTH, kPBufferSize , EGL_HEIGHT, kPBufferSize , EGL_NONE, EGL_NONE,
107
- };
108
- pbuffer = eglCreatePbufferSurface (dpy, config, pbufferAttributes);
109
- EXPECT_EGL_SUCCESS ();
165
+ // Test that multiple threads can draw and readback pixels successfully at the same time
166
+ TEST_P (MultithreadingTest, MultiContextDraw)
167
+ {
168
+ ANGLE_SKIP_TEST_IF (!platformSupportsMultithreading ());
110
169
111
- ctx = window->createContext (EGL_NO_CONTEXT);
112
- EXPECT_NE (EGL_NO_CONTEXT, ctx);
170
+ auto testBody = [](EGLSurface surface, size_t thread) {
171
+ constexpr size_t kIterationsPerThread = 32 ;
172
+ constexpr size_t kDrawsPerIteration = 500 ;
113
173
114
- EXPECT_EGL_TRUE (eglMakeCurrent (dpy, pbuffer, pbuffer, ctx));
115
- EXPECT_EGL_SUCCESS ();
116
- }
174
+ ANGLE_GL_PROGRAM (program, essl1_shaders::vs::Simple (), essl1_shaders::fs::UniformColor ());
175
+ glUseProgram (program);
117
176
118
- for (size_t iteration = 0 ; iteration < kIterationsPerThread ; iteration++)
119
- {
120
- std::lock_guard<decltype (mutex)> lock (mutex);
177
+ GLint colorLocation = glGetUniformLocation (program, essl1_shaders::ColorUniform ());
121
178
122
- // Base the clear color on the thread and iteration indexes so every clear color is
123
- // unique
124
- const GLColor color (static_cast <GLubyte>(thread), static_cast <GLubyte>(iteration),
125
- 0 , 255 );
126
- const angle::Vector4 floatColor = color.toNormalizedVector ();
179
+ auto quadVertices = GetQuadVertices ();
127
180
128
- glClearColor (floatColor[0 ], floatColor[1 ], floatColor[2 ], floatColor[3 ]);
129
- EXPECT_GL_NO_ERROR ();
181
+ GLBuffer vertexBuffer;
182
+ glBindBuffer (GL_ARRAY_BUFFER, vertexBuffer);
183
+ glBufferData (GL_ARRAY_BUFFER, sizeof (GLfloat) * 3 * 6 , quadVertices.data (), GL_STATIC_DRAW);
130
184
131
- glClear (GL_COLOR_BUFFER_BIT);
132
- EXPECT_GL_NO_ERROR ();
185
+ GLint positionLocation = glGetAttribLocation (program, essl1_shaders::PositionAttrib ());
186
+ glEnableVertexAttribArray (positionLocation);
187
+ glVertexAttribPointer (positionLocation, 3 , GL_FLOAT, GL_FALSE, 0 , 0 );
133
188
134
- EXPECT_PIXEL_COLOR_EQ (0 , 0 , color);
135
- }
189
+ for (size_t iteration = 0 ; iteration < kIterationsPerThread ; iteration++)
190
+ {
191
+ // Base the clear color on the thread and iteration indexes so every clear color is
192
+ // unique
193
+ const GLColor color (static_cast <GLubyte>(thread % 255 ),
194
+ static_cast <GLubyte>(iteration % 255 ), 0 , 255 );
195
+ const angle::Vector4 floatColor = color.toNormalizedVector ();
196
+ glUniform4fv (colorLocation, 1 , floatColor.data ());
136
197
198
+ for (size_t draw = 0 ; draw < kDrawsPerIteration ; draw++)
137
199
{
138
- std::lock_guard<decltype (mutex)> lock (mutex);
139
-
140
- // Clean up
141
- EXPECT_EGL_TRUE (
142
- eglMakeCurrent (dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
143
- EXPECT_EGL_SUCCESS ();
144
-
145
- eglDestroySurface (dpy, pbuffer);
146
- eglDestroyContext (dpy, ctx);
200
+ glDrawArrays (GL_TRIANGLES, 0 , 6 );
147
201
}
148
- });
149
- }
150
202
151
- for (std::thread &thread : threads)
152
- {
153
- thread. join () ;
154
- }
203
+ EXPECT_PIXEL_COLOR_EQ ( 0 , 0 , color);
204
+ }
205
+ } ;
206
+ runMultithreadedGLTest (testBody, 4 );
155
207
}
156
208
157
209
// TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
0 commit comments