• 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 "test_utils/ANGLETest.h"
9 #include "test_utils/MultiThreadSteps.h"
10 #include "test_utils/gl_raii.h"
11 #include "util/EGLWindow.h"
12 #include "util/test_utils.h"
13 
14 #include <atomic>
15 #include <mutex>
16 #include <thread>
17 
18 namespace angle
19 {
20 
21 class MultithreadingTest : public ANGLETest<>
22 {
23   public:
24     static constexpr uint32_t kSize = 512;
25 
26   protected:
MultithreadingTest()27     MultithreadingTest()
28     {
29         setWindowWidth(kSize);
30         setWindowHeight(kSize);
31         setConfigRedBits(8);
32         setConfigGreenBits(8);
33         setConfigBlueBits(8);
34         setConfigAlphaBits(8);
35     }
36 
hasFenceSyncExtension() const37     bool hasFenceSyncExtension() const
38     {
39         return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_fence_sync");
40     }
hasWaitSyncExtension() const41     bool hasWaitSyncExtension() const
42     {
43         return hasFenceSyncExtension() &&
44                IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_wait_sync");
45     }
hasGLSyncExtension() const46     bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); }
47 
createMultithreadedContext(EGLWindow * window,EGLContext shareCtx)48     EGLContext createMultithreadedContext(EGLWindow *window, EGLContext shareCtx)
49     {
50         EGLint attribs[] = {EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, mVirtualizationGroup++,
51                             EGL_NONE};
52         if (!IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
53                                           "EGL_ANGLE_context_virtualization"))
54         {
55             attribs[0] = EGL_NONE;
56         }
57 
58         return window->createContext(shareCtx, attribs);
59     }
60 
runMultithreadedGLTest(std::function<void (EGLSurface surface,size_t threadIndex)> testBody,size_t threadCount)61     void runMultithreadedGLTest(
62         std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
63         size_t threadCount)
64     {
65         std::mutex mutex;
66 
67         EGLWindow *window = getEGLWindow();
68         EGLDisplay dpy    = window->getDisplay();
69         EGLConfig config  = window->getConfig();
70 
71         constexpr EGLint kPBufferSize = 256;
72 
73         std::vector<std::thread> threads(threadCount);
74         for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
75         {
76             threads[threadIdx] = std::thread([&, threadIdx]() {
77                 EGLSurface surface = EGL_NO_SURFACE;
78                 EGLContext ctx     = EGL_NO_CONTEXT;
79 
80                 {
81                     std::lock_guard<decltype(mutex)> lock(mutex);
82 
83                     // Initialize the pbuffer and context
84                     EGLint pbufferAttributes[] = {
85                         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
86                     };
87                     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
88                     EXPECT_EGL_SUCCESS();
89 
90                     ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
91                     EXPECT_NE(EGL_NO_CONTEXT, ctx);
92 
93                     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
94                     EXPECT_EGL_SUCCESS();
95                 }
96 
97                 testBody(surface, threadIdx);
98 
99                 {
100                     std::lock_guard<decltype(mutex)> lock(mutex);
101 
102                     // Clean up
103                     EXPECT_EGL_TRUE(
104                         eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
105                     EXPECT_EGL_SUCCESS();
106 
107                     eglDestroySurface(dpy, surface);
108                     eglDestroyContext(dpy, ctx);
109                 }
110             });
111         }
112 
113         for (std::thread &thread : threads)
114         {
115             thread.join();
116         }
117     }
118 
119     std::atomic<EGLint> mVirtualizationGroup;
120 };
121 
122 class MultithreadingTestES3 : public MultithreadingTest
123 {
124   public:
125     void textureThreadFunction(bool useDraw);
126     void mainThreadDraw(bool useDraw);
127 
128   protected:
MultithreadingTestES3()129     MultithreadingTestES3()
130         : mTexture2D(0), mExitThread(false), mMainThreadSyncObj(NULL), mSecondThreadSyncObj(NULL)
131     {
132         setWindowWidth(kSize);
133         setWindowHeight(kSize);
134         setConfigRedBits(8);
135         setConfigGreenBits(8);
136         setConfigBlueBits(8);
137         setConfigAlphaBits(8);
138     }
139 
create2DTexture()140     GLuint create2DTexture()
141     {
142         GLuint texture2D;
143         glGenTextures(1, &texture2D);
144         glActiveTexture(GL_TEXTURE0);
145         glBindTexture(GL_TEXTURE_2D, texture2D);
146         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
147                      nullptr);
148         EXPECT_GL_NO_ERROR();
149         return texture2D;
150     }
151 
testSetUp()152     void testSetUp() override { mTexture2D = create2DTexture(); }
153 
testTearDown()154     void testTearDown() override
155     {
156         if (mTexture2D)
157         {
158             glDeleteTextures(1, &mTexture2D);
159         }
160     }
161 
162     enum class FenceTest
163     {
164         ClientWait,
165         ServerWait,
166         GetStatus,
167     };
168     enum class FlushMethod
169     {
170         Flush,
171         Finish,
172     };
173     void testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod);
174 
175     enum class DrawOrder
176     {
177         Before,
178         After,
179     };
180     void testFramebufferFetch(DrawOrder drawOrder);
181 
182     std::mutex mMutex;
183     GLuint mTexture2D;
184     std::atomic<bool> mExitThread;
185     std::atomic<bool> mDrawGreen;  // Toggle drawing green or red
186     std::atomic<GLsync> mMainThreadSyncObj;
187     std::atomic<GLsync> mSecondThreadSyncObj;
188 };
189 
190 // Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest,MakeCurrentSingleContext)191 TEST_P(MultithreadingTest, MakeCurrentSingleContext)
192 {
193     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
194 
195     std::mutex mutex;
196 
197     EGLWindow *window  = getEGLWindow();
198     EGLDisplay dpy     = window->getDisplay();
199     EGLContext ctx     = window->getContext();
200     EGLSurface surface = window->getSurface();
201 
202     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
203     EXPECT_EGL_SUCCESS();
204 
205     constexpr size_t kThreadCount = 16;
206     std::array<std::thread, kThreadCount> threads;
207     for (std::thread &thread : threads)
208     {
209         thread = std::thread([&]() {
210             std::lock_guard<decltype(mutex)> lock(mutex);
211 
212             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
213             EXPECT_EGL_SUCCESS();
214 
215             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
216             EXPECT_EGL_SUCCESS();
217 
218             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
219             EXPECT_EGL_SUCCESS();
220         });
221     }
222 
223     for (std::thread &thread : threads)
224     {
225         thread.join();
226     }
227 
228     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
229     EXPECT_EGL_SUCCESS();
230 }
231 
232 // Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextClear)233 TEST_P(MultithreadingTest, MultiContextClear)
234 {
235     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
236 
237     auto testBody = [](EGLSurface surface, size_t thread) {
238         constexpr size_t kIterationsPerThread = 32;
239         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
240         {
241             // Base the clear color on the thread and iteration indexes so every clear color is
242             // unique
243             const GLColor color(static_cast<GLubyte>(thread % 255),
244                                 static_cast<GLubyte>(iteration % 255), 0, 255);
245             const angle::Vector4 floatColor = color.toNormalizedVector();
246 
247             glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
248             EXPECT_GL_NO_ERROR();
249 
250             glClear(GL_COLOR_BUFFER_BIT);
251             EXPECT_GL_NO_ERROR();
252 
253             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
254         }
255     };
256     runMultithreadedGLTest(
257         testBody,
258         getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 72);
259 }
260 
261 // Verify that threads can interleave eglDestroyContext and draw calls without
262 // any crashes.
TEST_P(MultithreadingTest,MultiContextDeleteDraw)263 TEST_P(MultithreadingTest, MultiContextDeleteDraw)
264 {
265     // Skip this test on non-D3D11 backends, as it has the potential to time-out
266     // and this test was originally intended to catch a crash on the D3D11 backend.
267     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
268     ANGLE_SKIP_TEST_IF(!IsD3D11());
269 
270     EGLWindow *window = getEGLWindow();
271     EGLDisplay dpy    = window->getDisplay();
272     EGLConfig config  = window->getConfig();
273 
274     std::thread t1 = std::thread([&]() {
275         // 5000 is chosen here as it reliably reproduces the former crash.
276         for (int i = 0; i < 5000; i++)
277         {
278             EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
279             EGLContext ctx2 = createMultithreadedContext(window, EGL_NO_CONTEXT);
280 
281             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
282             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
283 
284             EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
285             EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
286         }
287     });
288 
289     std::thread t2 = std::thread([&]() {
290         EGLint pbufferAttributes[] = {
291             EGL_WIDTH, 256, EGL_HEIGHT, 256, EGL_NONE, EGL_NONE,
292         };
293 
294         EGLSurface surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
295         EXPECT_EGL_SUCCESS();
296 
297         auto ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
298         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
299 
300         constexpr size_t kIterationsPerThread = 512;
301         constexpr size_t kDrawsPerIteration   = 512;
302 
303         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
304         glUseProgram(program);
305 
306         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
307 
308         auto quadVertices = GetQuadVertices();
309 
310         GLBuffer vertexBuffer;
311         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
312         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
313 
314         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
315         glEnableVertexAttribArray(positionLocation);
316         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
317         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
318         {
319             const GLColor color(static_cast<GLubyte>(15151 % 255),
320                                 static_cast<GLubyte>(iteration % 255), 0, 255);
321             const angle::Vector4 floatColor = color.toNormalizedVector();
322             glUniform4fv(colorLocation, 1, floatColor.data());
323             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
324             {
325                 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
326                 glDrawArrays(GL_TRIANGLES, 0, 6);
327             }
328         }
329     });
330 
331     t1.join();
332     t2.join();
333 }
334 
335 // Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextDraw)336 TEST_P(MultithreadingTest, MultiContextDraw)
337 {
338     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
339 
340     ANGLE_SKIP_TEST_IF(isSwiftshader());
341 
342     auto testBody = [](EGLSurface surface, size_t thread) {
343         constexpr size_t kIterationsPerThread = 32;
344         constexpr size_t kDrawsPerIteration   = 500;
345 
346         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
347         glUseProgram(program);
348 
349         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
350 
351         auto quadVertices = GetQuadVertices();
352 
353         GLBuffer vertexBuffer;
354         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
355         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
356 
357         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
358         glEnableVertexAttribArray(positionLocation);
359         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
360 
361         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
362         {
363             // Base the clear color on the thread and iteration indexes so every clear color is
364             // unique
365             const GLColor color(static_cast<GLubyte>(thread % 255),
366                                 static_cast<GLubyte>(iteration % 255), 0, 255);
367             const angle::Vector4 floatColor = color.toNormalizedVector();
368             glUniform4fv(colorLocation, 1, floatColor.data());
369 
370             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
371             {
372                 glDrawArrays(GL_TRIANGLES, 0, 6);
373             }
374 
375             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
376         }
377     };
378     runMultithreadedGLTest(testBody, 4);
379 }
380 
381 // Test that multiple threads can draw and read back pixels correctly.
382 // Using eglSwapBuffers stresses race conditions around use of QueueSerials.
TEST_P(MultithreadingTest,MultiContextDrawWithSwapBuffers)383 TEST_P(MultithreadingTest, MultiContextDrawWithSwapBuffers)
384 {
385     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
386 
387     // http://anglebug.com/5099
388     ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
389 
390     EGLWindow *window = getEGLWindow();
391     EGLDisplay dpy    = window->getDisplay();
392 
393     auto testBody = [dpy](EGLSurface surface, size_t thread) {
394         constexpr size_t kIterationsPerThread = 100;
395         constexpr size_t kDrawsPerIteration   = 10;
396 
397         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
398         glUseProgram(program);
399 
400         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
401 
402         auto quadVertices = GetQuadVertices();
403 
404         GLBuffer vertexBuffer;
405         glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
406         glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
407 
408         GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
409         glEnableVertexAttribArray(positionLocation);
410         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
411 
412         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
413         {
414             // Base the clear color on the thread and iteration indexes so every clear color is
415             // unique
416             const GLColor color(static_cast<GLubyte>(thread % 255),
417                                 static_cast<GLubyte>(iteration % 255), 0, 255);
418             const angle::Vector4 floatColor = color.toNormalizedVector();
419             glUniform4fv(colorLocation, 1, floatColor.data());
420 
421             for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
422             {
423                 glDrawArrays(GL_TRIANGLES, 0, 6);
424             }
425 
426             EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
427             EXPECT_EGL_SUCCESS();
428 
429             EXPECT_PIXEL_COLOR_EQ(0, 0, color);
430         }
431     };
432     runMultithreadedGLTest(
433         testBody,
434         getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 32);
435 }
436 
437 // Test that ANGLE handles multiple threads creating and destroying resources (vertex buffer in this
438 // case).
TEST_P(MultithreadingTest,MultiContextCreateAndDeleteResources)439 TEST_P(MultithreadingTest, MultiContextCreateAndDeleteResources)
440 {
441     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
442 
443     EGLWindow *window = getEGLWindow();
444     EGLDisplay dpy    = window->getDisplay();
445 
446     auto testBody = [dpy](EGLSurface surface, size_t thread) {
447         constexpr size_t kIterationsPerThread = 32;
448         constexpr size_t kDrawsPerIteration   = 1;
449 
450         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
451         glUseProgram(program);
452 
453         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
454 
455         auto quadVertices = GetQuadVertices();
456 
457         for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
458         {
459             GLBuffer vertexBuffer;
460             glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
461             glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(),
462                          GL_STATIC_DRAW);
463 
464             GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
465             glEnableVertexAttribArray(positionLocation);
466             glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
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         glFinish();
486     };
487     runMultithreadedGLTest(
488         testBody,
489         getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 32);
490 }
491 
TEST_P(MultithreadingTest,MultiCreateContext)492 TEST_P(MultithreadingTest, MultiCreateContext)
493 {
494     // Supported by CGL, GLX, and WGL (https://anglebug.com/4725)
495     // Not supported on Ozone (https://crbug.com/1103009)
496     ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsMac()) || IsOzone());
497 
498     EGLWindow *window  = getEGLWindow();
499     EGLDisplay dpy     = window->getDisplay();
500     EGLContext ctx     = window->getContext();
501     EGLSurface surface = window->getSurface();
502 
503     // Un-makeCurrent the test window's context
504     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
505     EXPECT_EGL_SUCCESS();
506 
507     constexpr size_t kThreadCount = 16;
508     std::atomic<uint32_t> barrier(0);
509     std::vector<std::thread> threads(kThreadCount);
510     std::vector<EGLContext> contexts(kThreadCount);
511     for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
512     {
513         threads[threadIdx] = std::thread([&, threadIdx]() {
514             contexts[threadIdx] = EGL_NO_CONTEXT;
515             {
516                 contexts[threadIdx] = createMultithreadedContext(window, EGL_NO_CONTEXT);
517                 EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
518 
519                 barrier++;
520             }
521 
522             while (barrier < kThreadCount)
523             {
524             }
525 
526             {
527                 EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
528             }
529         });
530     }
531 
532     for (std::thread &thread : threads)
533     {
534         thread.join();
535     }
536 
537     // Re-make current the test window's context for teardown.
538     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
539     EXPECT_EGL_SUCCESS();
540 }
541 
542 // Create multiple shared context and draw with shared vertex buffer simutanously
TEST_P(MultithreadingTest,CreateMultiSharedContextAndDraw)543 TEST_P(MultithreadingTest, CreateMultiSharedContextAndDraw)
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() || IsMac()) || IsOzone());
548     EGLWindow *window             = getEGLWindow();
549     EGLDisplay dpy                = window->getDisplay();
550     EGLConfig config              = window->getConfig();
551     constexpr EGLint kPBufferSize = 256;
552 
553     // Initialize the pbuffer and context
554     EGLint pbufferAttributes[] = {
555         EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
556     };
557     EGLSurface sharedSurface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
558     EXPECT_EGL_SUCCESS();
559     EGLContext sharedCtx = createMultithreadedContext(window, EGL_NO_CONTEXT);
560     EXPECT_NE(EGL_NO_CONTEXT, sharedCtx);
561     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, sharedSurface, sharedSurface, sharedCtx));
562     EXPECT_EGL_SUCCESS();
563 
564     // Create a shared vertextBuffer
565     auto quadVertices = GetQuadVertices();
566     GLBuffer sharedVertexBuffer;
567     glBindBuffer(GL_ARRAY_BUFFER, sharedVertexBuffer);
568     glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
569     ASSERT_GL_NO_ERROR();
570 
571     // Now draw with the buffer and verify
572     {
573         ANGLE_GL_PROGRAM(sharedProgram, essl1_shaders::vs::Simple(),
574                          essl1_shaders::fs::UniformColor());
575         glUseProgram(sharedProgram);
576         GLint colorLocation = glGetUniformLocation(sharedProgram, essl1_shaders::ColorUniform());
577         GLint positionLocation =
578             glGetAttribLocation(sharedProgram, essl1_shaders::PositionAttrib());
579         glEnableVertexAttribArray(positionLocation);
580         glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
581         const GLColor color(0, 0, 0, 255);
582         const angle::Vector4 floatColor = color.toNormalizedVector();
583         glUniform4fv(colorLocation, 1, floatColor.data());
584         glDrawArrays(GL_TRIANGLES, 0, 6);
585         EXPECT_PIXEL_COLOR_EQ(0, 0, color);
586     }
587 
588     // Create shared context in their own threads and draw with the shared vertex buffer at the same
589     // time.
590     size_t threadCount                    = 16;
591     constexpr size_t kIterationsPerThread = 3;
592     constexpr size_t kDrawsPerIteration   = 50;
593     std::vector<std::thread> threads(threadCount);
594     std::atomic<uint32_t> numOfContextsCreated(0);
595     std::mutex mutex;
596     for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
597     {
598         threads[threadIdx] = std::thread([&, threadIdx]() {
599             EGLSurface surface = EGL_NO_SURFACE;
600             EGLContext ctx     = EGL_NO_CONTEXT;
601 
602             {
603                 std::lock_guard<decltype(mutex)> lock(mutex);
604                 // Initialize the pbuffer and context
605                 surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
606                 EXPECT_EGL_SUCCESS();
607                 ctx = createMultithreadedContext(window, /*EGL_NO_CONTEXT*/ sharedCtx);
608                 EXPECT_NE(EGL_NO_CONTEXT, ctx);
609                 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
610                 EXPECT_EGL_SUCCESS();
611                 numOfContextsCreated++;
612             }
613 
614             // Wait for all contexts created.
615             while (numOfContextsCreated < threadCount)
616             {
617             }
618 
619             // Now draw with shared vertex buffer
620             {
621                 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(),
622                                  essl1_shaders::fs::UniformColor());
623                 glUseProgram(program);
624 
625                 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
626                 GLint positionLocation =
627                     glGetAttribLocation(program, essl1_shaders::PositionAttrib());
628 
629                 // Use sharedVertexBuffer
630                 glBindBuffer(GL_ARRAY_BUFFER, sharedVertexBuffer);
631                 glEnableVertexAttribArray(positionLocation);
632                 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
633 
634                 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
635                 {
636                     // Base the clear color on the thread and iteration indexes so every clear color
637                     // is unique
638                     const GLColor color(static_cast<GLubyte>(threadIdx % 255),
639                                         static_cast<GLubyte>(iteration % 255), 0, 255);
640                     const angle::Vector4 floatColor = color.toNormalizedVector();
641                     glUniform4fv(colorLocation, 1, floatColor.data());
642 
643                     for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
644                     {
645                         glDrawArrays(GL_TRIANGLES, 0, 6);
646                     }
647 
648                     EXPECT_PIXEL_COLOR_EQ(0, 0, color);
649                 }
650             }
651 
652             // tear down shared context
653             {
654                 std::lock_guard<decltype(mutex)> lock(mutex);
655                 EXPECT_EGL_TRUE(
656                     eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
657                 EXPECT_EGL_SUCCESS();
658                 eglDestroySurface(dpy, surface);
659                 eglDestroyContext(dpy, ctx);
660             }
661         });
662     }
663 
664     for (std::thread &thread : threads)
665     {
666         thread.join();
667     }
668 
669     eglDestroySurface(dpy, sharedSurface);
670     eglDestroyContext(dpy, sharedCtx);
671 
672     // Re-make current the test window's context for teardown.
673     EXPECT_EGL_TRUE(
674         eglMakeCurrent(dpy, window->getSurface(), window->getSurface(), window->getContext()));
675     EXPECT_EGL_SUCCESS();
676 }
677 
678 // Producer/Consumer test using EGLImages and EGLSyncs
TEST_P(MultithreadingTest,EGLImageProduceConsume)679 TEST_P(MultithreadingTest, EGLImageProduceConsume)
680 {
681     EGLWindow *window  = getEGLWindow();
682     EGLDisplay dpy     = window->getDisplay();
683     EGLContext rootCtx = window->getContext();
684 
685     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image"));
686     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
687     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_fence_sync"));
688 
689     struct sharedImage
690     {
691         EGLImage image;
692         EGLSync sync;
693         GLuint rootTexture;
694     };
695 
696     std::mutex mutex;
697     std::vector<sharedImage> waitingForProduce;
698     std::vector<sharedImage> waitingForConsume;
699 
700     constexpr size_t kNumImages = 10;
701     for (size_t i = 0; i < kNumImages; i++)
702     {
703         GLuint texture;
704         glGenTextures(1, &texture);
705         glBindTexture(GL_TEXTURE_2D, texture);
706         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
707 
708         sharedImage img;
709         img.image       = eglCreateImageKHR(dpy, rootCtx, EGL_GL_TEXTURE_2D_KHR,
710                                             reinterpret_cast<EGLClientBuffer>(texture), nullptr);
711         img.sync        = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
712         img.rootTexture = texture;
713 
714         waitingForProduce.push_back(std::move(img));
715     }
716 
717     constexpr size_t kIterations = 10000;
718 
719     std::thread producerThread([&]() {
720         EGLContext ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
721         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx));
722 
723         {
724             ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(),
725                              essl1_shaders::fs::UniformColor());
726             glUseProgram(drawColor);
727             GLint colorUniformLocation =
728                 glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
729             ASSERT_NE(colorUniformLocation, -1);
730 
731             size_t iteration = 0;
732             while (iteration < kIterations)
733             {
734                 sharedImage img;
735                 {
736                     std::lock_guard<decltype(mutex)> lock(mutex);
737                     if (waitingForProduce.empty())
738                     {
739                         continue;
740                     }
741                     img = std::move(waitingForProduce.back());
742                     waitingForProduce.pop_back();
743                 }
744 
745                 eglWaitSync(dpy, img.sync, 0);
746                 EXPECT_EGL_SUCCESS();
747 
748                 eglDestroySync(dpy, img.sync);
749 
750                 GLTexture texture;
751                 glBindTexture(GL_TEXTURE_2D, texture);
752                 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img.image);
753                 EXPECT_GL_NO_ERROR();
754 
755                 GLFramebuffer fbo;
756                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
757                 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
758                                        0);
759 
760                 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
761                 glClear(GL_COLOR_BUFFER_BIT);
762 
763                 glUniform4f(colorUniformLocation, float(iteration) / kIterations, 0.0f, 0.0f, 1.0f);
764                 drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0);
765 
766                 glBindTexture(GL_TEXTURE_2D, 0);
767                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
768 
769                 img.sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
770                 EXPECT_EGL_SUCCESS();
771                 glFlush();
772 
773                 {
774                     std::lock_guard<decltype(mutex)> lock(mutex);
775                     waitingForConsume.insert(waitingForConsume.begin(), std::move(img));
776                 }
777 
778                 iteration++;
779             }
780         }
781 
782         eglDestroyContext(dpy, ctx);
783     });
784 
785     std::thread consumerThread([&]() {
786         EGLContext ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
787         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx));
788 
789         {
790             ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
791                              essl1_shaders::fs::Texture2D());
792             glUseProgram(drawTexture);
793             GLint textureUniformLocation =
794                 glGetUniformLocation(drawTexture, angle::essl1_shaders::Texture2DUniform());
795             ASSERT_NE(textureUniformLocation, -1);
796             glUniform1i(textureUniformLocation, 0);
797             glActiveTexture(GL_TEXTURE0);
798 
799             GLTexture backbufferTexture;
800             glBindTexture(GL_TEXTURE_2D, backbufferTexture);
801             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE,
802                          nullptr);
803 
804             GLFramebuffer fbo;
805             glBindFramebuffer(GL_FRAMEBUFFER, fbo);
806             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
807                                    backbufferTexture, 0);
808 
809             size_t iteration = 0;
810             while (iteration < kIterations)
811             {
812                 sharedImage img;
813                 {
814                     std::lock_guard<decltype(mutex)> lock(mutex);
815                     if (waitingForConsume.empty())
816                     {
817                         continue;
818                     }
819                     img = std::move(waitingForConsume.back());
820                     waitingForConsume.pop_back();
821                 }
822 
823                 eglWaitSync(dpy, img.sync, 0);
824                 EXPECT_EGL_SUCCESS();
825                 eglDestroySync(dpy, img.sync);
826 
827                 GLTexture texture;
828                 glBindTexture(GL_TEXTURE_2D, texture);
829                 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img.image);
830                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
831                 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
832 
833                 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0);
834                 EXPECT_GL_NO_ERROR();
835 
836                 glBindTexture(GL_TEXTURE_2D, 0);
837 
838                 img.sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
839                 EXPECT_EGL_SUCCESS();
840                 glFlush();
841 
842                 {
843                     std::lock_guard<decltype(mutex)> lock(mutex);
844                     waitingForProduce.insert(waitingForProduce.begin(), std::move(img));
845                 }
846 
847                 iteration++;
848             }
849         }
850         eglDestroyContext(dpy, ctx);
851     });
852 
853     producerThread.join();
854     consumerThread.join();
855 
856     // Clean up
857     {
858         for (auto &img : waitingForProduce)
859         {
860             eglDestroyImageKHR(dpy, img.image);
861             eglDestroySync(dpy, img.sync);
862             glDeleteTextures(1, &img.rootTexture);
863         }
864         for (auto &img : waitingForConsume)
865         {
866             eglDestroyImageKHR(dpy, img.image);
867             eglDestroySync(dpy, img.sync);
868             glDeleteTextures(1, &img.rootTexture);
869         }
870     }
871 }
872 
textureThreadFunction(bool useDraw)873 void MultithreadingTestES3::textureThreadFunction(bool useDraw)
874 {
875     EGLWindow *window  = getEGLWindow();
876     EGLDisplay dpy     = window->getDisplay();
877     EGLConfig config   = window->getConfig();
878     EGLSurface surface = EGL_NO_SURFACE;
879     EGLContext ctx     = EGL_NO_CONTEXT;
880 
881     // Initialize the pbuffer and context
882     EGLint pbufferAttributes[] = {
883         EGL_WIDTH, kSize, EGL_HEIGHT, kSize, EGL_NONE, EGL_NONE,
884     };
885     surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
886     EXPECT_EGL_SUCCESS();
887     EXPECT_NE(EGL_NO_SURFACE, surface);
888 
889     ctx = createMultithreadedContext(window, window->getContext());
890     EXPECT_NE(EGL_NO_CONTEXT, ctx);
891 
892     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
893     EXPECT_EGL_SUCCESS();
894 
895     std::vector<GLColor> greenColor(kSize * kSize, GLColor::green);
896     std::vector<GLColor> redColor(kSize * kSize, GLColor::red);
897     ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
898     ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
899 
900     glBindTexture(GL_TEXTURE_2D, mTexture2D);
901     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
902     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
903     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
904     ASSERT_GL_NO_ERROR();
905 
906     GLFramebuffer fbo;
907     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
908     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
909     ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
910 
911     mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
912     ASSERT_GL_NO_ERROR();
913     // Force the fence to be created
914     glFlush();
915 
916     // Draw something
917     while (!mExitThread)
918     {
919         std::lock_guard<decltype(mMutex)> lock(mMutex);
920 
921         if (mMainThreadSyncObj != nullptr)
922         {
923             glWaitSync(mMainThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
924             ASSERT_GL_NO_ERROR();
925             glDeleteSync(mMainThreadSyncObj);
926             ASSERT_GL_NO_ERROR();
927             mMainThreadSyncObj = nullptr;
928         }
929         else
930         {
931             continue;
932         }
933 
934         glBindTexture(GL_TEXTURE_2D, mTexture2D);
935         ASSERT_GL_NO_ERROR();
936 
937         if (mDrawGreen)
938         {
939             if (useDraw)
940             {
941                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
942                 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.0f);
943             }
944             else
945             {
946                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
947                              greenColor.data());
948             }
949             ASSERT_GL_NO_ERROR();
950         }
951         else
952         {
953             if (useDraw)
954             {
955                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
956                 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.0f);
957             }
958             else
959             {
960                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
961                              redColor.data());
962             }
963             ASSERT_GL_NO_ERROR();
964         }
965 
966         ASSERT_EQ(mSecondThreadSyncObj.load(), nullptr);
967         mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
968         ASSERT_GL_NO_ERROR();
969         // Force the fence to be created
970         glFlush();
971 
972         mDrawGreen = !mDrawGreen;
973     }
974 
975     // Clean up
976     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
977     EXPECT_EGL_SUCCESS();
978 
979     eglDestroySurface(dpy, surface);
980     eglDestroyContext(dpy, ctx);
981 }
982 
983 // Test fence sync with multiple threads drawing
mainThreadDraw(bool useDraw)984 void MultithreadingTestES3::mainThreadDraw(bool useDraw)
985 {
986     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
987 
988     EGLWindow *window  = getEGLWindow();
989     EGLDisplay dpy     = window->getDisplay();
990     EGLContext ctx     = window->getContext();
991     EGLSurface surface = window->getSurface();
992     // Use odd numbers so we bounce between red and green in the final image
993     constexpr int kNumIterations = 5;
994     constexpr int kNumDraws      = 5;
995 
996     mDrawGreen = false;
997 
998     std::thread textureThread(&MultithreadingTestES3::textureThreadFunction, this, true);
999 
1000     ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
1001 
1002     for (int iterations = 0; iterations < kNumIterations; ++iterations)
1003     {
1004         for (int draws = 0; draws < kNumDraws;)
1005         {
1006             std::lock_guard<decltype(mMutex)> lock(mMutex);
1007 
1008             if (mSecondThreadSyncObj != nullptr)
1009             {
1010                 glWaitSync(mSecondThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
1011                 ASSERT_GL_NO_ERROR();
1012                 glDeleteSync(mSecondThreadSyncObj);
1013                 ASSERT_GL_NO_ERROR();
1014                 mSecondThreadSyncObj = nullptr;
1015             }
1016             else
1017             {
1018                 continue;
1019             }
1020 
1021             glBindFramebuffer(GL_FRAMEBUFFER, 0);
1022             glBindTexture(GL_TEXTURE_2D, mTexture2D);
1023             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1024             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1025             glUseProgram(texProgram);
1026             drawQuad(texProgram, essl1_shaders::PositionAttrib(), 0.0f);
1027 
1028             ASSERT_EQ(mMainThreadSyncObj.load(), nullptr);
1029             mMainThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1030             ASSERT_GL_NO_ERROR();
1031             // Force the fence to be created
1032             glFlush();
1033 
1034             ++draws;
1035         }
1036 
1037         ASSERT_GL_NO_ERROR();
1038         swapBuffers();
1039     }
1040 
1041     mExitThread = true;
1042     textureThread.join();
1043 
1044     ASSERT_GL_NO_ERROR();
1045     GLColor color;
1046     if (mDrawGreen)
1047     {
1048         color = GLColor::green;
1049     }
1050     else
1051     {
1052         color = GLColor::red;
1053     }
1054     EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, color);
1055 
1056     // Re-make current the test window's context for teardown.
1057     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
1058     EXPECT_EGL_SUCCESS();
1059 }
1060 
1061 // Test that glFenceSync/glWaitSync works correctly with multithreading.
1062 // Main thread: Samples from the shared texture to draw to the default FBO.
1063 // Secondary (Texture) thread: Draws to the shared texture, which the Main thread samples from.
1064 // The overall execution flow is:
1065 // Main Thread:
1066 // 1. Wait for the mSecondThreadSyncObj fence object to be created.
1067 //    - This fence object is used by synchronize access to the shared texture by indicating that the
1068 //    Secondary thread's draws to the texture have all completed and it's now safe to sample from
1069 //    it.
1070 // 2. Once the fence is created, add a glWaitSync(mSecondThreadSyncObj) to the command stream and
1071 //    then delete it.
1072 // 3. Draw, sampling from the shared texture.
1073 // 4. Create a new mMainThreadSyncObj.
1074 //    - This fence object is used to synchronize access to the shared texture by indicating that the
1075 //    Main thread's draws are no longer sampling from the texture, so it's now safe for the
1076 //    Secondary thread to draw to it again with a new color.
1077 // Secondary (Texture) Thread:
1078 // 1. Wait for the mMainThreadSyncObj fence object to be created.
1079 // 2. Once the fence is created, add a glWaitSync(mMainThreadSyncObj) to the command stream and then
1080 //    delete it.
1081 // 3. Draw/Fill the texture.
1082 // 4. Create a new mSecondThreadSyncObj.
1083 //
1084 // These threads loop for the specified number of iterations, drawing/sampling the shared texture
1085 // with the necessary glFlush()s and occasional eglSwapBuffers() to mimic a real multithreaded GLES
1086 // application.
TEST_P(MultithreadingTestES3,MultithreadFenceDraw)1087 TEST_P(MultithreadingTestES3, MultithreadFenceDraw)
1088 {
1089     // http://anglebug.com/5418
1090     ANGLE_SKIP_TEST_IF(IsLinux() && IsVulkan() && (IsIntel() || isSwiftshader()));
1091 
1092     // Have the secondary thread use glDrawArrays()
1093     mainThreadDraw(true);
1094 }
1095 
1096 // Same as MultithreadFenceDraw, but with the secondary thread using glTexImage2D rather than
1097 // glDrawArrays.
TEST_P(MultithreadingTestES3,MultithreadFenceTexImage)1098 TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)
1099 {
1100     // http://anglebug.com/5418
1101     ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
1102 
1103     // http://anglebug.com/5439
1104     ANGLE_SKIP_TEST_IF(IsLinux() && isSwiftshader());
1105 
1106     // Have the secondary thread use glTexImage2D()
1107     mainThreadDraw(false);
1108 }
1109 
1110 // Test that waiting on a sync object that hasn't been flushed and without a current context returns
1111 // TIMEOUT_EXPIRED or CONDITION_SATISFIED, but doesn't generate an error or crash.
TEST_P(MultithreadingTest,NoFlushNoContextReturnsTimeout)1112 TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)
1113 {
1114     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1115     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1116 
1117     std::mutex mutex;
1118 
1119     EGLWindow *window = getEGLWindow();
1120     EGLDisplay dpy    = window->getDisplay();
1121 
1122     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
1123     glClear(GL_COLOR_BUFFER_BIT);
1124 
1125     EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1126     EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1127 
1128     std::thread thread = std::thread([&]() {
1129         std::lock_guard<decltype(mutex)> lock(mutex);
1130         // Make sure there is no active context on this thread.
1131         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1132         EXPECT_EGL_SUCCESS();
1133         // Don't wait forever to make sure the test terminates
1134         constexpr GLuint64 kTimeout = 1'000'000'000;  // 1 second
1135         int result                  = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
1136         // We typically expect to get back TIMEOUT_EXPIRED since the sync object was never flushed.
1137         // However, the OpenGL ES backend returns CONDITION_SATISFIED, which is also a passing
1138         // result.
1139         ASSERT_TRUE(result == EGL_TIMEOUT_EXPIRED_KHR || result == EGL_CONDITION_SATISFIED_KHR);
1140     });
1141 
1142     thread.join();
1143 
1144     EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
1145 }
1146 
1147 // Test that waiting on sync object that hasn't been flushed yet, but is later flushed by another
1148 // thread, correctly returns when the fence is signalled without a timeout.
TEST_P(MultithreadingTest,CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)1149 TEST_P(MultithreadingTest, CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)
1150 {
1151     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1152     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1153 
1154     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
1155 
1156     std::mutex mutex;
1157     std::condition_variable condVar;
1158 
1159     enum class Step
1160     {
1161         Start,
1162         Thread0Clear,
1163         Thread1CreateFence,
1164         Thread0ClientWaitSync,
1165         Thread1Flush,
1166         Finish,
1167         Abort,
1168     };
1169     Step currentStep = Step::Start;
1170 
1171     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1172         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1173 
1174         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1175 
1176         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1177 
1178         // Do work.
1179         glClearColor(1.0, 0.0, 0.0, 1.0);
1180         glClear(GL_COLOR_BUFFER_BIT);
1181 
1182         // Wait for thread 1 to clear.
1183         threadSynchronization.nextStep(Step::Thread0Clear);
1184         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1185 
1186         // Wait on the sync object, but do *not* flush it, since the other thread will flush.
1187         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1188         threadSynchronization.nextStep(Step::Thread0ClientWaitSync);
1189         ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, eglClientWaitSyncKHR(dpy, sync, 0, kTimeout));
1190 
1191         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1192     };
1193 
1194     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1195         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1196 
1197         // Wait for thread 0 to clear.
1198         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
1199 
1200         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1201 
1202         // Do work.
1203         glClearColor(0.0, 1.0, 0.0, 1.0);
1204         glClear(GL_COLOR_BUFFER_BIT);
1205 
1206         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1207         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1208 
1209         // Wait for the thread 0 to eglClientWaitSyncKHR().
1210         threadSynchronization.nextStep(Step::Thread1CreateFence);
1211         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ClientWaitSync));
1212 
1213         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1214         angle::Sleep(500);
1215         glFlush();
1216 
1217         // Clean up
1218         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1219 
1220         threadSynchronization.nextStep(Step::Finish);
1221     };
1222 
1223     std::array<LockStepThreadFunc, 2> threadFuncs = {
1224         std::move(thread0),
1225         std::move(thread1),
1226     };
1227 
1228     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1229 
1230     ASSERT_NE(currentStep, Step::Abort);
1231 }
1232 
1233 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1234 // that.
testFenceWithOpenRenderPass(FenceTest test,FlushMethod flushMethod)1235 void MultithreadingTestES3::testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod)
1236 {
1237     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1238     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1239 
1240     constexpr uint32_t kWidth  = 100;
1241     constexpr uint32_t kHeight = 200;
1242 
1243     GLsync sync    = 0;
1244     GLuint texture = 0;
1245 
1246     std::mutex mutex;
1247     std::condition_variable condVar;
1248 
1249     enum class Step
1250     {
1251         Start,
1252         Thread0CreateFence,
1253         Thread1WaitFence,
1254         Thread0Finish,
1255         Finish,
1256         Abort,
1257     };
1258     Step currentStep = Step::Start;
1259 
1260     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1261         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1262 
1263         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1264 
1265         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1266 
1267         // Create a shared texture to test synchronization
1268         GLTexture color;
1269         texture = color;
1270 
1271         glBindTexture(GL_TEXTURE_2D, texture);
1272         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1273 
1274         GLFramebuffer fbo;
1275         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1276         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1277 
1278         // Draw to shared texture.
1279         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1280         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1281         ASSERT_GL_NO_ERROR();
1282 
1283         // Issue a fence.  A render pass is currently open, so the fence is not actually submitted
1284         // in the Vulkan backend.
1285         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1286         ASSERT_NE(sync, nullptr);
1287 
1288         // Wait for thread 1 to wait on it.
1289         threadSynchronization.nextStep(Step::Thread0CreateFence);
1290         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1WaitFence));
1291 
1292         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1293         angle::Sleep(500);
1294         switch (flushMethod)
1295         {
1296             case FlushMethod::Flush:
1297                 glFlush();
1298                 break;
1299             case FlushMethod::Finish:
1300                 glFinish();
1301                 break;
1302         }
1303 
1304         // Clean up
1305         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1306 
1307         threadSynchronization.nextStep(Step::Thread0Finish);
1308         threadSynchronization.waitForStep(Step::Finish);
1309     };
1310 
1311     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1312         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1313 
1314         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1315 
1316         // Wait for thread 0 to create the fence object.
1317         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateFence));
1318 
1319         // Test access to the fence object
1320         threadSynchronization.nextStep(Step::Thread1WaitFence);
1321 
1322         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1323         GLenum result               = GL_CONDITION_SATISFIED;
1324         switch (test)
1325         {
1326             case FenceTest::ClientWait:
1327                 result = glClientWaitSync(sync, 0, kTimeout);
1328                 break;
1329             case FenceTest::ServerWait:
1330                 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1331                 break;
1332             case FenceTest::GetStatus:
1333             {
1334                 GLint value;
1335                 glGetSynciv(sync, GL_SYNC_STATUS, 1, nullptr, &value);
1336                 if (value != GL_SIGNALED)
1337                 {
1338                     result = glClientWaitSync(sync, 0, kTimeout);
1339                 }
1340                 break;
1341             }
1342         }
1343         ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1344 
1345         // Verify the shared texture is drawn to.
1346         glBindTexture(GL_TEXTURE_2D, texture);
1347 
1348         GLFramebuffer fbo;
1349         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1350         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1351 
1352         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1353 
1354         // Clean up
1355         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1356 
1357         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Finish));
1358         threadSynchronization.nextStep(Step::Finish);
1359     };
1360 
1361     std::array<LockStepThreadFunc, 2> threadFuncs = {
1362         std::move(thread0),
1363         std::move(thread1),
1364     };
1365 
1366     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1367 
1368     ASSERT_NE(currentStep, Step::Abort);
1369 }
1370 
1371 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1372 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFlush)1373 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFlush)
1374 {
1375     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Flush);
1376 }
1377 
1378 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1379 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFlush)1380 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFlush)
1381 {
1382     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Flush);
1383 }
1384 
1385 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1386 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFlush)1387 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFlush)
1388 {
1389     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Flush);
1390 }
1391 
1392 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1393 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFinish)1394 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFinish)
1395 {
1396     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Finish);
1397 }
1398 
1399 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1400 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFinish)1401 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFinish)
1402 {
1403     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Finish);
1404 }
1405 
1406 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1407 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFinish)1408 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFinish)
1409 {
1410     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Finish);
1411 }
1412 
1413 // Test the following scenario:
1414 //
1415 // - Thread A opens a render pass, and flushes it.  In the Vulkan backend, this may make the flush
1416 //   deferred.
1417 // - Thread B opens a render pass and creates a fence.  In the Vulkan backend, this also defers the
1418 //   flush.
1419 // - Thread C waits on fence
1420 //
1421 // In the Vulkan backend, submission of the fence is implied by thread C's wait, and thread A may
1422 // also be flushed as collateral.  If the fence's serial is updated based on thread A's submission,
1423 // synchronization between B and C would be broken.
TEST_P(MultithreadingTestES3,ThreadCWaitBeforeThreadBSyncFinish)1424 TEST_P(MultithreadingTestES3, ThreadCWaitBeforeThreadBSyncFinish)
1425 {
1426     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1427     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1428 
1429     constexpr uint32_t kWidth  = 100;
1430     constexpr uint32_t kHeight = 200;
1431 
1432     GLsync sync    = 0;
1433     GLuint texture = 0;
1434 
1435     std::mutex mutex;
1436     std::condition_variable condVar;
1437 
1438     enum class Step
1439     {
1440         Start,
1441         Thread0DrawAndFlush,
1442         Thread1CreateFence,
1443         Thread2WaitFence,
1444         Thread2Finished,
1445         Finish,
1446         Abort,
1447     };
1448     Step currentStep = Step::Start;
1449 
1450     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1451         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1452 
1453         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1454 
1455         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1456 
1457         // Open a render pass and flush it.
1458         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1459         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1460         glFlush();
1461         ASSERT_GL_NO_ERROR();
1462 
1463         threadSynchronization.nextStep(Step::Thread0DrawAndFlush);
1464         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1465 
1466         // Clean up
1467         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1468     };
1469 
1470     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1471         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1472 
1473         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1474 
1475         // Wait for thread 0 to set up
1476         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DrawAndFlush));
1477 
1478         // Create a shared texture to test synchronization
1479         GLTexture color;
1480         texture = color;
1481 
1482         glBindTexture(GL_TEXTURE_2D, texture);
1483         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1484 
1485         GLFramebuffer fbo;
1486         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1487         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1488 
1489         // Draw to shared texture.
1490         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1491         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1492         ASSERT_GL_NO_ERROR();
1493 
1494         // Issue a fence.  A render pass is currently open, so the fence is not actually submitted
1495         // in the Vulkan backend.
1496         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1497         ASSERT_NE(sync, nullptr);
1498 
1499         // Wait for thread 1 to wait on it.
1500         threadSynchronization.nextStep(Step::Thread1CreateFence);
1501         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2WaitFence));
1502 
1503         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1504         angle::Sleep(500);
1505         glFlush();
1506 
1507         // Clean up
1508         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1509 
1510         threadSynchronization.nextStep(Step::Thread2Finished);
1511         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1512     };
1513 
1514     auto thread2 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1515         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1516 
1517         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1518 
1519         // Wait for thread 0 to create the fence object.
1520         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1521 
1522         // Test access to the fence object
1523         threadSynchronization.nextStep(Step::Thread2WaitFence);
1524 
1525         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1526         GLenum result               = glClientWaitSync(sync, 0, kTimeout);
1527         ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1528 
1529         // Verify the shared texture is drawn to.
1530         glBindTexture(GL_TEXTURE_2D, texture);
1531 
1532         GLFramebuffer fbo;
1533         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1534         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1535 
1536         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1537 
1538         // Clean up
1539         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1540 
1541         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2Finished));
1542         threadSynchronization.nextStep(Step::Finish);
1543     };
1544 
1545     std::array<LockStepThreadFunc, 3> threadFuncs = {
1546         std::move(thread0),
1547         std::move(thread1),
1548         std::move(thread2),
1549     };
1550 
1551     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1552 
1553     ASSERT_NE(currentStep, Step::Abort);
1554 }
1555 
1556 // Test that having commands recorded but not submitted on one thread using a texture, does not
1557 // interfere with similar commands on another thread using the same texture.  Regression test for a
1558 // bug in the Vulkan backend where the first thread would batch updates to a descriptor set not
1559 // visible to the other thread, while the other thread picks up the (unupdated) descriptor set from
1560 // a shared cache.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads)1561 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads)
1562 {
1563     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1564     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1565 
1566     GLsync sync    = 0;
1567     GLuint texture = 0;
1568 
1569     constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1570 
1571     std::mutex mutex;
1572     std::condition_variable condVar;
1573 
1574     enum class Step
1575     {
1576         Start,
1577         Thread0CreateTextureAndDraw,
1578         Thread1DrawAndFlush,
1579         Finish,
1580         Abort,
1581     };
1582     Step currentStep = Step::Start;
1583 
1584     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1585         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1586 
1587         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1588 
1589         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1590 
1591         // Create a texture, and record a command that draws into it.
1592         GLTexture color;
1593         texture = color;
1594 
1595         glBindTexture(GL_TEXTURE_2D, texture);
1596         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1597         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1598         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1599         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1600 
1601         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1602         ASSERT_NE(sync, nullptr);
1603 
1604         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1605                          essl1_shaders::fs::Texture2D());
1606         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1607 
1608         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1609         // Vulkan backend.
1610         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1611         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1612 
1613         // Flush after thread 1
1614         EXPECT_PIXEL_COLOR_NEAR(
1615             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1616 
1617         // Clean up
1618         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1619 
1620         threadSynchronization.nextStep(Step::Finish);
1621     };
1622 
1623     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1624         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1625 
1626         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1627 
1628         // Wait for thread 0 to set up
1629         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1630 
1631         // Synchronize with the texture upload (but not the concurrent read)
1632         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1633 
1634         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1635         // descriptor sets used in the Vulkan backend are identical.
1636         glBindTexture(GL_TEXTURE_2D, texture);
1637         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1638                          essl1_shaders::fs::Texture2D());
1639         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1640 
1641         // Flush
1642         EXPECT_PIXEL_COLOR_NEAR(
1643             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1644 
1645         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1646 
1647         // Clean up
1648         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1649 
1650         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1651     };
1652 
1653     std::array<LockStepThreadFunc, 2> threadFuncs = {
1654         std::move(thread0),
1655         std::move(thread1),
1656     };
1657 
1658     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1659 
1660     ASSERT_NE(currentStep, Step::Abort);
1661 }
1662 
1663 // Similar to UnsynchronizedTextureReads, but the texture update is done through framebuffer write.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads2)1664 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads2)
1665 {
1666     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1667     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1668 
1669     GLsync sync    = 0;
1670     GLuint texture = 0;
1671 
1672     std::mutex mutex;
1673     std::condition_variable condVar;
1674 
1675     enum class Step
1676     {
1677         Start,
1678         Thread0CreateTextureAndDraw,
1679         Thread1DrawAndFlush,
1680         Finish,
1681         Abort,
1682     };
1683     Step currentStep = Step::Start;
1684 
1685     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1686         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1687 
1688         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1689 
1690         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1691 
1692         // Create a texture, and record a command that draws into it.
1693         GLTexture color;
1694         texture = color;
1695 
1696         glBindTexture(GL_TEXTURE_2D, texture);
1697         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1698         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1699         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1700 
1701         GLFramebuffer fbo;
1702         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1703         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1704 
1705         glClearColor(1, 0, 0, 1);
1706         glClear(GL_COLOR_BUFFER_BIT);
1707         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1708         glBindFramebuffer(GL_FRAMEBUFFER, 0);
1709 
1710         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1711         ASSERT_NE(sync, nullptr);
1712 
1713         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1714                          essl1_shaders::fs::Texture2D());
1715         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1716 
1717         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1718         // Vulkan backend.
1719         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1720         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1721 
1722         // Flush after thread 1
1723         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1724 
1725         // Clean up
1726         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1727 
1728         threadSynchronization.nextStep(Step::Finish);
1729     };
1730 
1731     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1732         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1733 
1734         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1735 
1736         // Wait for thread 0 to set up
1737         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1738 
1739         // Synchronize with the texture update (but not the concurrent read)
1740         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1741 
1742         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1743         // descriptor sets used in the Vulkan backend are identical.
1744         glBindTexture(GL_TEXTURE_2D, texture);
1745         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1746                          essl1_shaders::fs::Texture2D());
1747         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1748 
1749         // Flush
1750         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1751 
1752         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1753 
1754         // Clean up
1755         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1756 
1757         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1758     };
1759 
1760     std::array<LockStepThreadFunc, 2> threadFuncs = {
1761         std::move(thread0),
1762         std::move(thread1),
1763     };
1764 
1765     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1766 
1767     ASSERT_NE(currentStep, Step::Abort);
1768 }
1769 
1770 // Similar to UnsynchronizedTextureReads, but the texture is used once.  This is because
1771 // UnsynchronizedTextureRead hits a different bug than it intends to test.  This test makes sure the
1772 // image is put in the right layout, by using it together with another texture (i.e. a different
1773 // descriptor set).
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads3)1774 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads3)
1775 {
1776     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1777 
1778     constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1779 
1780     GLuint texture = 0;
1781 
1782     std::mutex mutex;
1783     std::condition_variable condVar;
1784 
1785     enum class Step
1786     {
1787         Start,
1788         Thread0CreateTextureAndDraw,
1789         Thread1DrawAndFlush,
1790         Finish,
1791         Abort,
1792     };
1793     Step currentStep = Step::Start;
1794 
1795     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1796         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1797 
1798         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1799 
1800         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1801 
1802         // Create a texture, and record a command that draws into it.
1803         GLTexture color;
1804         texture = color;
1805 
1806         glActiveTexture(GL_TEXTURE0);
1807         glBindTexture(GL_TEXTURE_2D, texture);
1808         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1809         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1810         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1811         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1812 
1813         glActiveTexture(GL_TEXTURE1);
1814         GLTexture color2;
1815         glBindTexture(GL_TEXTURE_2D, color2);
1816         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1817         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1818         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1819         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1820 
1821         ANGLE_GL_PROGRAM(setupTexture, essl1_shaders::vs::Texture2D(),
1822                          R"(precision mediump float;
1823 uniform sampler2D tex2D;
1824 uniform sampler2D tex2D2;
1825 varying vec2 v_texCoord;
1826 
1827 void main()
1828 {
1829     gl_FragColor = texture2D(tex2D, v_texCoord) + texture2D(tex2D2, v_texCoord);
1830 })");
1831         drawQuad(setupTexture, essl1_shaders::PositionAttrib(), 0.0f);
1832         ASSERT_GL_NO_ERROR();
1833 
1834         glFinish();
1835 
1836         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1837                          essl1_shaders::fs::Texture2D());
1838         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1839         ASSERT_GL_NO_ERROR();
1840 
1841         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1842         // Vulkan backend.
1843         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1844         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1845 
1846         // Flush after thread 1
1847         EXPECT_PIXEL_COLOR_NEAR(
1848             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1849 
1850         // Clean up
1851         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1852 
1853         threadSynchronization.nextStep(Step::Finish);
1854     };
1855 
1856     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1857         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1858 
1859         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1860 
1861         // Wait for thread 0 to set up
1862         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1863 
1864         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1865         // descriptor sets used in the Vulkan backend are identical.
1866         glBindTexture(GL_TEXTURE_2D, texture);
1867         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1868                          essl1_shaders::fs::Texture2D());
1869         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1870         ASSERT_GL_NO_ERROR();
1871 
1872         // Flush
1873         EXPECT_PIXEL_COLOR_NEAR(
1874             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1875 
1876         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1877 
1878         // Clean up
1879         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1880 
1881         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1882     };
1883 
1884     std::array<LockStepThreadFunc, 2> threadFuncs = {
1885         std::move(thread0),
1886         std::move(thread1),
1887     };
1888 
1889     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1890 
1891     ASSERT_NE(currentStep, Step::Abort);
1892 }
1893 
1894 // Test framebuffer fetch program used between share groups.
testFramebufferFetch(DrawOrder drawOrder)1895 void MultithreadingTestES3::testFramebufferFetch(DrawOrder drawOrder)
1896 {
1897     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1898     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
1899 
1900     GLProgram framebufferFetchProgram;
1901 
1902     constexpr char kFS[] = R"(#version 300 es
1903 #extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
1904 layout(noncoherent, location = 0) inout highp vec4 o_color;
1905 
1906 uniform highp vec4 u_color;
1907 void main (void)
1908 {
1909     o_color += u_color;
1910 })";
1911 
1912     std::mutex mutex;
1913     std::condition_variable condVar;
1914 
1915     enum class Step
1916     {
1917         Start,
1918         Thread0PreCreateProgram,
1919         Thread1CreateProgram,
1920         Finish,
1921         Abort,
1922     };
1923     Step currentStep = Step::Start;
1924 
1925     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1926         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1927 
1928         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1929 
1930         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1931 
1932         // Open a render pass, if requested.
1933         if (drawOrder == DrawOrder::Before)
1934         {
1935             ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1936             drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1937             ASSERT_GL_NO_ERROR();
1938         }
1939 
1940         threadSynchronization.nextStep(Step::Thread0PreCreateProgram);
1941         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateProgram));
1942 
1943         // Render using the framebuffer fetch program
1944         if (drawOrder == DrawOrder::After)
1945         {
1946             ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1947             drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1948             ASSERT_GL_NO_ERROR();
1949         }
1950 
1951         glFramebufferFetchBarrierEXT();
1952 
1953         glUseProgram(framebufferFetchProgram);
1954         GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1955         glUniform4f(colorLocation, 1, 0, 0, 0);
1956         drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1957         ASSERT_GL_NO_ERROR();
1958 
1959         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
1960 
1961         threadSynchronization.nextStep(Step::Finish);
1962 
1963         // Clean up
1964         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1965     };
1966 
1967     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1968         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1969 
1970         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1971 
1972         // Wait for thread 0 to set up
1973         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0PreCreateProgram));
1974 
1975         // Create the framebuffer fetch program
1976         framebufferFetchProgram.makeRaster(essl3_shaders::vs::Simple(), kFS);
1977         glUseProgram(framebufferFetchProgram);
1978 
1979         // Notify the other thread to use it
1980         threadSynchronization.nextStep(Step::Thread1CreateProgram);
1981         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1982 
1983         glClearColor(0, 0, 0, 1);
1984         glClear(GL_COLOR_BUFFER_BIT);
1985 
1986         glFramebufferFetchBarrierEXT();
1987 
1988         glUseProgram(framebufferFetchProgram);
1989         GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1990         glUniform4f(colorLocation, 0, 0, 1, 0);
1991         drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1992         ASSERT_GL_NO_ERROR();
1993 
1994         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1995 
1996         // Clean up
1997         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1998     };
1999 
2000     std::array<LockStepThreadFunc, 2> threadFuncs = {
2001         std::move(thread0),
2002         std::move(thread1),
2003     };
2004 
2005     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
2006 
2007     ASSERT_NE(currentStep, Step::Abort);
2008 }
2009 
2010 // Thread 1 creates the framebuffer fetch program.  Thread 0 proceeds to use it.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchBeforeRenderPass)2011 TEST_P(MultithreadingTestES3, CreateFramebufferFetchBeforeRenderPass)
2012 {
2013     testFramebufferFetch(DrawOrder::After);
2014 }
2015 
2016 // Thread 1 creates the framebuffer fetch program while thread 0 is mid render pass.  Thread 0
2017 // proceeds to use the framebuffer fetch program in the rest of its render pass.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchMidRenderPass)2018 TEST_P(MultithreadingTestES3, CreateFramebufferFetchMidRenderPass)
2019 {
2020     testFramebufferFetch(DrawOrder::Before);
2021 }
2022 
2023 // Test async monolithic pipeline creation in the Vulkan backend vs shared programs.  This test
2024 // makes one context/thread create a set of programs, then has another context/thread use them a few
2025 // times, and then the original context destroys them.
TEST_P(MultithreadingTestES3,ProgramUseAndDestroyInTwoContexts)2026 TEST_P(MultithreadingTestES3, ProgramUseAndDestroyInTwoContexts)
2027 {
2028     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2029 
2030     GLProgram programs[6];
2031 
2032     GLsync sync = 0;
2033 
2034     std::mutex mutex;
2035     std::condition_variable condVar;
2036 
2037     enum class Step
2038     {
2039         Start,
2040         Thread0CreatePrograms,
2041         Thread1UsePrograms,
2042         Finish,
2043         Abort,
2044     };
2045     Step currentStep = Step::Start;
2046 
2047     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2048         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2049 
2050         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2051 
2052         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2053 
2054         // Create the programs
2055         programs[0].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2056         programs[1].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2057         programs[2].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2058         programs[3].makeRaster(essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Checkered());
2059         programs[4].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
2060         programs[5].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
2061 
2062         EXPECT_TRUE(programs[0].valid());
2063         EXPECT_TRUE(programs[1].valid());
2064         EXPECT_TRUE(programs[2].valid());
2065         EXPECT_TRUE(programs[3].valid());
2066         EXPECT_TRUE(programs[4].valid());
2067         EXPECT_TRUE(programs[5].valid());
2068 
2069         threadSynchronization.nextStep(Step::Thread0CreatePrograms);
2070         // Wait for the other thread to use the programs
2071         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsePrograms));
2072 
2073         // Destroy them
2074         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
2075         programs[0].reset();
2076         programs[1].reset();
2077         programs[2].reset();
2078         programs[3].reset();
2079         programs[4].reset();
2080         programs[5].reset();
2081 
2082         threadSynchronization.nextStep(Step::Finish);
2083 
2084         // Clean up
2085         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2086     };
2087 
2088     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2089         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2090 
2091         // Wait for thread 0 to create the programs
2092         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatePrograms));
2093 
2094         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2095 
2096         // Use them a few times.
2097         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2098         drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2099         drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2100         drawQuad(programs[3], essl1_shaders::PositionAttrib(), 0.0f);
2101         drawQuad(programs[4], essl1_shaders::PositionAttrib(), 0.0f);
2102 
2103         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2104         drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2105         drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2106 
2107         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2108 
2109         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2110         ASSERT_NE(sync, nullptr);
2111 
2112         // Notify the other thread to destroy the programs.
2113         threadSynchronization.nextStep(Step::Thread1UsePrograms);
2114         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2115 
2116         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
2117 
2118         // Clean up
2119         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2120     };
2121 
2122     std::array<LockStepThreadFunc, 2> threadFuncs = {
2123         std::move(thread0),
2124         std::move(thread1),
2125     };
2126 
2127     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
2128 
2129     ASSERT_NE(currentStep, Step::Abort);
2130 }
2131 
2132 // Tests that Context with High Priority will correctly sample Texture rendered by Share Context
2133 // with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriority)2134 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriority)
2135 {
2136     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2137 
2138     constexpr size_t kIterationCountMax = 10;
2139 
2140     const bool reduceLoad       = isSwiftshader();
2141     const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2142     const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2143 
2144     // Initialize contexts
2145     EGLWindow *window = getEGLWindow();
2146     EGLDisplay dpy    = window->getDisplay();
2147     EGLConfig config  = window->getConfig();
2148 
2149     // Large enough texture to catch timing problems.
2150     constexpr GLsizei kTexSize       = 1024;
2151     constexpr size_t kThreadCount    = 2;
2152     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2153     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2154 
2155     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2156 
2157     EGLint pbufferAttributes[kThreadCount][6] = {
2158         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2159         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2160 
2161     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2162                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2163     EGLint *extraAttributes = attributes;
2164     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2165     {
2166         attributes[2] = EGL_NONE;
2167     }
2168     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2169     {
2170         // Run tests with single priority anyway.
2171         extraAttributes += 2;
2172     }
2173 
2174     for (size_t t = 0; t < kThreadCount; ++t)
2175     {
2176         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2177         EXPECT_EGL_SUCCESS();
2178 
2179         attributes[1] = priorities[t];
2180         attributes[3] = mVirtualizationGroup++;
2181 
2182         ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2183         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2184     }
2185 
2186     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2187     std::mutex mutex;
2188     std::condition_variable condVar;
2189 
2190     enum class Step
2191     {
2192         Start,
2193         Thread0Init,
2194         Thread1Init,
2195         Thread0Draw,
2196         Thread1Draw,
2197         Finish = Thread0Draw + kIterationCountMax * 2,
2198         Abort,
2199     };
2200     Step currentStep = Step::Start;
2201 
2202     GLTexture texture;
2203     GLsync thread0DrawSyncObj;
2204 
2205     auto calculateTestColor = [](size_t i) {
2206         return GLColor(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2207     };
2208     auto makeStep = [](Step base, size_t i) {
2209         return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2210     };
2211 
2212     // Render to the texture.
2213     std::thread thread0 = std::thread([&]() {
2214         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2215 
2216         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2217         EXPECT_EGL_SUCCESS();
2218 
2219         glBindTexture(GL_TEXTURE_2D, texture);
2220         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2221                      nullptr);
2222         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2223         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2224 
2225         GLFramebuffer fbo;
2226         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2227         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2228         glViewport(0, 0, kTexSize, kTexSize);
2229 
2230         ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2231                          essl1_shaders::fs::UniformColor());
2232         GLint colorLocation =
2233             glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2234         ASSERT_NE(-1, colorLocation);
2235         glUseProgram(colorProgram);
2236 
2237         // Notify second thread that initialization is finished.
2238         threadSynchronization.nextStep(Step::Thread0Init);
2239         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2240 
2241         for (size_t i = 0; i < iterationCount; ++i)
2242         {
2243             // Simulate heavy work...
2244             glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2245             for (size_t j = 0; j < heavyDrawCount; ++j)
2246             {
2247                 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2248             }
2249 
2250             // Draw with test color.
2251             Vector4 color = calculateTestColor(i).toNormalizedVector();
2252             glUniform4f(colorLocation, color.x(), color.y(), color.z(), color.w());
2253             drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2254 
2255             thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2256             ASSERT_GL_NO_ERROR();
2257 
2258             // Notify second thread that draw is finished.
2259             threadSynchronization.nextStep(makeStep(Step::Thread0Draw, i));
2260             ASSERT_TRUE(threadSynchronization.waitForStep(
2261                 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i)));
2262         }
2263 
2264         EXPECT_GL_NO_ERROR();
2265         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2266         EXPECT_EGL_SUCCESS();
2267     });
2268 
2269     // Sample texture
2270     std::thread thread1 = std::thread([&]() {
2271         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2272 
2273         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2274         EXPECT_EGL_SUCCESS();
2275 
2276         glViewport(0, 0, kTexSize, kTexSize);
2277 
2278         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2279                          essl1_shaders::fs::Texture2D());
2280         glUseProgram(textureProgram);
2281 
2282         // Wait for first thread to finish initializing.
2283         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Init));
2284 
2285         glBindTexture(GL_TEXTURE_2D, texture);
2286 
2287         // Wait for first thread to draw using the shared texture.
2288         threadSynchronization.nextStep(Step::Thread1Init);
2289 
2290         for (size_t i = 0; i < iterationCount; ++i)
2291         {
2292             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Draw, i)));
2293 
2294             ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2295             glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2296             ASSERT_GL_NO_ERROR();
2297 
2298             // Should draw test color.
2299             drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2300 
2301             // Check test color in four corners.
2302             GLColor color = calculateTestColor(i);
2303             EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2304             EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2305             EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2306             EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2307 
2308             glDeleteSync(thread0DrawSyncObj);
2309             ASSERT_GL_NO_ERROR();
2310             thread0DrawSyncObj = nullptr;
2311 
2312             threadSynchronization.nextStep(
2313                 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i));
2314         }
2315 
2316         EXPECT_GL_NO_ERROR();
2317         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2318         EXPECT_EGL_SUCCESS();
2319     });
2320 
2321     thread0.join();
2322     thread1.join();
2323 
2324     ASSERT_NE(currentStep, Step::Abort);
2325 
2326     // Clean up
2327     for (size_t t = 0; t < kThreadCount; ++t)
2328     {
2329         eglDestroySurface(dpy, surface[t]);
2330         eglDestroyContext(dpy, ctx[t]);
2331     }
2332 }
2333 
2334 // Tests that newly created Context with High Priority will correctly sample Texture already
2335 // rendered by Share Context with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleInNewContextWithDifferentPriority)2336 TEST_P(MultithreadingTestES3, RenderThenSampleInNewContextWithDifferentPriority)
2337 {
2338     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2339     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2340 
2341     const bool reduceLoad       = isSwiftshader();
2342     const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2343 
2344     // Initialize contexts
2345     EGLWindow *window = getEGLWindow();
2346     EGLDisplay dpy    = window->getDisplay();
2347     EGLConfig config  = window->getConfig();
2348 
2349     // Large enough texture to catch timing problems.
2350     constexpr GLsizei kTexSize       = 1024;
2351     constexpr size_t kThreadCount    = 2;
2352     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2353     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2354 
2355     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2356 
2357     EGLint pbufferAttributes[kThreadCount][6] = {
2358         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2359         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2360 
2361     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2362                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2363     EGLint *extraAttributes = attributes;
2364     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2365     {
2366         attributes[2] = EGL_NONE;
2367     }
2368     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2369     {
2370         // Run tests with single priority anyway.
2371         extraAttributes += 2;
2372     }
2373 
2374     for (size_t t = 0; t < kThreadCount; ++t)
2375     {
2376         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2377         EXPECT_EGL_SUCCESS();
2378 
2379         attributes[1] = priorities[t];
2380         attributes[3] = mVirtualizationGroup++;
2381 
2382         // Second context will be created in a thread 1
2383         if (t == 0)
2384         {
2385             ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2386             EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2387         }
2388     }
2389 
2390     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2391     std::mutex mutex;
2392     std::condition_variable condVar;
2393 
2394     enum class Step
2395     {
2396         Start,
2397         Thread0Draw,
2398         Thread1Draw,
2399         Finish,
2400         Abort,
2401     };
2402     Step currentStep = Step::Start;
2403 
2404     // Create shared resources before threads to minimize timing delays.
2405     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2406     EXPECT_EGL_SUCCESS();
2407 
2408     ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2409     ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2410     ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2411     ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2412                      essl1_shaders::fs::Texture2D());
2413 
2414     GLTexture texture;
2415     glBindTexture(GL_TEXTURE_2D, texture);
2416     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2417                  nullptr);
2418     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2419     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2420 
2421     GLFramebuffer fbo;
2422     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2423     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2424     glViewport(0, 0, kTexSize, kTexSize);
2425 
2426     EXPECT_GL_NO_ERROR();
2427     EXPECT_EGL_TRUE(window->makeCurrent());
2428     EXPECT_EGL_SUCCESS();
2429 
2430     GLsync thread0DrawSyncObj;
2431 
2432     // Render to the texture.
2433     std::thread thread0 = std::thread([&]() {
2434         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2435 
2436         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2437         EXPECT_EGL_SUCCESS();
2438 
2439         // Enable additive blend
2440         glEnable(GL_BLEND);
2441         glBlendFunc(GL_ONE, GL_ONE);
2442 
2443         GLuint query;
2444         glGenQueries(1, &query);
2445         glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2446         ASSERT_GL_NO_ERROR();
2447 
2448         // Simulate heavy work...
2449         glUseProgram(redProgram);
2450         for (size_t j = 0; j < heavyDrawCount; ++j)
2451         {
2452             // Draw with Red color.
2453             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2454         }
2455 
2456         // Draw with Green color.
2457         glUseProgram(greenProgram);
2458         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2459 
2460         // This should force "flushToPrimary()"
2461         glEndQuery(GL_TIME_ELAPSED_EXT);
2462         glDeleteQueries(1, &query);
2463         ASSERT_GL_NO_ERROR();
2464 
2465         // Continue draw with Blue color after flush...
2466         glUseProgram(blueProgram);
2467         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2468 
2469         thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2470         ASSERT_GL_NO_ERROR();
2471 
2472         // Notify second thread that draw is finished.
2473         threadSynchronization.nextStep(Step::Thread0Draw);
2474         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2475 
2476         EXPECT_GL_NO_ERROR();
2477         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2478         EXPECT_EGL_SUCCESS();
2479     });
2480 
2481     // Sample texture
2482     std::thread thread1 = std::thread([&]() {
2483         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2484 
2485         // Wait for first thread to finish draw.
2486         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2487 
2488         // Create High priority Context when Low priority Context already rendered to the texture.
2489         ctx[1] = window->createContext(ctx[0], extraAttributes);
2490         EXPECT_NE(EGL_NO_CONTEXT, ctx[1]);
2491 
2492         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2493         EXPECT_EGL_SUCCESS();
2494 
2495         glViewport(0, 0, kTexSize, kTexSize);
2496 
2497         ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2498         glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2499         ASSERT_GL_NO_ERROR();
2500 
2501         // Should draw test color.
2502         glUseProgram(textureProgram);
2503         glBindTexture(GL_TEXTURE_2D, texture);
2504         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2505 
2506         // Check test color in four corners.
2507         GLColor color = GLColor::white;
2508         EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2509         EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2510         EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2511         EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2512 
2513         glDeleteSync(thread0DrawSyncObj);
2514         ASSERT_GL_NO_ERROR();
2515         thread0DrawSyncObj = nullptr;
2516 
2517         threadSynchronization.nextStep(Step::Finish);
2518 
2519         EXPECT_GL_NO_ERROR();
2520         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2521         EXPECT_EGL_SUCCESS();
2522     });
2523 
2524     thread0.join();
2525     thread1.join();
2526 
2527     ASSERT_NE(currentStep, Step::Abort);
2528 
2529     // Clean up
2530     for (size_t t = 0; t < kThreadCount; ++t)
2531     {
2532         eglDestroySurface(dpy, surface[t]);
2533         eglDestroyContext(dpy, ctx[t]);
2534     }
2535 }
2536 
2537 // Tests that Context with High Priority will correctly sample EGLImage target Texture rendered by
2538 // other Context with Low Priority into EGLImage source texture.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriorityUsingEGLImage)2539 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriorityUsingEGLImage)
2540 {
2541     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2542     ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
2543     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_EGL_image"));
2544     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2545 
2546     const bool reduceLoad       = isSwiftshader();
2547     const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2548 
2549     // Initialize contexts
2550     EGLWindow *window = getEGLWindow();
2551     EGLDisplay dpy    = window->getDisplay();
2552     EGLConfig config  = window->getConfig();
2553 
2554     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image_base"));
2555     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
2556 
2557     // Large enough texture to catch timing problems.
2558     constexpr GLsizei kTexSize       = 1024;
2559     constexpr size_t kThreadCount    = 2;
2560     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2561     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2562 
2563     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2564 
2565     EGLint pbufferAttributes[kThreadCount][6] = {
2566         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2567         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2568 
2569     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2570                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2571     EGLint *extraAttributes = attributes;
2572     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2573     {
2574         attributes[2] = EGL_NONE;
2575     }
2576     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2577     {
2578         // Run tests with single priority anyway.
2579         extraAttributes += 2;
2580     }
2581 
2582     for (size_t t = 0; t < kThreadCount; ++t)
2583     {
2584         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2585         EXPECT_EGL_SUCCESS();
2586 
2587         attributes[1] = priorities[t];
2588         attributes[3] = mVirtualizationGroup++;
2589 
2590         // Contexts not shared
2591         ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2592         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2593     }
2594 
2595     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2596     std::mutex mutex;
2597     std::condition_variable condVar;
2598 
2599     enum class Step
2600     {
2601         Start,
2602         Thread1Init,
2603         Thread0Draw,
2604         Thread1Draw,
2605         Finish,
2606         Abort,
2607     };
2608     Step currentStep = Step::Start;
2609 
2610     EGLImage image  = EGL_NO_IMAGE_KHR;
2611     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
2612 
2613     // Render to the EGLImage source texture.
2614     std::thread thread0 = std::thread([&]() {
2615         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2616 
2617         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2618         EXPECT_EGL_SUCCESS();
2619 
2620         // Create source texture.
2621         GLTexture texture;
2622         glBindTexture(GL_TEXTURE_2D, texture);
2623         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2624                      nullptr);
2625         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2626         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2627 
2628         GLFramebuffer fbo;
2629         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2630         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2631         glViewport(0, 0, kTexSize, kTexSize);
2632 
2633         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2634         ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2635         ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2636 
2637         // Wait for second thread to finish initializing.
2638         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2639 
2640         // Enable additive blend
2641         glEnable(GL_BLEND);
2642         glBlendFunc(GL_ONE, GL_ONE);
2643 
2644         GLuint query;
2645         glGenQueries(1, &query);
2646         glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2647         ASSERT_GL_NO_ERROR();
2648 
2649         // Simulate heavy work...
2650         glUseProgram(redProgram);
2651         for (size_t j = 0; j < heavyDrawCount; ++j)
2652         {
2653             // Draw with Red color.
2654             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2655         }
2656 
2657         // Draw with Green color.
2658         glUseProgram(greenProgram);
2659         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2660 
2661         // This should force "flushToPrimary()"
2662         glEndQuery(GL_TIME_ELAPSED_EXT);
2663         glDeleteQueries(1, &query);
2664         ASSERT_GL_NO_ERROR();
2665 
2666         // Continue draw with Blue color after flush...
2667         glUseProgram(blueProgram);
2668         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2669 
2670         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
2671         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
2672 
2673         // Create EGLImage.
2674         image = eglCreateImageKHR(
2675             dpy, ctx[0], EGL_GL_TEXTURE_2D_KHR,
2676             reinterpret_cast<EGLClientBuffer>(static_cast<uintptr_t>(texture)), nullptr);
2677         ASSERT_EGL_SUCCESS();
2678 
2679         // Notify second thread that draw is finished.
2680         threadSynchronization.nextStep(Step::Thread0Draw);
2681         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2682 
2683         EXPECT_GL_NO_ERROR();
2684         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2685         EXPECT_EGL_SUCCESS();
2686     });
2687 
2688     // Sample texture
2689     std::thread thread1 = std::thread([&]() {
2690         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2691 
2692         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2693         EXPECT_EGL_SUCCESS();
2694 
2695         glViewport(0, 0, kTexSize, kTexSize);
2696 
2697         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2698                          essl1_shaders::fs::Texture2D());
2699         glUseProgram(textureProgram);
2700 
2701         // Create target texture.
2702         GLTexture texture;
2703         glBindTexture(GL_TEXTURE_2D, texture);
2704         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2705         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2706 
2707         // Wait for first thread to draw into the source texture.
2708         threadSynchronization.nextStep(Step::Thread1Init);
2709         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2710 
2711         // Wait for draw to complete
2712         ASSERT_TRUE(eglWaitSyncKHR(dpy, sync, 0));
2713 
2714         // Specify target texture from EGLImage.
2715         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
2716         ASSERT_GL_NO_ERROR();
2717 
2718         // Should draw test color.
2719         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2720         ASSERT_GL_NO_ERROR();
2721 
2722         // Check test color in four corners.
2723         GLColor color = GLColor::white;
2724         EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2725         EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2726         EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2727         EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2728 
2729         EXPECT_EGL_TRUE(eglDestroyImageKHR(dpy, image));
2730         image = EGL_NO_IMAGE_KHR;
2731 
2732         EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
2733         sync = EGL_NO_SYNC_KHR;
2734 
2735         threadSynchronization.nextStep(Step::Finish);
2736 
2737         EXPECT_GL_NO_ERROR();
2738         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2739         EXPECT_EGL_SUCCESS();
2740     });
2741 
2742     thread0.join();
2743     thread1.join();
2744 
2745     ASSERT_NE(currentStep, Step::Abort);
2746 
2747     // Clean up
2748     for (size_t t = 0; t < kThreadCount; ++t)
2749     {
2750         eglDestroySurface(dpy, surface[t]);
2751         eglDestroyContext(dpy, ctx[t]);
2752     }
2753 }
2754 
2755 // Tests mixing commands of Contexts with different Priorities in a single Command Buffers (Vulkan).
TEST_P(MultithreadingTestES3,ContextPriorityMixing)2756 TEST_P(MultithreadingTestES3, ContextPriorityMixing)
2757 {
2758     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2759     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2760 
2761     constexpr size_t kIterationCountMax = 10;
2762 
2763     const bool reduceLoad       = isSwiftshader();
2764     const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2765     const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2766 
2767     // Initialize contexts
2768     EGLWindow *window = getEGLWindow();
2769     EGLDisplay dpy    = window->getDisplay();
2770     EGLConfig config  = window->getConfig();
2771 
2772     // Large enough texture to catch timing problems.
2773     constexpr GLsizei kTexSize       = 1024;
2774     constexpr size_t kThreadCount    = 2;
2775     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2776     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2777 
2778     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2779 
2780     EGLint pbufferAttributes[kThreadCount][6] = {
2781         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2782         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2783 
2784     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2785                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2786     EGLint *extraAttributes = attributes;
2787     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2788     {
2789         attributes[2] = EGL_NONE;
2790     }
2791     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2792     {
2793         // Run tests with single priority anyway.
2794         extraAttributes += 2;
2795     }
2796 
2797     for (size_t t = 0; t < kThreadCount; ++t)
2798     {
2799         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2800         EXPECT_EGL_SUCCESS();
2801 
2802         attributes[1] = priorities[t];
2803         attributes[3] = mVirtualizationGroup++;
2804 
2805         // Contexts not shared
2806         ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2807         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2808     }
2809 
2810     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2811     std::mutex mutex;
2812     std::condition_variable condVar;
2813 
2814     enum class Step
2815     {
2816         Start,
2817         Thread1DrawColor,
2818         Thread0Iterate,
2819         Finish = Thread1DrawColor + kIterationCountMax * 2,
2820         Abort,
2821     };
2822     Step currentStep = Step::Start;
2823 
2824     auto makeStep = [](Step base, size_t i) {
2825         return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2826     };
2827 
2828     // Triggers commands submission.
2829     std::thread thread0 = std::thread([&]() {
2830         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2831 
2832         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2833         EXPECT_EGL_SUCCESS();
2834 
2835         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2836 
2837         for (size_t i = 0; i < iterationCount; ++i)
2838         {
2839             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread1DrawColor, i)));
2840 
2841             glUseProgram(redProgram);
2842             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2843             ASSERT_GL_NO_ERROR();
2844 
2845             // This should perform commands submission.
2846             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2847             EXPECT_EGL_SUCCESS();
2848 
2849             threadSynchronization.nextStep(makeStep(Step::Thread0Iterate, i));
2850 
2851             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2852             EXPECT_EGL_SUCCESS();
2853         }
2854 
2855         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2856 
2857         EXPECT_GL_NO_ERROR();
2858         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2859         EXPECT_EGL_SUCCESS();
2860     });
2861 
2862     // Render and then sample texture.
2863     std::thread thread1 = std::thread([&]() {
2864         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2865 
2866         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2867         EXPECT_EGL_SUCCESS();
2868 
2869         glDisable(GL_DEPTH_TEST);
2870         glViewport(0, 0, kTexSize, kTexSize);
2871 
2872         GLTexture texture;
2873         glBindTexture(GL_TEXTURE_2D, texture);
2874         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2875                      nullptr);
2876         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2877         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2878 
2879         GLFramebuffer fbo;
2880         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2881         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2882 
2883         ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2884                          essl1_shaders::fs::UniformColor());
2885         GLint colorLocation =
2886             glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2887         ASSERT_NE(-1, colorLocation);
2888 
2889         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2890                          essl1_shaders::fs::Texture2D());
2891 
2892         for (size_t i = 0; i < iterationCount; ++i)
2893         {
2894             glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2895             glUseProgram(colorProgram);
2896 
2897             GLuint query;
2898             glGenQueries(1, &query);
2899             glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2900             ASSERT_GL_NO_ERROR();
2901 
2902             // Simulate heavy work...
2903             glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2904             for (size_t j = 0; j < heavyDrawCount; ++j)
2905             {
2906                 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2907             }
2908 
2909             // Draw with test color.
2910             GLColor color(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2911             Vector4 colorF = color.toNormalizedVector();
2912             glUniform4f(colorLocation, colorF.x(), colorF.y(), colorF.z(), colorF.w());
2913             drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2914             ASSERT_GL_NO_ERROR();
2915 
2916             // This should force "flushToPrimary()"
2917             glEndQuery(GL_TIME_ELAPSED_EXT);
2918             glDeleteQueries(1, &query);
2919             ASSERT_GL_NO_ERROR();
2920 
2921             threadSynchronization.nextStep(makeStep(Step::Thread1DrawColor, i));
2922             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Iterate, i)));
2923 
2924             glBindFramebuffer(GL_FRAMEBUFFER, 0);
2925             glUseProgram(textureProgram);
2926 
2927             // Should draw test color.
2928             drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2929             ASSERT_GL_NO_ERROR();
2930 
2931             // Check test color in four corners.
2932             EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2933             EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2934             EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2935             EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2936         }
2937 
2938         threadSynchronization.nextStep(Step::Finish);
2939 
2940         EXPECT_GL_NO_ERROR();
2941         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2942         EXPECT_EGL_SUCCESS();
2943     });
2944 
2945     thread0.join();
2946     thread1.join();
2947 
2948     ASSERT_NE(currentStep, Step::Abort);
2949 
2950     // Clean up
2951     for (size_t t = 0; t < kThreadCount; ++t)
2952     {
2953         eglDestroySurface(dpy, surface[t]);
2954         eglDestroyContext(dpy, ctx[t]);
2955     }
2956 }
2957 
2958 // Test that it is possible to upload textures in one thread and use them in another with
2959 // synchronization.
TEST_P(MultithreadingTestES3,MultithreadedTextureUploadAndDraw)2960 TEST_P(MultithreadingTestES3, MultithreadedTextureUploadAndDraw)
2961 {
2962     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2963 
2964     constexpr size_t kTexSize = 4;
2965     GLTexture texture1;
2966     GLTexture texture2;
2967     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
2968     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
2969 
2970     // Sync primitives
2971     GLsync sync = nullptr;
2972     std::mutex mutex;
2973     std::condition_variable condVar;
2974 
2975     enum class Step
2976     {
2977         Start,
2978         Thread0UploadFinish,
2979         Finish,
2980         Abort,
2981     };
2982     Step currentStep = Step::Start;
2983 
2984     // Threads to upload and draw with textures.
2985     auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2986         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2987         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2988 
2989         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2990 
2991         // Two mipmap textures are defined here. They are used for drawing in the other thread.
2992         glBindTexture(GL_TEXTURE_2D, texture1);
2993         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2994                      textureColors1.data());
2995         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
2996                      GL_UNSIGNED_BYTE, textureColors1.data());
2997         glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
2998                      GL_UNSIGNED_BYTE, textureColors1.data());
2999         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3000         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3001         ASSERT_GL_NO_ERROR();
3002 
3003         glBindTexture(GL_TEXTURE_2D, texture2);
3004         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3005                      textureColors2.data());
3006         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3007                      GL_UNSIGNED_BYTE, textureColors2.data());
3008         glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
3009                      GL_UNSIGNED_BYTE, textureColors2.data());
3010         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3011         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3012         ASSERT_GL_NO_ERROR();
3013 
3014         // Create a sync object to be used for the draw thread.
3015         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3016         glFlush();
3017         ASSERT_NE(sync, nullptr);
3018 
3019         threadSynchronization.nextStep(Step::Thread0UploadFinish);
3020         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3021     };
3022 
3023     auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3024         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3025         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0UploadFinish));
3026 
3027         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3028 
3029         // Wait for the sync object to be signaled.
3030         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3031         ASSERT_GL_NO_ERROR();
3032 
3033         // Draw using the textures from the texture upload thread.
3034         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
3035                          essl1_shaders::fs::Texture2D());
3036         glUseProgram(textureProgram);
3037 
3038         glBindTexture(GL_TEXTURE_2D, texture1);
3039         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3040         glFlush();
3041         ASSERT_GL_NO_ERROR();
3042         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3043 
3044         glBindTexture(GL_TEXTURE_2D, texture2);
3045         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3046         glFlush();
3047         ASSERT_GL_NO_ERROR();
3048         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3049 
3050         threadSynchronization.nextStep(Step::Finish);
3051     };
3052 
3053     std::array<LockStepThreadFunc, 2> threadFuncs = {
3054         std::move(thread0Upload),
3055         std::move(thread1Draw),
3056     };
3057 
3058     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3059 
3060     ASSERT_NE(currentStep, Step::Abort);
3061 }
3062 
3063 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3064 // previous context, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnNewThread)3065 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnNewThread)
3066 {
3067     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3068 
3069     constexpr size_t kTexSize = 4;
3070     GLTexture texture1;
3071     GLTexture texture2;
3072     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3073     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3074 
3075     EGLWindow *window = getEGLWindow();
3076     EGLDisplay dpy    = window->getDisplay();
3077 
3078     std::thread thread = std::thread([&]() {
3079         // Create a context and upload the textures.
3080         EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
3081         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3082 
3083         glBindTexture(GL_TEXTURE_2D, texture1);
3084         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3085                      textureColors1.data());
3086         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3087                      GL_UNSIGNED_BYTE, textureColors1.data());
3088         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3089         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3090         ASSERT_GL_NO_ERROR();
3091 
3092         glBindTexture(GL_TEXTURE_2D, texture2);
3093         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3094                      textureColors2.data());
3095         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3096                      GL_UNSIGNED_BYTE, textureColors2.data());
3097         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3098         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3099         ASSERT_GL_NO_ERROR();
3100 
3101         // Create a new context and use the uploaded textures.
3102         EGLContext ctx2 = createMultithreadedContext(window, ctx1);
3103         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
3104 
3105         GLFramebuffer fbo;
3106         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3107 
3108         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3109         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3110 
3111         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3112         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3113 
3114         // Destroy the contexts.
3115         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
3116         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3117     });
3118 
3119     thread.join();
3120 }
3121 
3122 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3123 // main thread, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnMainThread)3124 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnMainThread)
3125 {
3126     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3127 
3128     EGLWindow *window = getEGLWindow();
3129     EGLDisplay dpy    = window->getDisplay();
3130 
3131     // Upload the textures.
3132     constexpr size_t kTexSize = 4;
3133     GLTexture texture1;
3134     GLTexture texture2;
3135     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3136     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3137 
3138     glBindTexture(GL_TEXTURE_2D, texture1);
3139     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3140                  textureColors1.data());
3141     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3142                  GL_UNSIGNED_BYTE, textureColors1.data());
3143     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3144     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3145     ASSERT_GL_NO_ERROR();
3146 
3147     glBindTexture(GL_TEXTURE_2D, texture2);
3148     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3149                  textureColors2.data());
3150     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3151                  GL_UNSIGNED_BYTE, textureColors2.data());
3152     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3153     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3154     ASSERT_GL_NO_ERROR();
3155 
3156     GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3157 
3158     std::thread thread = std::thread([&]() {
3159         // Create a context.
3160         EGLContext ctx1 = createMultithreadedContext(window, window->getContext());
3161         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3162 
3163         // Wait for the sync object to be signaled.
3164         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3165         ASSERT_GL_NO_ERROR();
3166 
3167         // Use the uploaded textures in the main thread.
3168         GLFramebuffer fbo;
3169         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3170 
3171         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3172         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3173 
3174         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3175         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3176 
3177         // Destroy the context.
3178         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3179     });
3180 
3181     thread.join();
3182 }
3183 
3184 // Test when lots of upload happens on a different thread at the same time as the main thread doing
3185 // draws.
TEST_P(MultithreadingTestES3,SimultaneousUploadAndDraw)3186 TEST_P(MultithreadingTestES3, SimultaneousUploadAndDraw)
3187 {
3188     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3189 
3190     // The following shader is used to create busy work while the worker thread is doing something.
3191     // It intentionally spreads its uniforms and inputs so the main thread has to make many GL
3192     // calls.
3193     constexpr char kBusyDrawVS[] = R"(#version 300 es
3194 uniform mediump float x0;
3195 uniform mediump float y0;
3196 uniform mediump float x1;
3197 uniform mediump float y1;
3198 
3199 in mediump float r;
3200 in mediump float g;
3201 in mediump float b;
3202 in mediump float a;
3203 
3204 out mediump vec4 color;
3205 
3206 void main()
3207 {
3208     // gl_VertexID    x    y
3209     //      0        -1   -1
3210     //      1         1   -1
3211     //      2        -1    1
3212     //      3         1    1
3213     int bit0 = gl_VertexID & 1;
3214     int bit1 = gl_VertexID >> 1;
3215     gl_Position.x = bit0 == 0 ? x0 : x1;
3216     gl_Position.y = bit1 == 0 ? y0 : y1;
3217     gl_Position.z = 0.;
3218     gl_Position.w = 1.;
3219 
3220     color = vec4(r, g, b, a);
3221 })";
3222     constexpr char kBusyDrawFS[] = R"(#version 300 es
3223 
3224 in mediump vec4 color;
3225 out mediump vec4 colorOut;
3226 
3227 void main()
3228 {
3229     colorOut = color;
3230 })";
3231 
3232     // The following shader is used to consume the results of texture uploads, ensuring appropriate
3233     // synchronization.
3234     constexpr char kTextureDrawVS[] = R"(#version 300 es
3235 out mediump vec2 uv;
3236 
3237 void main()
3238 {
3239     // gl_VertexID    x    y
3240     //      0        -1   -1
3241     //      1         1   -1
3242     //      2        -1    1
3243     //      3         1    1
3244     int bit0 = gl_VertexID & 1;
3245     int bit1 = gl_VertexID >> 1;
3246     gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1);
3247     uv = vec2(bit0, bit1);
3248 })";
3249     constexpr char kTextureDrawFS[] = R"(#version 300 es
3250 
3251 uniform mediump sampler2D s0;
3252 uniform mediump sampler2D s1;
3253 uniform mediump sampler2D s2;
3254 uniform mediump sampler2D s3;
3255 uniform mediump sampler2D s4;
3256 uniform mediump sampler2D s5;
3257 uniform mediump sampler2D s6;
3258 uniform mediump sampler2D s7;
3259 uniform mediump sampler2D s8;
3260 uniform mediump sampler2D s9;
3261 
3262 in mediump vec2 uv;
3263 out mediump vec4 colorOut;
3264 
3265 void main()
3266 {
3267     highp vec4 result = texture(s0, uv) +
3268                         texture(s1, uv) +
3269                         texture(s2, uv) +
3270                         texture(s3, uv) +
3271                         texture(s4, uv) +
3272                         texture(s5, uv) +
3273                         texture(s6, uv) +
3274                         texture(s7, uv) +
3275                         texture(s8, uv) +
3276                         texture(s9, uv);
3277     result /= 10.;
3278 
3279     colorOut = result;
3280 })";
3281 
3282     constexpr uint32_t kTextureCount = 10;
3283     GLuint textures[kTextureCount];
3284 
3285     ASSERT(IsGLExtensionEnabled("GL_KHR_texture_compression_astc_ldr") ||
3286            IsGLExtensionEnabled("GL_EXT_texture_compression_bptc"));
3287     // Note ASTC may be emulated in ANGLE, so check for BPTC first
3288     const bool hasBPTC = IsGLExtensionEnabled("GL_EXT_texture_compression_bptc");
3289     const GLenum compressedFormat =
3290         hasBPTC ? GL_COMPRESSED_RGBA_BPTC_UNORM_EXT : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
3291 
3292     std::vector<uint8_t> textureData[kTextureCount];
3293 
3294     constexpr int kSurfaceWidth  = 256;
3295     constexpr int kSurfaceHeight = 512;
3296     constexpr int kTexSize       = 1024;
3297 
3298     // Sync primitives
3299     GLsync sync = nullptr;
3300     std::mutex mutex;
3301     std::condition_variable condVar;
3302 
3303     enum class Step
3304     {
3305         Start,
3306         Thread1Ready,
3307         Thread0UploadFinish,
3308         Finish,
3309         Abort,
3310     };
3311     Step currentStep = Step::Start;
3312 
3313     // Threads to upload and draw with textures.
3314     auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3315         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3316         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3317 
3318         // Wait for the other thread to set everything up
3319         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3320 
3321         // Perform uploads while the other thread does draws
3322         for (uint32_t i = 0; i < kTextureCount; ++i)
3323         {
3324             glBindTexture(GL_TEXTURE_2D, textures[i]);
3325             glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTexSize, kTexSize, compressedFormat,
3326                                       static_cast<GLsizei>(textureData[i].size()),
3327                                       textureData[i].data());
3328         }
3329         ASSERT_GL_NO_ERROR();
3330 
3331         // Create a sync object to be used for the draw thread.
3332         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3333         ASSERT_NE(sync, nullptr);
3334         ASSERT_GL_NO_ERROR();
3335 
3336         threadSynchronization.nextStep(Step::Thread0UploadFinish);
3337         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3338     };
3339 
3340     auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3341         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3342         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3343 
3344         ANGLE_GL_PROGRAM(busyDrawProgram, kBusyDrawVS, kBusyDrawFS);
3345 
3346         // Set up the test.  Don't let the other thread work yet.
3347         glUseProgram(busyDrawProgram);
3348         GLuint busyDrawX0Loc = glGetUniformLocation(busyDrawProgram, "x0");
3349         GLuint busyDrawY0Loc = glGetUniformLocation(busyDrawProgram, "y0");
3350         GLuint busyDrawX1Loc = glGetUniformLocation(busyDrawProgram, "x1");
3351         GLuint busyDrawY1Loc = glGetUniformLocation(busyDrawProgram, "y1");
3352         GLuint busyDrawRLoc  = glGetAttribLocation(busyDrawProgram, "r");
3353         GLuint busyDrawGLoc  = glGetAttribLocation(busyDrawProgram, "g");
3354         GLuint busyDrawBLoc  = glGetAttribLocation(busyDrawProgram, "b");
3355         GLuint busyDrawALoc  = glGetAttribLocation(busyDrawProgram, "a");
3356 
3357         ANGLE_GL_PROGRAM(textureDrawProgram, kTextureDrawVS, kTextureDrawFS);
3358         GLuint textureDrawSamplerLoc[kTextureCount] = {};
3359 
3360         glUseProgram(textureDrawProgram);
3361         glGenTextures(kTextureCount, textures);
3362         for (uint32_t i = 0; i < kTextureCount; ++i)
3363         {
3364             std::ostringstream name;
3365             name << "s" << i;
3366 
3367             textureDrawSamplerLoc[i] = glGetUniformLocation(textureDrawProgram, name.str().c_str());
3368 
3369             glBindTexture(GL_TEXTURE_2D, textures[i]);
3370             glTexStorage2D(GL_TEXTURE_2D, 1, compressedFormat, kTexSize, kTexSize);
3371 
3372             // Both ASTC 4x4 and BPTC have 1 byte per pixel.  The textures' contents are arbitrary
3373             // but distinct.
3374             textureData[i].resize(kTexSize * kTexSize);
3375             for (int y = 0; y < kTexSize; ++y)
3376             {
3377                 for (int x = 0; x < kTexSize; ++x)
3378                 {
3379                     textureData[i][y * kTexSize + x] = (i * 50 + y + x) % 255;
3380                 }
3381             }
3382         }
3383         ASSERT_GL_NO_ERROR();
3384 
3385         // Now that everything is set up, let the upload thread work while this thread does draws.
3386         threadSynchronization.nextStep(Step::Thread1Ready);
3387 
3388         int w = kSurfaceWidth;
3389         int h = kSurfaceHeight;
3390 
3391         glClear(GL_COLOR_BUFFER_BIT);
3392         glViewport(0, 0, w, h);
3393         glUseProgram(busyDrawProgram);
3394         for (uint32_t y = 0; y < 8; ++y)
3395         {
3396             for (uint32_t x = 0; x < 8; ++x)
3397             {
3398                 float width  = w / 4;
3399                 float height = h / 8;
3400 
3401                 glUniform1f(busyDrawX0Loc, x * width / w - 1);
3402                 glUniform1f(busyDrawY0Loc, y * height / h);
3403                 glUniform1f(busyDrawX1Loc, (x + 1) * width / w - 1);
3404                 glUniform1f(busyDrawY1Loc, (y + 1) * height / h);
3405 
3406                 glVertexAttrib1f(busyDrawRLoc, x / 8.0f);
3407                 glVertexAttrib1f(busyDrawGLoc, y / 8.0f);
3408                 glVertexAttrib1f(busyDrawBLoc, 0);
3409                 glVertexAttrib1f(busyDrawALoc, 1);
3410 
3411                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3412             }
3413         }
3414         ASSERT_GL_NO_ERROR();
3415 
3416         // Wait for the other thread to finish with uploads.
3417         threadSynchronization.waitForStep(Step::Thread0UploadFinish);
3418 
3419         // Wait for fence and use all textures in a draw.
3420         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3421 
3422         glUseProgram(textureDrawProgram);
3423         for (uint32_t i = 0; i < kTextureCount; ++i)
3424         {
3425             glActiveTexture(GL_TEXTURE0 + i);
3426             glBindTexture(GL_TEXTURE_2D, textures[i]);
3427             glUniform1i(textureDrawSamplerLoc[i], i);
3428         }
3429         glViewport(0, 0, w, h / 2);
3430         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3431         ASSERT_GL_NO_ERROR();
3432 
3433         threadSynchronization.nextStep(Step::Finish);
3434 
3435         // Verify results
3436         for (uint32_t y = 0; y < 8; ++y)
3437         {
3438             for (uint32_t x = 0; x < 8; ++x)
3439             {
3440                 int width  = w / 8;
3441                 int height = h / 16;
3442 
3443                 EXPECT_PIXEL_COLOR_NEAR(x * width + width / 2, h - (y * height + height / 2),
3444                                         GLColor(x * 255 / 8, (7 - y) * 255 / 8, 0, 255), 1);
3445             }
3446         }
3447         ASSERT_GL_NO_ERROR();
3448 
3449         for (uint32_t x = 0; x < 8; ++x)
3450         {
3451             // The compressed data is gibberish, just ensure it's not black.
3452             EXPECT_PIXEL_COLOR_NEAR(x * w / 8, h / 4, GLColor(128, 128, 128, 128), 127);
3453         }
3454         ASSERT_GL_NO_ERROR();
3455     };
3456 
3457     std::array<LockStepThreadFunc, 2> threadFuncs = {
3458         std::move(thread0Upload),
3459         std::move(thread1Draw),
3460     };
3461 
3462     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
3463                                threadFuncs.data());
3464 
3465     ASSERT_NE(currentStep, Step::Abort);
3466 }
3467 
3468 // Test that calling glUniformBlockBinding on one context affects all contexts.
TEST_P(MultithreadingTestES3,UniformBlockBinding)3469 TEST_P(MultithreadingTestES3, UniformBlockBinding)
3470 {
3471     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3472 
3473     constexpr char kVS[] = R"(#version 300 es
3474 void main()
3475 {
3476     vec2 pos = vec2(0.0);
3477     switch (gl_VertexID) {
3478         case 0: pos = vec2(-1.0, -1.0); break;
3479         case 1: pos = vec2(3.0, -1.0); break;
3480         case 2: pos = vec2(-1.0, 3.0); break;
3481     };
3482     gl_Position = vec4(pos, 0.0, 1.0);
3483 })";
3484     constexpr char kFS[] = R"(#version 300 es
3485 out mediump vec4 colorOut;
3486 
3487 layout(std140) uniform buffer { mediump vec4 color; };
3488 
3489 void main()
3490 {
3491     colorOut = color;
3492 })";
3493 
3494     GLProgram program;
3495     GLint uniformBufferIndex;
3496 
3497     // Sync primitives
3498     std::mutex mutex;
3499     std::condition_variable condVar;
3500 
3501     enum class Step
3502     {
3503         Start,
3504         Thread1Ready,
3505         Thread0BindingChanged,
3506         Thread1FinishedDrawing,
3507         Finish,
3508         Abort,
3509     };
3510     Step currentStep = Step::Start;
3511 
3512     // Threads to create programs and draw with different uniform blocks.
3513     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3514         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3515         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3516 
3517         // Create buffers bound to bindings 1 and 2
3518         constexpr std::array<float, 4> kRed              = {1, 0, 0, 1};
3519         constexpr std::array<float, 4> kTransparentGreen = {0, 1, 0, 0};
3520         GLBuffer red, transparentGreen;
3521         glBindBuffer(GL_UNIFORM_BUFFER, red);
3522         glBufferData(GL_UNIFORM_BUFFER, sizeof(kRed), kRed.data(), GL_STATIC_DRAW);
3523         glBindBuffer(GL_UNIFORM_BUFFER, transparentGreen);
3524         glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentGreen), kTransparentGreen.data(),
3525                      GL_STATIC_DRAW);
3526 
3527         glBindBufferBase(GL_UNIFORM_BUFFER, 1, transparentGreen);
3528         glBindBufferBase(GL_UNIFORM_BUFFER, 2, red);
3529 
3530         // Wait for the other thread to set everything up
3531         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3532 
3533         // Issue a draw call.  The buffer should be transparent green now
3534         glClearColor(0, 0, 0, 0);
3535         glClear(GL_COLOR_BUFFER_BIT);
3536         glEnable(GL_BLEND);
3537         glBlendFunc(GL_ONE, GL_ONE);
3538 
3539         glUseProgram(program);
3540         glDrawArrays(GL_TRIANGLES, 0, 3);
3541 
3542         // Change the binding
3543         glUniformBlockBinding(program, uniformBufferIndex, 1);
3544         ASSERT_GL_NO_ERROR();
3545 
3546         // Let the other thread work before any deferred operations for the binding change above are
3547         // processed in this context.
3548         threadSynchronization.nextStep(Step::Thread0BindingChanged);
3549         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3550 
3551         // Draw again, it should accumulate blue and the buffer should become magenta.
3552         glDrawArrays(GL_TRIANGLES, 0, 3);
3553 
3554         // Verify results
3555         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
3556         ASSERT_GL_NO_ERROR();
3557 
3558         threadSynchronization.nextStep(Step::Finish);
3559     };
3560 
3561     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3562         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3563         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3564 
3565         // Create buffers bound to bindings 1 and 2
3566         constexpr std::array<float, 4> kBlue           = {0, 0, 1, 1};
3567         constexpr std::array<float, 4> kTransparentRed = {1, 0, 0, 0};
3568         GLBuffer blue, transparentRed;
3569         glBindBuffer(GL_UNIFORM_BUFFER, blue);
3570         glBufferData(GL_UNIFORM_BUFFER, sizeof(kBlue), kBlue.data(), GL_STATIC_DRAW);
3571         glBindBuffer(GL_UNIFORM_BUFFER, transparentRed);
3572         glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentRed), kTransparentRed.data(),
3573                      GL_STATIC_DRAW);
3574 
3575         glBindBufferBase(GL_UNIFORM_BUFFER, 1, blue);
3576         glBindBufferBase(GL_UNIFORM_BUFFER, 2, transparentRed);
3577 
3578         // Create the program
3579         program.makeRaster(kVS, kFS);
3580         glUseProgram(program);
3581         uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
3582 
3583         // Configure the buffer binding to binding 2
3584         glUniformBlockBinding(program, uniformBufferIndex, 2);
3585         ASSERT_GL_NO_ERROR();
3586 
3587         // Issue a draw call.  The buffer should be transparent red now
3588         glClearColor(0, 0, 0, 0);
3589         glClear(GL_COLOR_BUFFER_BIT);
3590         glEnable(GL_BLEND);
3591         glBlendFunc(GL_ONE, GL_ONE);
3592 
3593         glDrawArrays(GL_TRIANGLES, 0, 3);
3594 
3595         // Now that everything is set up, let the other thread continue
3596         threadSynchronization.nextStep(Step::Thread1Ready);
3597         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0BindingChanged));
3598 
3599         // The other thread has changed the binding.  Draw again, it should accumulate blue and the
3600         // buffer should become magenta.
3601         glDrawArrays(GL_TRIANGLES, 0, 3);
3602 
3603         // Verify results
3604         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
3605         ASSERT_GL_NO_ERROR();
3606 
3607         // Tell the other thread to finish up.
3608         threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3609         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3610     };
3611 
3612     std::array<LockStepThreadFunc, 2> threadFuncs = {
3613         std::move(thread0),
3614         std::move(thread1),
3615     };
3616 
3617     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3618 
3619     ASSERT_NE(currentStep, Step::Abort);
3620 }
3621 
3622 // Test that observers are notified of a change in foveation state of a texture
TEST_P(MultithreadingTestES3,SharedFoveatedTexture)3623 TEST_P(MultithreadingTestES3, SharedFoveatedTexture)
3624 {
3625     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3626     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_QCOM_texture_foveated"));
3627 
3628     // Shared texture
3629     GLTexture texture;
3630 
3631     // Sync primitives
3632     std::mutex mutex;
3633     std::condition_variable condVar;
3634 
3635     enum class Step
3636     {
3637         Start,
3638         Thread0Draw,
3639         Thread1Draw,
3640         Thread0ConfiguredTextureFoveation,
3641         Finish,
3642         Abort,
3643     };
3644     Step currentStep = Step::Start;
3645 
3646     // Thread to configure texture foveation.
3647     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3648         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3649         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3650 
3651         // Create non-foveated framebuffer and attach shared texture as color attachment
3652         GLFramebuffer framebuffer;
3653         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3654         glActiveTexture(GL_TEXTURE0);
3655         glBindTexture(GL_TEXTURE_2D, texture);
3656         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
3657                      GL_UNSIGNED_BYTE, nullptr);
3658         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3659         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3660         EXPECT_GL_NO_ERROR();
3661         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3662         ASSERT_GL_NO_ERROR();
3663         EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3664 
3665         // Render before configuring foveation on the texture
3666         ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
3667         glUseProgram(greenProgram);
3668 
3669         // Draw
3670         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
3671         EXPECT_GL_NO_ERROR();
3672 
3673         // Verify results
3674         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
3675         ASSERT_GL_NO_ERROR();
3676 
3677         threadSynchronization.nextStep(Step::Thread0Draw);
3678         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
3679 
3680         // Configure foveation for the texture
3681         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM,
3682                         GL_FOVEATION_ENABLE_BIT_QCOM);
3683         EXPECT_GL_NO_ERROR();
3684         glTextureFoveationParametersQCOM(texture, 0, 0, 0.0f, 0.0f, 8.0f, 8.0f, 0.0f);
3685         EXPECT_GL_NO_ERROR();
3686 
3687         threadSynchronization.nextStep(Step::Thread0ConfiguredTextureFoveation);
3688         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3689     };
3690 
3691     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3692         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3693         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3694 
3695         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
3696 
3697         // Create non-foveated framebuffer and attach shared texture as color attachment
3698         GLFramebuffer framebuffer;
3699         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3700         glActiveTexture(GL_TEXTURE0);
3701         glBindTexture(GL_TEXTURE_2D, texture);
3702         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3703         ASSERT_GL_NO_ERROR();
3704         EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3705 
3706         // Render before configuring foveation on the texture
3707         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
3708         glUseProgram(redProgram);
3709 
3710         // Draw
3711         drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
3712         EXPECT_GL_NO_ERROR();
3713 
3714         // Verify results
3715         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3716         ASSERT_GL_NO_ERROR();
3717 
3718         threadSynchronization.nextStep(Step::Thread1Draw);
3719         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ConfiguredTextureFoveation));
3720 
3721         // Render after texture foveation was configured
3722         ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
3723         glUseProgram(blueProgram);
3724 
3725         // Draw
3726         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
3727         EXPECT_GL_NO_ERROR();
3728 
3729         // Verify results
3730         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
3731         ASSERT_GL_NO_ERROR();
3732 
3733         threadSynchronization.nextStep(Step::Finish);
3734     };
3735 
3736     std::array<LockStepThreadFunc, 2> threadFuncs = {
3737         std::move(thread0),
3738         std::move(thread1),
3739     };
3740 
3741     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3742 
3743     ASSERT_NE(currentStep, Step::Abort);
3744 }
3745 
3746 // Test that a program linked in one context can be bound in another context while link may be
3747 // happening in parallel.
TEST_P(MultithreadingTest,ProgramLinkAndBind)3748 TEST_P(MultithreadingTest, ProgramLinkAndBind)
3749 {
3750     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3751 
3752     GLuint vs;
3753     GLuint redfs;
3754     GLuint greenfs;
3755 
3756     GLuint program;
3757 
3758     // Sync primitives
3759     std::mutex mutex;
3760     std::condition_variable condVar;
3761 
3762     enum class Step
3763     {
3764         Start,
3765         Thread1Ready,
3766         Thread0ProgramLinked,
3767         Thread1FinishedDrawing,
3768         Finish,
3769         Abort,
3770     };
3771     Step currentStep = Step::Start;
3772 
3773     // Threads to create programs and draw.
3774     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3775         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3776         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3777 
3778         // Wait for thread 1 to bind the program before linking it
3779         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3780 
3781         glUseProgram(program);
3782 
3783         // Link a program, but don't resolve link.
3784         glDetachShader(program, greenfs);
3785         glAttachShader(program, redfs);
3786         glLinkProgram(program);
3787 
3788         // Let the other thread bind and use the program.
3789         threadSynchronization.nextStep(Step::Thread0ProgramLinked);
3790         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3791 
3792         // Draw in this context too
3793         drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3794 
3795         // Verify results
3796         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3797         ASSERT_GL_NO_ERROR();
3798 
3799         threadSynchronization.nextStep(Step::Finish);
3800     };
3801 
3802     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3803         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3804         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3805 
3806         vs      = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
3807         redfs   = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Red());
3808         greenfs = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Green());
3809         program = glCreateProgram();
3810 
3811         glAttachShader(program, vs);
3812         glAttachShader(program, greenfs);
3813         glLinkProgram(program);
3814         ASSERT_NE(CheckLinkStatusAndReturnProgram(program, true), 0u);
3815 
3816         // Bind the program before it's relinked.  Otherwise the program is resolved before the
3817         // binding happens.
3818         glUseProgram(program);
3819 
3820         threadSynchronization.nextStep(Step::Thread1Ready);
3821         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ProgramLinked));
3822 
3823         // Unbind and rebind for extra testing
3824         glUseProgram(0);
3825         glUseProgram(program);
3826 
3827         // Issue a draw call
3828         drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3829 
3830         // Verify results
3831         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3832         ASSERT_GL_NO_ERROR();
3833 
3834         // Tell the other thread to finish up.
3835         threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3836         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3837     };
3838 
3839     std::array<LockStepThreadFunc, 2> threadFuncs = {
3840         std::move(thread0),
3841         std::move(thread1),
3842     };
3843 
3844     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3845 
3846     ASSERT_NE(currentStep, Step::Abort);
3847 }
3848 
3849 // Test that two contexts in share group can generate, delete and bind buffers for themselves in
3850 // parallel.
TEST_P(MultithreadingTestES3,SimultaneousBufferBindAndGen)3851 TEST_P(MultithreadingTestES3, SimultaneousBufferBindAndGen)
3852 {
3853     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3854 
3855     constexpr char kFS[] = R"(#version 300 es
3856 precision mediump float;
3857 
3858 layout(std140) uniform Block
3859 {
3860     vec4 colorIn;
3861 };
3862 
3863 out vec4 color;
3864 
3865 void main()
3866 {
3867     color = colorIn;
3868 })";
3869 
3870     constexpr int kSurfaceWidth  = 32;
3871     constexpr int kSurfaceHeight = 128;
3872 
3873     // Sync primitives
3874     std::mutex mutex;
3875     std::condition_variable condVar;
3876 
3877     enum class Step
3878     {
3879         Start,
3880         Thread0Ready,
3881         Thread1Ready,
3882         Finish,
3883         Abort,
3884     };
3885     Step currentStep = Step::Start;
3886 
3887     auto threadFunc = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context, uint32_t index) {
3888         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3889         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3890 
3891         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
3892 
3893         // Make sure the two threads start work around the same time
3894         if (index == 0)
3895         {
3896             threadSynchronization.nextStep(Step::Thread0Ready);
3897             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3898         }
3899         else
3900         {
3901             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
3902             threadSynchronization.nextStep(Step::Thread1Ready);
3903         }
3904 
3905         std::vector<GLuint> buffers(kSurfaceWidth * kSurfaceHeight);
3906 
3907         glEnable(GL_SCISSOR_TEST);
3908         for (int y = 0; y < kSurfaceHeight; ++y)
3909         {
3910             for (int x = 0; x < kSurfaceWidth; ++x)
3911             {
3912                 GLuint &buffer            = buffers[y * kSurfaceWidth + x];
3913                 const float bufferData[4] = {
3914                     ((y * kSurfaceWidth + x + index * 100) % 255) / 255.0f,
3915                     ((y * kSurfaceWidth + x + index * 100 + 1) % 255) / 255.0f,
3916                     ((y * kSurfaceWidth + x + index * 100 + 2) % 255) / 255.0f,
3917                     ((y * kSurfaceWidth + x + index * 100 + 3) % 255) / 255.0f,
3918                 };
3919 
3920                 // Generate one buffer per pixel and shade the pixel with it.
3921                 glGenBuffers(1, &buffer);
3922                 glBindBuffer(GL_UNIFORM_BUFFER, buffers[y * kSurfaceWidth + x]);
3923                 glBufferData(GL_UNIFORM_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
3924                 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
3925 
3926                 glScissor(x, y, 1, 1);
3927                 drawQuad(program, essl3_shaders::PositionAttrib(), 0);
3928 
3929                 if ((x + y) % 2 == 0)
3930                 {
3931                     glDeleteBuffers(1, &buffer);
3932                     buffer = 0;
3933                 }
3934             }
3935         }
3936 
3937         // Verify the results
3938         auto verify = [&](int x, int y) {
3939             const GLColor expect((y * kSurfaceWidth + x + index * 100) % 255,
3940                                  (y * kSurfaceWidth + x + index * 100 + 1) % 255,
3941                                  (y * kSurfaceWidth + x + index * 100 + 2) % 255,
3942                                  (y * kSurfaceWidth + x + index * 100 + 3) % 255);
3943             EXPECT_PIXEL_COLOR_EQ(x, y, expect);
3944         };
3945 
3946         verify(0, 0);
3947         verify(0, kSurfaceHeight - 1);
3948         verify(kSurfaceWidth - 1, 0);
3949         verify(kSurfaceWidth - 1, kSurfaceHeight - 1);
3950         verify(kSurfaceWidth / 2, kSurfaceHeight / 2);
3951         ASSERT_GL_NO_ERROR();
3952 
3953         if (index == 0)
3954         {
3955             threadSynchronization.nextStep(Step::Finish);
3956         }
3957         else
3958         {
3959             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3960         }
3961     };
3962 
3963     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3964         threadFunc(dpy, surface, context, 0);
3965     };
3966     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3967         threadFunc(dpy, surface, context, 1);
3968     };
3969 
3970     std::array<LockStepThreadFunc, 2> threadFuncs = {
3971         std::move(thread0),
3972         std::move(thread1),
3973     };
3974 
3975     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
3976                                threadFuncs.data());
3977 
3978     ASSERT_NE(currentStep, Step::Abort);
3979 }
3980 
3981 // Test that ref counting is thread-safe when the same buffer is used in multiple threads.
TEST_P(MultithreadingTestES3,SimultaneousBufferBind)3982 TEST_P(MultithreadingTestES3, SimultaneousBufferBind)
3983 {
3984     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3985 
3986     constexpr char kFS[] = R"(#version 300 es
3987 precision mediump float;
3988 
3989 layout(std140) uniform Block
3990 {
3991     vec4 colorIn;
3992 };
3993 
3994 out vec4 color;
3995 
3996 void main()
3997 {
3998     color = colorIn;
3999 })";
4000 
4001     constexpr int kSurfaceWidth  = 32;
4002     constexpr int kSurfaceHeight = 128;
4003 
4004     GLuint buffer;
4005     GLsync sync = nullptr;
4006 
4007     // Sync primitives
4008     std::mutex mutex;
4009     std::condition_variable condVar;
4010 
4011     enum class Step
4012     {
4013         Start,
4014         Thread0Ready,
4015         Thread1Ready,
4016         Finish,
4017         Abort,
4018     };
4019     Step currentStep = Step::Start;
4020 
4021     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4022         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
4023         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4024 
4025         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4026 
4027         // Create the buffer in this context
4028         glGenBuffers(1, &buffer);
4029 
4030         constexpr float kBufferData[4] = {
4031             10.0f / 255.0f,
4032             50.0f / 255.0f,
4033             130.0f / 255.0f,
4034             220.0f / 255.0f,
4035         };
4036         glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4037         glBufferData(GL_UNIFORM_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW);
4038 
4039         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
4040 
4041         // Make sure the two threads start work around the same time
4042         threadSynchronization.nextStep(Step::Thread0Ready);
4043         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
4044 
4045         // Bind and unbind the buffer many times.  If ref counting is not thread safe, chances are
4046         // the ref count would be incorrect in the end.  This can result in the buffer prematurely
4047         // getting deleted.
4048         for (uint32_t i = 0; i < 8000; ++i)
4049         {
4050             glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? 0 : buffer);
4051         }
4052         ASSERT_GL_NO_ERROR();
4053 
4054         threadSynchronization.nextStep(Step::Finish);
4055     };
4056     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4057         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
4058         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4059 
4060         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4061 
4062         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
4063         threadSynchronization.nextStep(Step::Thread1Ready);
4064 
4065         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
4066 
4067         // Bind and unbind the buffer many times.
4068         for (uint32_t i = 0; i < 4000; ++i)
4069         {
4070             glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? buffer : 0);
4071         }
4072 
4073         // Draw with it to make sure buffer is still valid and not accidentally deleted due to bad
4074         // ref counting.
4075         glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4076         glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
4077         drawQuad(program, essl3_shaders::PositionAttrib(), 0);
4078 
4079         // Verify the results
4080         const GLColor expect(10, 50, 130, 220);
4081         EXPECT_PIXEL_RECT_EQ(0, 0, kSurfaceWidth, kSurfaceHeight, expect);
4082         ASSERT_GL_NO_ERROR();
4083 
4084         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
4085     };
4086 
4087     std::array<LockStepThreadFunc, 2> threadFuncs = {
4088         std::move(thread0),
4089         std::move(thread1),
4090     };
4091 
4092     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
4093                                threadFuncs.data());
4094 
4095     ASSERT_NE(currentStep, Step::Abort);
4096 }
4097 ANGLE_INSTANTIATE_TEST(
4098     MultithreadingTest,
4099     ES2_OPENGL(),
4100     ES3_OPENGL(),
4101     ES2_OPENGLES(),
4102     ES3_OPENGLES(),
4103     ES3_VULKAN(),
4104     ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4105     ES3_VULKAN_SWIFTSHADER()
4106         .enable(Feature::AsyncCommandQueue)
4107         .enable(Feature::SlowAsyncCommandQueueForTesting),
4108     ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4109     ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4110     ES3_VULKAN_SWIFTSHADER()
4111         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4112         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4113     ES3_VULKAN_SWIFTSHADER()
4114         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4115         .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4116     ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4117     ES3_VULKAN_SWIFTSHADER()
4118         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4119         .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4120     ES3_VULKAN_SWIFTSHADER()
4121         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4122         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4123         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4124     ES2_D3D11(),
4125     ES3_D3D11());
4126 
4127 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultithreadingTestES3);
4128 ANGLE_INSTANTIATE_TEST(
4129     MultithreadingTestES3,
4130     ES3_OPENGL(),
4131     ES3_OPENGLES(),
4132     ES3_VULKAN(),
4133     ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4134     ES3_VULKAN_SWIFTSHADER()
4135         .enable(Feature::AsyncCommandQueue)
4136         .enable(Feature::SlowAsyncCommandQueueForTesting),
4137     ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4138     ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4139     ES3_VULKAN_SWIFTSHADER()
4140         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4141         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4142     ES3_VULKAN_SWIFTSHADER()
4143         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4144         .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4145     ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4146     ES3_VULKAN_SWIFTSHADER()
4147         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4148         .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4149     ES3_VULKAN_SWIFTSHADER()
4150         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4151         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4152         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4153     ES3_D3D11());
4154 
4155 }  // namespace angle
4156