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 
148     void testSetUp() override { mTexture2D = create2DTexture(); }
149 
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 read back pixels correctly.
356 // Using eglSwapBuffers stresses race conditions around use of QueueSerials.
TEST_P(MultithreadingTest, MultiContextDrawWithSwapBuffers)357 TEST_P(MultithreadingTest, MultiContextDrawWithSwapBuffers)
358 {
359     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
360 
361     // http://anglebug.com/5099
362     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
363 
364     EGLWindow *window = getEGLWindow();
365     EGLDisplay dpy    = window->getDisplay();
366 
367     auto testBody = [dpy](EGLSurface surface, size_t thread) {
368         constexpr size_t kIterationsPerThread = 100;
369         constexpr size_t kDrawsPerIteration   = 10;
370 
371         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
372         glUseProgram(program);
373 
374         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
375 
376         auto quadVertices = GetQuadVertices();
377 
378         GLBuffer vertexBuffer;
379         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
380         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
381 
382         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
383         glEnableVertexAttribArray(positionLocation);
384         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
385 
386         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
387         {
388             // Base the clear color on the thread and iteration indexes so every clear color is
389             // unique
390             const GLColor color(static_cast<GLubyte>(thread % 255),
391                                 static_cast<GLubyte>(iteration % 255), 0, 255);
392             const angle::Vector4 floatColor = color.toNormalizedVector();
393             glUniform4fv(colorLocation, 1, floatColor.data());
394 
395             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
396             {
397                 glDrawArrays(GL_TRIANGLES, 0, 6);
398             }
399 
400             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
401             EXPECT_EGL_SUCCESS();
402 
403             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
404         }
405     };
406     runMultithreadedGLTest(testBody, 32);
407 }
408 
409 // Test that ANGLE handles multiple threads creating and destroying resources (vertex buffer in this
410 // case). Disable defer_flush_until_endrenderpass so that glFlush will issue work to GPU in order to
411 // maximize the chance we resources can be destroyed at the wrong time.
TEST_P(MultithreadingTest, MultiContextCreateAndDeleteResources)412 TEST_P(MultithreadingTest, MultiContextCreateAndDeleteResources)
413 {
414     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
415 
416     EGLWindow *window = getEGLWindow();
417     EGLDisplay dpy    = window->getDisplay();
418 
419     auto testBody = [dpy](EGLSurface surface, size_t thread) {
420         constexpr size_t kIterationsPerThread = 32;
421         constexpr size_t kDrawsPerIteration   = 1;
422 
423         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
424         glUseProgram(program);
425 
426         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
427 
428         auto quadVertices = GetQuadVertices();
429 
430         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
431         {
432             GLBuffer vertexBuffer;
433             glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
434             glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(),
435                          GL_STATIC_DRAW);
436 
437             GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
438             glEnableVertexAttribArray(positionLocation);
439             glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
440 
441             // Base the clear color on the thread and iteration indexes so every clear color is
442             // unique
443             const GLColor color(static_cast<GLubyte>(thread % 255),
444                                 static_cast<GLubyte>(iteration % 255), 0, 255);
445             const angle::Vector4 floatColor = color.toNormalizedVector();
446             glUniform4fv(colorLocation, 1, floatColor.data());
447 
448             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
449             {
450                 glDrawArrays(GL_TRIANGLES, 0, 6);
451             }
452 
453             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
454             EXPECT_EGL_SUCCESS();
455 
456             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
457         }
458         glFinish();
459     };
460     runMultithreadedGLTest(testBody, 32);
461 }
462 
TEST_P(MultithreadingTest, MultiCreateContext)463 TEST_P(MultithreadingTest, MultiCreateContext)
464 {
465     // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
466     // Not supported on Ozone (https://crbug.com/1103009)
467     ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsOSX()) || IsOzone());
468 
469     EGLWindow *window  = getEGLWindow();
470     EGLDisplay dpy     = window->getDisplay();
471     EGLContext ctx     = window->getContext();
472     EGLSurface surface = window->getSurface();
473 
474     // Un-makeCurrent the test window's context
475     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
476     EXPECT_EGL_SUCCESS();
477 
478     constexpr size_t kThreadCount = 16;
479     std::atomic<uint32_t> barrier(0);
480     std::vector<std::thread> threads(kThreadCount);
481     std::vector<EGLContext> contexts(kThreadCount);
482     for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
483     {
484         threads[threadIdx] = std::thread([&, threadIdx]() {
485             contexts[threadIdx] = EGL_NO_CONTEXT;
486             {
487                 contexts[threadIdx] = createMultithreadedContext(window, EGL_NO_CONTEXT);
488                 EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
489 
490                 barrier++;
491             }
492 
493             while (barrier < kThreadCount)
494             {
495             }
496 
497             {
498                 EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
499             }
500         });
501     }
502 
503     for (std::thread &thread : threads)
504     {
505         thread.join();
506     }
507 
508     // Re-make current the test window's context for teardown.
509     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
510     EXPECT_EGL_SUCCESS();
511 }
512 
textureThreadFunction(bool useDraw)513 void MultithreadingTestES3::textureThreadFunction(bool useDraw)
514 {
515     EGLWindow *window  = getEGLWindow();
516     EGLDisplay dpy     = window->getDisplay();
517     EGLConfig config   = window->getConfig();
518     EGLSurface surface = EGL_NO_SURFACE;
519     EGLContext ctx     = EGL_NO_CONTEXT;
520 
521     // Initialize the pbuffer and context
522     EGLint pbufferAttributes[] = {
523         EGL_WIDTH, kSize, EGL_HEIGHT, kSize, EGL_NONE, EGL_NONE,
524     };
525     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
526     EXPECT_EGL_SUCCESS();
527     EXPECT_NE(EGL_NO_SURFACE, surface);
528 
529     ctx = createMultithreadedContext(window, window->getContext());
530     EXPECT_NE(EGL_NO_CONTEXT, ctx);
531 
532     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
533     EXPECT_EGL_SUCCESS();
534 
535     std::vector<GLColor> greenColor(kSize * kSize, GLColor::green);
536     std::vector<GLColor> redColor(kSize * kSize, GLColor::red);
537     ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
538     ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
539 
540     glBindTexture(GL_TEXTURE_2D, mTexture2D);
541     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
542     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
543     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
544     ASSERT_GL_NO_ERROR();
545 
546     GLFramebuffer fbo;
547     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
548     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
549     ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
550 
551     mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
552     ASSERT_GL_NO_ERROR();
553     // Force the fence to be created
554     glFlush();
555 
556     // Draw something
557     while (!mExitThread)
558     {
559         std::lock_guard<decltype(mutex)> lock(mutex);
560 
561         if (mMainThreadSyncObj != nullptr)
562         {
563             glWaitSync(mMainThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
564             ASSERT_GL_NO_ERROR();
565             glDeleteSync(mSecondThreadSyncObj);
566             ASSERT_GL_NO_ERROR();
567             mMainThreadSyncObj = nullptr;
568         }
569         else
570         {
571             continue;
572         }
573 
574         glBindTexture(GL_TEXTURE_2D, mTexture2D);
575         ASSERT_GL_NO_ERROR();
576 
577         if (mDrawGreen)
578         {
579             if (useDraw)
580             {
581                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
582                 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
583             }
584             else
585             {
586                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
587                              greenColor.data());
588             }
589             ASSERT_GL_NO_ERROR();
590         }
591         else
592         {
593             if (useDraw)
594             {
595                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
596                 drawQuad(redProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
597             }
598             else
599             {
600                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
601                              redColor.data());
602             }
603             ASSERT_GL_NO_ERROR();
604         }
605 
606         ASSERT_EQ(mSecondThreadSyncObj.load(), nullptr);
607         mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
608         ASSERT_GL_NO_ERROR();
609         // Force the fence to be created
610         glFlush();
611 
612         mDrawGreen = !mDrawGreen;
613     }
614 
615     // Clean up
616     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
617     EXPECT_EGL_SUCCESS();
618 
619     eglDestroySurface(dpy, surface);
620     eglDestroyContext(dpy, ctx);
621 }
622 
623 // Test fence sync with multiple threads drawing
mainThreadDraw(bool useDraw)624 void MultithreadingTestES3::mainThreadDraw(bool useDraw)
625 {
626     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
627 
628     EGLWindow *window  = getEGLWindow();
629     EGLDisplay dpy     = window->getDisplay();
630     EGLContext ctx     = window->getContext();
631     EGLSurface surface = window->getSurface();
632     // Use odd numbers so we bounce between red and green in the final image
633     constexpr int kNumIterations = 5;
634     constexpr int kNumDraws      = 5;
635 
636     mDrawGreen = false;
637 
638     std::thread textureThread(&MultithreadingTestES3::textureThreadFunction, this, true);
639 
640     ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
641 
642     for (int iterations = 0; iterations < kNumIterations; ++iterations)
643     {
644         for (int draws = 0; draws < kNumDraws;)
645         {
646             std::lock_guard<decltype(mutex)> lock(mutex);
647 
648             if (mSecondThreadSyncObj != nullptr)
649             {
650                 glWaitSync(mSecondThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
651                 ASSERT_GL_NO_ERROR();
652                 glDeleteSync(mSecondThreadSyncObj);
653                 ASSERT_GL_NO_ERROR();
654                 mSecondThreadSyncObj = nullptr;
655             }
656             else
657             {
658                 continue;
659             }
660 
661             glBindFramebuffer(GL_FRAMEBUFFER, 0);
662             glBindTexture(GL_TEXTURE_2D, mTexture2D);
663             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
664             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
665             glUseProgram(texProgram);
666             drawQuad(texProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
667 
668             ASSERT_EQ(mMainThreadSyncObj.load(), nullptr);
669             mMainThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
670             ASSERT_GL_NO_ERROR();
671             // Force the fence to be created
672             glFlush();
673 
674             ++draws;
675         }
676 
677         ASSERT_GL_NO_ERROR();
678         swapBuffers();
679     }
680 
681     mExitThread = true;
682     textureThread.join();
683 
684     ASSERT_GL_NO_ERROR();
685     GLColor color;
686     if (mDrawGreen)
687     {
688         color = GLColor::green;
689     }
690     else
691     {
692         color = GLColor::red;
693     }
694     EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, color);
695 
696     // Re-make current the test window's context for teardown.
697     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
698     EXPECT_EGL_SUCCESS();
699 }
700 
701 // Test that glFenceSync/glWaitSync works correctly with multithreading.
702 // Main thread: Samples from the shared texture to draw to the default FBO.
703 // Secondary (Texture) thread: Draws to the shared texture, which the Main thread samples from.
704 // The overall execution flow is:
705 // Main Thread:
706 // 1. Wait for the mSecondThreadSyncObj fence object to be created.
707 //    - This fence object is used by synchronize access to the shared texture by indicating that the
708 //    Secondary thread's draws to the texture have all completed and it's now safe to sample from
709 //    it.
710 // 2. Once the fence is created, add a glWaitSync(mSecondThreadSyncObj) to the command stream and
711 //    then delete it.
712 // 3. Draw, sampling from the shared texture.
713 // 4. Create a new mMainThreadSyncObj.
714 //    - This fence object is used to synchronize access to the shared texture by indicating that the
715 //    Main thread's draws are no longer sampling from the texture, so it's now safe for the
716 //    Secondary thread to draw to it again with a new color.
717 // Secondary (Texture) Thread:
718 // 1. Wait for the mMainThreadSyncObj fence object to be created.
719 // 2. Once the fence is created, add a glWaitSync(mMainThreadSyncObj) to the command stream and then
720 //    delete it.
721 // 3. Draw/Fill the texture.
722 // 4. Create a new mSecondThreadSyncObj.
723 //
724 // These threads loop for the specified number of iterations, drawing/sampling the shared texture
725 // with the necessary glFlush()s and occasional eglSwapBuffers() to mimic a real multithreaded GLES
726 // application.
TEST_P(MultithreadingTestES3, MultithreadFenceDraw)727 TEST_P(MultithreadingTestES3, MultithreadFenceDraw)
728 {
729     // http://anglebug.com/5418
730     ANGLE_SKIP_TEST_IF(IsLinux() && IsVulkan() && (IsIntel() || IsSwiftshaderDevice()));
731 
732     // Have the secondary thread use glDrawArrays()
733     mainThreadDraw(true);
734 }
735 
736 // Same as MultithreadFenceDraw, but with the secondary thread using glTexImage2D rather than
737 // glDrawArrays.
TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)738 TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)
739 {
740     // http://anglebug.com/5418
741     ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
742 
743     // http://anglebug.com/5439
744     ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
745 
746     // Have the secondary thread use glTexImage2D()
747     mainThreadDraw(false);
748 }
749 
750 // Test that waiting on a sync object that hasn't been flushed and without a current context returns
751 // TIMEOUT_EXPIRED or CONDITION_SATISFIED, but doesn't generate an error or crash.
TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)752 TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)
753 {
754     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
755     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
756 
757     std::mutex mutex;
758 
759     EGLWindow *window = getEGLWindow();
760     EGLDisplay dpy    = window->getDisplay();
761 
762     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
763     glClear(GL_COLOR_BUFFER_BIT);
764 
765     EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
766     EXPECT_NE(sync, EGL_NO_SYNC_KHR);
767 
768     std::thread thread = std::thread([&]() {
769         std::lock_guard<decltype(mutex)> lock(mutex);
770         // Make sure there is no active context on this thread.
771         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
772         EXPECT_EGL_SUCCESS();
773         // Don't wait forever to make sure the test terminates
774         constexpr GLuint64 kTimeout = 1'000'000'000;  // 1 second
775         int result                  = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
776         // We typically expect to get back TIMEOUT_EXPIRED since the sync object was never flushed.
777         // However, the OpenGL ES backend returns CONDITION_SATISFIED, which is also a passing
778         // result.
779         ASSERT_TRUE(result == EGL_TIMEOUT_EXPIRED_KHR || result == EGL_CONDITION_SATISFIED_KHR);
780     });
781 
782     thread.join();
783 
784     EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
785 }
786 
787 // Test that waiting on sync object that hasn't been flushed yet, but is later flushed by another
788 // thread, correctly returns when the fence is signalled without a timeout.
789 TEST_P(MultithreadingTest, CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)
790 {
791     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
792     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
793     // TODO: Fails on Pixel 4 with OpenGLES backend.
794     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
795 
796     EGLWindow *window = getEGLWindow();
797     EGLDisplay dpy    = window->getDisplay();
798     EGLConfig config  = window->getConfig();
799     EGLSurface surface;
800     EGLContext context;
801     constexpr EGLint kPBufferSize = 256;
802     // Initialize the pbuffer and context
803     EGLint pbufferAttributes[] = {
804         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
805     };
806 
807     // Create 2 surfaces, one for each thread
808     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
809     EXPECT_EGL_SUCCESS();
810     // Create 2 shared contexts, one for each thread
811     context = window->createContext(EGL_NO_CONTEXT, nullptr);
812     EXPECT_NE(EGL_NO_CONTEXT, context);
813     // Sync object
814     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
815 
816     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
817     std::mutex mutex;
818     std::condition_variable condVar;
819 
820     enum class Step
821     {
822         Start,
823         Thread0Clear,
824         Thread1CreateFence,
825         Thread0ClientWaitSync,
826         Thread1Flush,
827         Finish,
828         Abort,
829     };
830     Step currentStep = Step::Start;
831 
832     std::thread thread0 = std::thread([&]() {
833         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
834 
835         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
836 
837         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
838 
839         // Do work.
840         glClearColor(1.0, 0.0, 0.0, 1.0);
841         glClear(GL_COLOR_BUFFER_BIT);
842 
843         // Wait for thread 1 to clear.
844         threadSynchronization.nextStep(Step::Thread0Clear);
845         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
846 
847         // Wait on the sync object, but do *not* flush it, since the other thread will flush.
848         constexpr GLuint64 kTimeout = 2'000'000'000;  // 1 second
849         threadSynchronization.nextStep(Step::Thread0ClientWaitSync);
850         ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, eglClientWaitSyncKHR(dpy, sync, 0, kTimeout));
851 
852         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
853     });
854 
855     std::thread thread1 = std::thread([&]() {
856         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
857 
858         // Wait for thread 0 to clear.
859         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
860 
861         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
862 
863         // Do work.
864         glClearColor(0.0, 1.0, 0.0, 1.0);
865         glClear(GL_COLOR_BUFFER_BIT);
866 
867         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
868         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
869 
870         // Wait for the thread 0 to eglClientWaitSyncKHR().
871         threadSynchronization.nextStep(Step::Thread1CreateFence);
872         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ClientWaitSync));
873 
874         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
875         angle::Sleep(500);
876         glFlush();
877 
878         // Clean up
879         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
880 
881         threadSynchronization.nextStep(Step::Finish);
882     });
883 
884     thread0.join();
885     thread1.join();
886 
887     // Clean up
888     if (surface != EGL_NO_SURFACE)
889     {
890         eglDestroySurface(dpy, surface);
891     }
892     if (context != EGL_NO_CONTEXT)
893     {
894         eglDestroyContext(dpy, context);
895     }
896 
897     ASSERT_NE(currentStep, Step::Abort);
898 }
899 
900 // TODO(geofflang): Test sharing a program between multiple shared contexts on multiple threads
901 
902 ANGLE_INSTANTIATE_TEST(MultithreadingTest,
903                        ES2_OPENGL(),
904                        ES3_OPENGL(),
905                        ES2_OPENGLES(),
906                        ES3_OPENGLES(),
907                        ES3_VULKAN(),
908                        ES3_VULKAN_SWIFTSHADER(),
909                        ES2_D3D11(),
910                        ES3_D3D11());
911 
912 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultithreadingTestES3);
913 ANGLE_INSTANTIATE_TEST(MultithreadingTestES3,
914                        ES3_OPENGL(),
915                        ES3_OPENGLES(),
916                        ES3_VULKAN(),
917                        ES3_VULKAN_SWIFTSHADER(),
918                        ES3_D3D11());
919 
920 }  // namespace angle
921