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