• 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 "platform/FeaturesVk.h"
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/MultiThreadSteps.h"
11 #include "test_utils/gl_raii.h"
12 #include "util/EGLWindow.h"
13 #include "util/test_utils.h"
14 
15 #include <atomic>
16 #include <mutex>
17 #include <thread>
18 
19 namespace angle
20 {
21 
22 class MultithreadingTest : public ANGLETest
23 {
24   public:
25     static constexpr uint32_t kSize = 512;
26 
27   protected:
MultithreadingTest()28     MultithreadingTest()
29     {
30         setWindowWidth(kSize);
31         setWindowHeight(kSize);
32         setConfigRedBits(8);
33         setConfigGreenBits(8);
34         setConfigBlueBits(8);
35         setConfigAlphaBits(8);
36     }
37 
hasFenceSyncExtension() const38     bool hasFenceSyncExtension() const
39     {
40         return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_fence_sync");
41     }
hasGLSyncExtension() const42     bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); }
43 
createMultithreadedContext(EGLWindow * window,EGLContext shareCtx)44     EGLContext createMultithreadedContext(EGLWindow *window, EGLContext shareCtx)
45     {
46         EGLint attribs[] = {EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, mVirtualizationGroup++,
47                             EGL_NONE};
48         if (!IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
49                                           "EGL_ANGLE_context_virtualization"))
50         {
51             attribs[0] = EGL_NONE;
52         }
53 
54         return window->createContext(shareCtx, attribs);
55     }
56 
runMultithreadedGLTest(std::function<void (EGLSurface surface,size_t threadIndex)> testBody,size_t threadCount)57     void runMultithreadedGLTest(
58         std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
59         size_t threadCount)
60     {
61         std::mutex mutex;
62 
63         EGLWindow *window = getEGLWindow();
64         EGLDisplay dpy    = window->getDisplay();
65         EGLConfig config  = window->getConfig();
66 
67         constexpr EGLint kPBufferSize = 256;
68 
69         std::vector<std::thread> threads(threadCount);
70         for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
71         {
72             threads[threadIdx] = std::thread([&, threadIdx]() {
73                 EGLSurface surface = EGL_NO_SURFACE;
74                 EGLContext ctx     = EGL_NO_CONTEXT;
75 
76                 {
77                     std::lock_guard<decltype(mutex)> lock(mutex);
78 
79                     // Initialize the pbuffer and context
80                     EGLint pbufferAttributes[] = {
81                         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
82                     };
83                     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
84                     EXPECT_EGL_SUCCESS();
85 
86                     ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
87                     EXPECT_NE(EGL_NO_CONTEXT, ctx);
88 
89                     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
90                     EXPECT_EGL_SUCCESS();
91                 }
92 
93                 testBody(surface, threadIdx);
94 
95                 {
96                     std::lock_guard<decltype(mutex)> lock(mutex);
97 
98                     // Clean up
99                     EXPECT_EGL_TRUE(
100                         eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
101                     EXPECT_EGL_SUCCESS();
102 
103                     eglDestroySurface(dpy, surface);
104                     eglDestroyContext(dpy, ctx);
105                 }
106             });
107         }
108 
109         for (std::thread &thread : threads)
110         {
111             thread.join();
112         }
113     }
114 
115     std::atomic<EGLint> mVirtualizationGroup;
116 };
117 
118 class MultithreadingTestES3 : public MultithreadingTest
119 {
120   public:
121     void textureThreadFunction(bool useDraw);
122     void mainThreadDraw(bool useDraw);
123 
124   protected:
MultithreadingTestES3()125     MultithreadingTestES3()
126         : mTexture2D(0), mExitThread(false), mMainThreadSyncObj(NULL), mSecondThreadSyncObj(NULL)
127     {
128         setWindowWidth(kSize);
129         setWindowHeight(kSize);
130         setConfigRedBits(8);
131         setConfigGreenBits(8);
132         setConfigBlueBits(8);
133         setConfigAlphaBits(8);
134     }
135 
create2DTexture()136     GLuint create2DTexture()
137     {
138         GLuint texture2D;
139         glGenTextures(1, &texture2D);
140         glActiveTexture(GL_TEXTURE0);
141         glBindTexture(GL_TEXTURE_2D, texture2D);
142         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
143                      nullptr);
144         EXPECT_GL_NO_ERROR();
145         return texture2D;
146     }
147 
testSetUp()148     void testSetUp() override { mTexture2D = create2DTexture(); }
149 
testTearDown()150     void testTearDown() override
151     {
152         if (mTexture2D)
153         {
154             glDeleteTextures(1, &mTexture2D);
155         }
156     }
157 
158     std::mutex mutex;
159     GLuint mTexture2D;
160     std::atomic<bool> mExitThread;
161     std::atomic<bool> mDrawGreen;  // Toggle drawing green or red
162     std::atomic<GLsync> mMainThreadSyncObj;
163     std::atomic<GLsync> mSecondThreadSyncObj;
164 };
165 
166 // Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest,MakeCurrentSingleContext)167 TEST_P(MultithreadingTest, MakeCurrentSingleContext)
168 {
169     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
170 
171     std::mutex mutex;
172 
173     EGLWindow *window  = getEGLWindow();
174     EGLDisplay dpy     = window->getDisplay();
175     EGLContext ctx     = window->getContext();
176     EGLSurface surface = window->getSurface();
177 
178     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
179     EXPECT_EGL_SUCCESS();
180 
181     constexpr size_t kThreadCount = 16;
182     std::array<std::thread, kThreadCount> threads;
183     for (std::thread &thread : threads)
184     {
185         thread = std::thread([&]() {
186             std::lock_guard<decltype(mutex)> lock(mutex);
187 
188             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
189             EXPECT_EGL_SUCCESS();
190 
191             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
192             EXPECT_EGL_SUCCESS();
193 
194             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
195             EXPECT_EGL_SUCCESS();
196         });
197     }
198 
199     for (std::thread &thread : threads)
200     {
201         thread.join();
202     }
203 
204     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
205     EXPECT_EGL_SUCCESS();
206 }
207 
208 // Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextClear)209 TEST_P(MultithreadingTest, MultiContextClear)
210 {
211     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
212 
213     auto testBody = [](EGLSurface surface, size_t thread) {
214         constexpr size_t kIterationsPerThread = 32;
215         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
216         {
217             // Base the clear color on the thread and iteration indexes so every clear color is
218             // unique
219             const GLColor color(static_cast<GLubyte>(thread % 255),
220                                 static_cast<GLubyte>(iteration % 255), 0, 255);
221             const angle::Vector4 floatColor = color.toNormalizedVector();
222 
223             glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
224             EXPECT_GL_NO_ERROR();
225 
226             glClear(GL_COLOR_BUFFER_BIT);
227             EXPECT_GL_NO_ERROR();
228 
229             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
230         }
231     };
232     runMultithreadedGLTest(testBody, 72);
233 }
234 
235 // Verify that threads can interleave eglDestroyContext and draw calls without
236 // any crashes.
TEST_P(MultithreadingTest,MultiContextDeleteDraw)237 TEST_P(MultithreadingTest, MultiContextDeleteDraw)
238 {
239     // Skip this test on non-D3D11 backends, as it has the potential to time-out
240     // and this test was originally intended to catch a crash on the D3D11 backend.
241     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
242     ANGLE_SKIP_TEST_IF(!IsD3D11());
243 
244     EGLWindow *window = getEGLWindow();
245     EGLDisplay dpy    = window->getDisplay();
246     EGLConfig config  = window->getConfig();
247 
248     std::thread t1 = std::thread([&]() {
249         // 5000 is chosen here as it reliably reproduces the former crash.
250         for (int i = 0; i < 5000; i++)
251         {
252             EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
253             EGLContext ctx2 = createMultithreadedContext(window, EGL_NO_CONTEXT);
254 
255             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
256             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
257 
258             EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
259             EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
260         }
261     });
262 
263     std::thread t2 = std::thread([&]() {
264         EGLint pbufferAttributes[] = {
265             EGL_WIDTH, 256, EGL_HEIGHT, 256, EGL_NONE, EGL_NONE,
266         };
267 
268         EGLSurface surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
269         EXPECT_EGL_SUCCESS();
270 
271         auto ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
272         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
273 
274         constexpr size_t kIterationsPerThread = 512;
275         constexpr size_t kDrawsPerIteration   = 512;
276 
277         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
278         glUseProgram(program);
279 
280         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
281 
282         auto quadVertices = GetQuadVertices();
283 
284         GLBuffer vertexBuffer;
285         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
286         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
287 
288         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
289         glEnableVertexAttribArray(positionLocation);
290         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
291         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
292         {
293             const GLColor color(static_cast<GLubyte>(15151 % 255),
294                                 static_cast<GLubyte>(iteration % 255), 0, 255);
295             const angle::Vector4 floatColor = color.toNormalizedVector();
296             glUniform4fv(colorLocation, 1, floatColor.data());
297             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
298             {
299                 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
300                 glDrawArrays(GL_TRIANGLES, 0, 6);
301             }
302         }
303     });
304 
305     t1.join();
306     t2.join();
307 }
308 
309 // Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextDraw)310 TEST_P(MultithreadingTest, MultiContextDraw)
311 {
312     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
313 
314     ANGLE_SKIP_TEST_IF(isSwiftshader());
315 
316     auto testBody = [](EGLSurface surface, size_t thread) {
317         constexpr size_t kIterationsPerThread = 32;
318         constexpr size_t kDrawsPerIteration   = 500;
319 
320         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
321         glUseProgram(program);
322 
323         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
324 
325         auto quadVertices = GetQuadVertices();
326 
327         GLBuffer vertexBuffer;
328         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
329         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
330 
331         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
332         glEnableVertexAttribArray(positionLocation);
333         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
334 
335         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
336         {
337             // Base the clear color on the thread and iteration indexes so every clear color is
338             // unique
339             const GLColor color(static_cast<GLubyte>(thread % 255),
340                                 static_cast<GLubyte>(iteration % 255), 0, 255);
341             const angle::Vector4 floatColor = color.toNormalizedVector();
342             glUniform4fv(colorLocation, 1, floatColor.data());
343 
344             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
345             {
346                 glDrawArrays(GL_TRIANGLES, 0, 6);
347             }
348 
349             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
350         }
351     };
352     runMultithreadedGLTest(testBody, 4);
353 }
354 
355 // Test that multiple threads can draw and readback pixels successfully at the same time while EGL
356 // images are bound between them
TEST_P(MultithreadingTest,MultiContextDrawWithEGLImage)357 TEST_P(MultithreadingTest, MultiContextDrawWithEGLImage)
358 {
359     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
360 
361     ANGLE_SKIP_TEST_IF(isSwiftshader());
362 
363     EGLWindow *window = getEGLWindow();
364     EGLDisplay dpy    = window->getDisplay();
365     ANGLE_SKIP_TEST_IF(IsGLExtensionEnabled("GL_OES_EGL_image"));
366     ANGLE_SKIP_TEST_IF(IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
367 
368     // Create a source 2D texture
369     GLTexture sourceTexture;
370     glBindTexture(GL_TEXTURE_2D, sourceTexture);
371     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red);
372 
373     // Disable mipmapping
374     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
375     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
376 
377     ASSERT_GL_NO_ERROR();
378 
379     // Create an image from the source texture
380     constexpr EGLint attribs[] = {
381         EGL_IMAGE_PRESERVED,
382         EGL_TRUE,
383         EGL_NONE,
384     };
385     EGLImageKHR image = eglCreateImageKHR(
386         dpy, window->getContext(), EGL_GL_TEXTURE_2D_KHR,
387         reinterpret_cast<EGLClientBuffer>(static_cast<size_t>(sourceTexture.get())), attribs);
388 
389     auto testBody = [image](EGLSurface surface, size_t thread) {
390         constexpr size_t kIterationsPerThread = 32;
391         constexpr size_t kDrawsPerIteration   = 500;
392 
393         // Just bind the EGL image to this context and leave it bound while drawing
394         GLTexture destTexture;
395         glBindTexture(GL_TEXTURE_EXTERNAL_OES, destTexture);
396         glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, image);
397 
398         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
399         glUseProgram(program);
400 
401         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
402 
403         auto quadVertices = GetQuadVertices();
404 
405         GLBuffer vertexBuffer;
406         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
407         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
408 
409         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
410         glEnableVertexAttribArray(positionLocation);
411         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
412 
413         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
414         {
415             // Base the clear color on the thread and iteration indexes so every clear color is
416             // unique
417             const GLColor color(static_cast<GLubyte>(thread % 255),
418                                 static_cast<GLubyte>(iteration % 255), 0, 255);
419             const angle::Vector4 floatColor = color.toNormalizedVector();
420             glUniform4fv(colorLocation, 1, floatColor.data());
421 
422             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
423             {
424                 glDrawArrays(GL_TRIANGLES, 0, 6);
425             }
426 
427             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
428         }
429     };
430     runMultithreadedGLTest(testBody, 4);
431 
432     eglDestroyImage(dpy, image);
433 }
434 
435 // Test that multiple threads can draw and read back pixels correctly.
436 // Using eglSwapBuffers stresses race conditions around use of QueueSerials.
TEST_P(MultithreadingTest,MultiContextDrawWithSwapBuffers)437 TEST_P(MultithreadingTest, MultiContextDrawWithSwapBuffers)
438 {
439     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
440 
441     // http://anglebug.com/5099
442     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
443 
444     EGLWindow *window = getEGLWindow();
445     EGLDisplay dpy    = window->getDisplay();
446 
447     auto testBody = [dpy](EGLSurface surface, size_t thread) {
448         constexpr size_t kIterationsPerThread = 100;
449         constexpr size_t kDrawsPerIteration   = 10;
450 
451         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
452         glUseProgram(program);
453 
454         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
455 
456         auto quadVertices = GetQuadVertices();
457 
458         GLBuffer vertexBuffer;
459         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
460         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
461 
462         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
463         glEnableVertexAttribArray(positionLocation);
464         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
465 
466         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
467         {
468             // Base the clear color on the thread and iteration indexes so every clear color is
469             // unique
470             const GLColor color(static_cast<GLubyte>(thread % 255),
471                                 static_cast<GLubyte>(iteration % 255), 0, 255);
472             const angle::Vector4 floatColor = color.toNormalizedVector();
473             glUniform4fv(colorLocation, 1, floatColor.data());
474 
475             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
476             {
477                 glDrawArrays(GL_TRIANGLES, 0, 6);
478             }
479 
480             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
481             EXPECT_EGL_SUCCESS();
482 
483             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
484         }
485     };
486     runMultithreadedGLTest(testBody, 32);
487 }
488 
489 // Test that ANGLE handles multiple threads creating and destroying resources (vertex buffer in this
490 // case). Disable defer_flush_until_endrenderpass so that glFlush will issue work to GPU in order to
491 // maximize the chance we resources can be destroyed at the wrong time.
TEST_P(MultithreadingTest,MultiContextCreateAndDeleteResources)492 TEST_P(MultithreadingTest, MultiContextCreateAndDeleteResources)
493 {
494     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
495 
496     EGLWindow *window = getEGLWindow();
497     EGLDisplay dpy    = window->getDisplay();
498 
499     auto testBody = [dpy](EGLSurface surface, size_t thread) {
500         constexpr size_t kIterationsPerThread = 32;
501         constexpr size_t kDrawsPerIteration   = 1;
502 
503         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
504         glUseProgram(program);
505 
506         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
507 
508         auto quadVertices = GetQuadVertices();
509 
510         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
511         {
512             GLBuffer vertexBuffer;
513             glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
514             glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(),
515                          GL_STATIC_DRAW);
516 
517             GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
518             glEnableVertexAttribArray(positionLocation);
519             glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
520 
521             // Base the clear color on the thread and iteration indexes so every clear color is
522             // unique
523             const GLColor color(static_cast<GLubyte>(thread % 255),
524                                 static_cast<GLubyte>(iteration % 255), 0, 255);
525             const angle::Vector4 floatColor = color.toNormalizedVector();
526             glUniform4fv(colorLocation, 1, floatColor.data());
527 
528             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
529             {
530                 glDrawArrays(GL_TRIANGLES, 0, 6);
531             }
532 
533             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
534             EXPECT_EGL_SUCCESS();
535 
536             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
537         }
538         glFinish();
539     };
540     runMultithreadedGLTest(testBody, 32);
541 }
542 
TEST_P(MultithreadingTest,MultiCreateContext)543 TEST_P(MultithreadingTest, MultiCreateContext)
544 {
545     // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
546     // Not supported on Ozone (https://crbug.com/1103009)
547     ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsOSX()) || IsOzone());
548 
549     EGLWindow *window  = getEGLWindow();
550     EGLDisplay dpy     = window->getDisplay();
551     EGLContext ctx     = window->getContext();
552     EGLSurface surface = window->getSurface();
553 
554     // Un-makeCurrent the test window's context
555     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
556     EXPECT_EGL_SUCCESS();
557 
558     constexpr size_t kThreadCount = 16;
559     std::atomic<uint32_t> barrier(0);
560     std::vector<std::thread> threads(kThreadCount);
561     std::vector<EGLContext> contexts(kThreadCount);
562     for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
563     {
564         threads[threadIdx] = std::thread([&, threadIdx]() {
565             contexts[threadIdx] = EGL_NO_CONTEXT;
566             {
567                 contexts[threadIdx] = createMultithreadedContext(window, EGL_NO_CONTEXT);
568                 EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
569 
570                 barrier++;
571             }
572 
573             while (barrier < kThreadCount)
574             {
575             }
576 
577             {
578                 EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
579             }
580         });
581     }
582 
583     for (std::thread &thread : threads)
584     {
585         thread.join();
586     }
587 
588     // Re-make current the test window's context for teardown.
589     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
590     EXPECT_EGL_SUCCESS();
591 }
592 
textureThreadFunction(bool useDraw)593 void MultithreadingTestES3::textureThreadFunction(bool useDraw)
594 {
595     EGLWindow *window  = getEGLWindow();
596     EGLDisplay dpy     = window->getDisplay();
597     EGLConfig config   = window->getConfig();
598     EGLSurface surface = EGL_NO_SURFACE;
599     EGLContext ctx     = EGL_NO_CONTEXT;
600 
601     // Initialize the pbuffer and context
602     EGLint pbufferAttributes[] = {
603         EGL_WIDTH, kSize, EGL_HEIGHT, kSize, EGL_NONE, EGL_NONE,
604     };
605     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
606     EXPECT_EGL_SUCCESS();
607     EXPECT_NE(EGL_NO_SURFACE, surface);
608 
609     ctx = createMultithreadedContext(window, window->getContext());
610     EXPECT_NE(EGL_NO_CONTEXT, ctx);
611 
612     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
613     EXPECT_EGL_SUCCESS();
614 
615     std::vector<GLColor> greenColor(kSize * kSize, GLColor::green);
616     std::vector<GLColor> redColor(kSize * kSize, GLColor::red);
617     ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
618     ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
619 
620     glBindTexture(GL_TEXTURE_2D, mTexture2D);
621     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
622     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
623     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
624     ASSERT_GL_NO_ERROR();
625 
626     GLFramebuffer fbo;
627     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
628     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
629     ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
630 
631     mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
632     ASSERT_GL_NO_ERROR();
633     // Force the fence to be created
634     glFlush();
635 
636     // Draw something
637     while (!mExitThread)
638     {
639         std::lock_guard<decltype(mutex)> lock(mutex);
640 
641         if (mMainThreadSyncObj != nullptr)
642         {
643             glWaitSync(mMainThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
644             ASSERT_GL_NO_ERROR();
645             glDeleteSync(mSecondThreadSyncObj);
646             ASSERT_GL_NO_ERROR();
647             mMainThreadSyncObj = nullptr;
648         }
649         else
650         {
651             continue;
652         }
653 
654         glBindTexture(GL_TEXTURE_2D, mTexture2D);
655         ASSERT_GL_NO_ERROR();
656 
657         if (mDrawGreen)
658         {
659             if (useDraw)
660             {
661                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
662                 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
663             }
664             else
665             {
666                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
667                              greenColor.data());
668             }
669             ASSERT_GL_NO_ERROR();
670         }
671         else
672         {
673             if (useDraw)
674             {
675                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
676                 drawQuad(redProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
677             }
678             else
679             {
680                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
681                              redColor.data());
682             }
683             ASSERT_GL_NO_ERROR();
684         }
685 
686         ASSERT_EQ(mSecondThreadSyncObj.load(), nullptr);
687         mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
688         ASSERT_GL_NO_ERROR();
689         // Force the fence to be created
690         glFlush();
691 
692         mDrawGreen = !mDrawGreen;
693     }
694 
695     // Clean up
696     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
697     EXPECT_EGL_SUCCESS();
698 
699     eglDestroySurface(dpy, surface);
700     eglDestroyContext(dpy, ctx);
701 }
702 
703 // Test fence sync with multiple threads drawing
mainThreadDraw(bool useDraw)704 void MultithreadingTestES3::mainThreadDraw(bool useDraw)
705 {
706     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
707 
708     EGLWindow *window  = getEGLWindow();
709     EGLDisplay dpy     = window->getDisplay();
710     EGLContext ctx     = window->getContext();
711     EGLSurface surface = window->getSurface();
712     // Use odd numbers so we bounce between red and green in the final image
713     constexpr int kNumIterations = 5;
714     constexpr int kNumDraws      = 5;
715 
716     mDrawGreen = false;
717 
718     std::thread textureThread(&MultithreadingTestES3::textureThreadFunction, this, true);
719 
720     ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
721 
722     for (int iterations = 0; iterations < kNumIterations; ++iterations)
723     {
724         for (int draws = 0; draws < kNumDraws;)
725         {
726             std::lock_guard<decltype(mutex)> lock(mutex);
727 
728             if (mSecondThreadSyncObj != nullptr)
729             {
730                 glWaitSync(mSecondThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
731                 ASSERT_GL_NO_ERROR();
732                 glDeleteSync(mSecondThreadSyncObj);
733                 ASSERT_GL_NO_ERROR();
734                 mSecondThreadSyncObj = nullptr;
735             }
736             else
737             {
738                 continue;
739             }
740 
741             glBindFramebuffer(GL_FRAMEBUFFER, 0);
742             glBindTexture(GL_TEXTURE_2D, mTexture2D);
743             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
744             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
745             glUseProgram(texProgram);
746             drawQuad(texProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
747 
748             ASSERT_EQ(mMainThreadSyncObj.load(), nullptr);
749             mMainThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
750             ASSERT_GL_NO_ERROR();
751             // Force the fence to be created
752             glFlush();
753 
754             ++draws;
755         }
756 
757         ASSERT_GL_NO_ERROR();
758         swapBuffers();
759     }
760 
761     mExitThread = true;
762     textureThread.join();
763 
764     ASSERT_GL_NO_ERROR();
765     GLColor color;
766     if (mDrawGreen)
767     {
768         color = GLColor::green;
769     }
770     else
771     {
772         color = GLColor::red;
773     }
774     EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, color);
775 
776     // Re-make current the test window's context for teardown.
777     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
778     EXPECT_EGL_SUCCESS();
779 }
780 
781 // Test that glFenceSync/glWaitSync works correctly with multithreading.
782 // Main thread: Samples from the shared texture to draw to the default FBO.
783 // Secondary (Texture) thread: Draws to the shared texture, which the Main thread samples from.
784 // The overall execution flow is:
785 // Main Thread:
786 // 1. Wait for the mSecondThreadSyncObj fence object to be created.
787 //    - This fence object is used by synchronize access to the shared texture by indicating that the
788 //    Secondary thread's draws to the texture have all completed and it's now safe to sample from
789 //    it.
790 // 2. Once the fence is created, add a glWaitSync(mSecondThreadSyncObj) to the command stream and
791 //    then delete it.
792 // 3. Draw, sampling from the shared texture.
793 // 4. Create a new mMainThreadSyncObj.
794 //    - This fence object is used to synchronize access to the shared texture by indicating that the
795 //    Main thread's draws are no longer sampling from the texture, so it's now safe for the
796 //    Secondary thread to draw to it again with a new color.
797 // Secondary (Texture) Thread:
798 // 1. Wait for the mMainThreadSyncObj fence object to be created.
799 // 2. Once the fence is created, add a glWaitSync(mMainThreadSyncObj) to the command stream and then
800 //    delete it.
801 // 3. Draw/Fill the texture.
802 // 4. Create a new mSecondThreadSyncObj.
803 //
804 // These threads loop for the specified number of iterations, drawing/sampling the shared texture
805 // with the necessary glFlush()s and occasional eglSwapBuffers() to mimic a real multithreaded GLES
806 // application.
TEST_P(MultithreadingTestES3,MultithreadFenceDraw)807 TEST_P(MultithreadingTestES3, MultithreadFenceDraw)
808 {
809     // http://anglebug.com/5418
810     ANGLE_SKIP_TEST_IF(IsLinux() && IsVulkan() && (IsIntel() || isSwiftshader()));
811 
812     // Have the secondary thread use glDrawArrays()
813     mainThreadDraw(true);
814 }
815 
816 // Same as MultithreadFenceDraw, but with the secondary thread using glTexImage2D rather than
817 // glDrawArrays.
TEST_P(MultithreadingTestES3,MultithreadFenceTexImage)818 TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)
819 {
820     // http://anglebug.com/5418
821     ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
822 
823     // http://anglebug.com/5439
824     ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
825 
826     // Have the secondary thread use glTexImage2D()
827     mainThreadDraw(false);
828 }
829 
830 // Test that waiting on a sync object that hasn't been flushed and without a current context returns
831 // TIMEOUT_EXPIRED or CONDITION_SATISFIED, but doesn't generate an error or crash.
TEST_P(MultithreadingTest,NoFlushNoContextReturnsTimeout)832 TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)
833 {
834     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
835     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
836 
837     std::mutex mutex;
838 
839     EGLWindow *window = getEGLWindow();
840     EGLDisplay dpy    = window->getDisplay();
841 
842     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
843     glClear(GL_COLOR_BUFFER_BIT);
844 
845     EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
846     EXPECT_NE(sync, EGL_NO_SYNC_KHR);
847 
848     std::thread thread = std::thread([&]() {
849         std::lock_guard<decltype(mutex)> lock(mutex);
850         // Make sure there is no active context on this thread.
851         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
852         EXPECT_EGL_SUCCESS();
853         // Don't wait forever to make sure the test terminates
854         constexpr GLuint64 kTimeout = 1'000'000'000;  // 1 second
855         int result                  = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
856         // We typically expect to get back TIMEOUT_EXPIRED since the sync object was never flushed.
857         // However, the OpenGL ES backend returns CONDITION_SATISFIED, which is also a passing
858         // result.
859         ASSERT_TRUE(result == EGL_TIMEOUT_EXPIRED_KHR || result == EGL_CONDITION_SATISFIED_KHR);
860     });
861 
862     thread.join();
863 
864     EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
865 }
866 
867 // Test that waiting on sync object that hasn't been flushed yet, but is later flushed by another
868 // thread, correctly returns when the fence is signalled without a timeout.
TEST_P(MultithreadingTest,CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)869 TEST_P(MultithreadingTest, CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)
870 {
871     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
872     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
873     // TODO: Fails on Pixel 4 with OpenGLES backend.
874     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
875 
876     EGLWindow *window = getEGLWindow();
877     EGLDisplay dpy    = window->getDisplay();
878     EGLConfig config  = window->getConfig();
879     EGLSurface surface;
880     EGLContext context;
881     constexpr EGLint kPBufferSize = 256;
882     // Initialize the pbuffer and context
883     EGLint pbufferAttributes[] = {
884         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
885     };
886 
887     // Create 2 surfaces, one for each thread
888     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
889     EXPECT_EGL_SUCCESS();
890     // Create 2 shared contexts, one for each thread
891     context = window->createContext(EGL_NO_CONTEXT, nullptr);
892     EXPECT_NE(EGL_NO_CONTEXT, context);
893     // Sync object
894     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
895 
896     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
897     std::mutex mutex;
898     std::condition_variable condVar;
899 
900     enum class Step
901     {
902         Start,
903         Thread0Clear,
904         Thread1CreateFence,
905         Thread0ClientWaitSync,
906         Thread1Flush,
907         Finish,
908         Abort,
909     };
910     Step currentStep = Step::Start;
911 
912     std::thread thread0 = std::thread([&]() {
913         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
914 
915         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
916 
917         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
918 
919         // Do work.
920         glClearColor(1.0, 0.0, 0.0, 1.0);
921         glClear(GL_COLOR_BUFFER_BIT);
922 
923         // Wait for thread 1 to clear.
924         threadSynchronization.nextStep(Step::Thread0Clear);
925         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
926 
927         // Wait on the sync object, but do *not* flush it, since the other thread will flush.
928         constexpr GLuint64 kTimeout = 2'000'000'000;  // 1 second
929         threadSynchronization.nextStep(Step::Thread0ClientWaitSync);
930         ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, eglClientWaitSyncKHR(dpy, sync, 0, kTimeout));
931 
932         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
933     });
934 
935     std::thread thread1 = std::thread([&]() {
936         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
937 
938         // Wait for thread 0 to clear.
939         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
940 
941         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
942 
943         // Do work.
944         glClearColor(0.0, 1.0, 0.0, 1.0);
945         glClear(GL_COLOR_BUFFER_BIT);
946 
947         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
948         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
949 
950         // Wait for the thread 0 to eglClientWaitSyncKHR().
951         threadSynchronization.nextStep(Step::Thread1CreateFence);
952         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ClientWaitSync));
953 
954         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
955         angle::Sleep(500);
956         glFlush();
957 
958         // Clean up
959         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
960 
961         threadSynchronization.nextStep(Step::Finish);
962     });
963 
964     thread0.join();
965     thread1.join();
966 
967     // Clean up
968     if (surface != EGL_NO_SURFACE)
969     {
970         eglDestroySurface(dpy, surface);
971     }
972     if (context != EGL_NO_CONTEXT)
973     {
974         eglDestroyContext(dpy, context);
975     }
976 
977     ASSERT_NE(currentStep, Step::Abort);
978 }
979 
980 // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
981 
982 ANGLE_INSTANTIATE_TEST(MultithreadingTest,
983                        ES2_OPENGL(),
984                        ES3_OPENGL(),
985                        ES2_OPENGLES(),
986                        ES3_OPENGLES(),
987                        ES3_VULKAN(),
988                        ES3_VULKAN_SWIFTSHADER(),
989                        ES2_D3D11(),
990                        ES3_D3D11());
991 
992 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultithreadingTestES3);
993 ANGLE_INSTANTIATE_TEST(MultithreadingTestES3,
994                        ES3_OPENGL(),
995                        ES3_OPENGLES(),
996                        ES3_VULKAN(),
997                        ES3_VULKAN_SWIFTSHADER(),
998                        ES3_D3D11());
999 
1000 }  // namespace angle
1001