xref: /aosp_15_r20/external/angle/src/tests/gl_tests/MultithreadingTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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/42263666
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/42263324)
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/42263324)
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 
914     // Draw something
915     while (!mExitThread)
916     {
917         if (mMainThreadSyncObj == nullptr)
918         {
919             angle::Sleep(0);
920         }
921 
922         std::lock_guard<decltype(mMutex)> lock(mMutex);
923 
924         if (mMainThreadSyncObj != nullptr)
925         {
926             glWaitSync(mMainThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
927             ASSERT_GL_NO_ERROR();
928             glDeleteSync(mMainThreadSyncObj);
929             ASSERT_GL_NO_ERROR();
930             mMainThreadSyncObj = nullptr;
931         }
932         else
933         {
934             continue;
935         }
936 
937         mDrawGreen = !mDrawGreen;
938 
939         glBindTexture(GL_TEXTURE_2D, mTexture2D);
940         ASSERT_GL_NO_ERROR();
941 
942         if (mDrawGreen)
943         {
944             if (useDraw)
945             {
946                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
947                 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.0f);
948             }
949             else
950             {
951                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
952                              greenColor.data());
953             }
954             ASSERT_GL_NO_ERROR();
955         }
956         else
957         {
958             if (useDraw)
959             {
960                 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
961                 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.0f);
962             }
963             else
964             {
965                 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
966                              redColor.data());
967             }
968             ASSERT_GL_NO_ERROR();
969         }
970 
971         ASSERT_EQ(mSecondThreadSyncObj.load(), nullptr);
972         mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
973         ASSERT_GL_NO_ERROR();
974     }
975 
976     // Clean up
977     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
978     EXPECT_EGL_SUCCESS();
979 
980     eglDestroySurface(dpy, surface);
981     eglDestroyContext(dpy, ctx);
982 }
983 
984 // Test fence sync with multiple threads drawing
mainThreadDraw(bool useDraw)985 void MultithreadingTestES3::mainThreadDraw(bool useDraw)
986 {
987     EGLWindow *window  = getEGLWindow();
988     EGLDisplay dpy     = window->getDisplay();
989     EGLContext ctx     = window->getContext();
990     EGLSurface surface = window->getSurface();
991     // Use odd numbers so we bounce between red and green in the final image
992     constexpr int kNumIterations = 5;
993     constexpr int kNumDraws      = 5;
994 
995     mDrawGreen = false;
996 
997     std::thread textureThread(&MultithreadingTestES3::textureThreadFunction, this, true);
998 
999     ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
1000 
1001     for (int iterations = 0; iterations < kNumIterations; ++iterations)
1002     {
1003         GLColor expectedDrawColor;
1004 
1005         for (int draws = 0; draws < kNumDraws;)
1006         {
1007             if (mSecondThreadSyncObj == nullptr)
1008             {
1009                 angle::Sleep(0);
1010             }
1011 
1012             std::lock_guard<decltype(mMutex)> lock(mMutex);
1013 
1014             if (mSecondThreadSyncObj != nullptr)
1015             {
1016                 glWaitSync(mSecondThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
1017                 ASSERT_GL_NO_ERROR();
1018                 glDeleteSync(mSecondThreadSyncObj);
1019                 ASSERT_GL_NO_ERROR();
1020                 mSecondThreadSyncObj = nullptr;
1021             }
1022             else
1023             {
1024                 continue;
1025             }
1026 
1027             glBindFramebuffer(GL_FRAMEBUFFER, 0);
1028             glBindTexture(GL_TEXTURE_2D, mTexture2D);
1029             glUseProgram(texProgram);
1030             drawQuad(texProgram, essl1_shaders::PositionAttrib(), 0.0f);
1031 
1032             // mDrawGreen will be changed by the background thread past mMainThreadSyncObj
1033             // as it will start drawing the next color to fbo. This shouldn't affect
1034             // pixels of the current frame so save the expected color before unblocking the thread
1035             expectedDrawColor = mDrawGreen ? GLColor::green : GLColor::red;
1036 
1037             ASSERT_EQ(mMainThreadSyncObj.load(), nullptr);
1038             mMainThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1039             ASSERT_GL_NO_ERROR();
1040 
1041             ++draws;
1042         }
1043 
1044         ASSERT_GL_NO_ERROR();
1045         EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, expectedDrawColor);
1046 
1047         swapBuffers();
1048     }
1049 
1050     mExitThread = true;
1051     textureThread.join();
1052 
1053     // Re-make current the test window's context for teardown.
1054     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
1055     EXPECT_EGL_SUCCESS();
1056 }
1057 
1058 // Test that glFenceSync/glWaitSync works correctly with multithreading.
1059 // Main thread: Samples from the shared texture to draw to the default FBO.
1060 // Secondary (Texture) thread: Draws to the shared texture, which the Main thread samples from.
1061 // The overall execution flow is:
1062 // Main Thread:
1063 // 1. Wait for the mSecondThreadSyncObj fence object to be created.
1064 //    - This fence object is used by synchronize access to the shared texture by indicating that the
1065 //    Secondary thread's draws to the texture have all completed and it's now safe to sample from
1066 //    it.
1067 // 2. Once the fence is created, add a glWaitSync(mSecondThreadSyncObj) to the command stream and
1068 //    then delete it.
1069 // 3. Draw, sampling from the shared texture.
1070 // 4. Create a new mMainThreadSyncObj.
1071 //    - This fence object is used to synchronize access to the shared texture by indicating that the
1072 //    Main thread's draws are no longer sampling from the texture, so it's now safe for the
1073 //    Secondary thread to draw to it again with a new color.
1074 // Secondary (Texture) Thread:
1075 // 1. Wait for the mMainThreadSyncObj fence object to be created.
1076 // 2. Once the fence is created, add a glWaitSync(mMainThreadSyncObj) to the command stream and then
1077 //    delete it.
1078 // 3. Draw/Fill the texture.
1079 // 4. Create a new mSecondThreadSyncObj.
1080 //
1081 // These threads loop for the specified number of iterations, drawing/sampling the shared texture
1082 // with the necessary glFlush()s and occasional eglSwapBuffers() to mimic a real multithreaded GLES
1083 // application.
TEST_P(MultithreadingTestES3,MultithreadFenceDraw)1084 TEST_P(MultithreadingTestES3, MultithreadFenceDraw)
1085 {
1086     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1087 
1088     // Have the secondary thread use glDrawArrays()
1089     mainThreadDraw(true);
1090 }
1091 
1092 // Same as MultithreadFenceDraw, but with the secondary thread using glTexImage2D rather than
1093 // glDrawArrays.
TEST_P(MultithreadingTestES3,MultithreadFenceTexImage)1094 TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)
1095 {
1096     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1097 
1098     // Have the secondary thread use glTexImage2D()
1099     mainThreadDraw(false);
1100 }
1101 
1102 // Test that waiting on a sync object that hasn't been flushed and without a current context returns
1103 // TIMEOUT_EXPIRED or CONDITION_SATISFIED, but doesn't generate an error or crash.
TEST_P(MultithreadingTest,NoFlushNoContextReturnsTimeout)1104 TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)
1105 {
1106     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1107     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1108 
1109     std::mutex mutex;
1110 
1111     EGLWindow *window = getEGLWindow();
1112     EGLDisplay dpy    = window->getDisplay();
1113 
1114     glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
1115     glClear(GL_COLOR_BUFFER_BIT);
1116 
1117     EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1118     EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1119 
1120     std::thread thread = std::thread([&]() {
1121         std::lock_guard<decltype(mutex)> lock(mutex);
1122         // Make sure there is no active context on this thread.
1123         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1124         EXPECT_EGL_SUCCESS();
1125         // Don't wait forever to make sure the test terminates
1126         constexpr GLuint64 kTimeout = 1'000'000'000;  // 1 second
1127         int result                  = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
1128         // We typically expect to get back TIMEOUT_EXPIRED since the sync object was never flushed.
1129         // However, the OpenGL ES backend returns CONDITION_SATISFIED, which is also a passing
1130         // result.
1131         ASSERT_TRUE(result == EGL_TIMEOUT_EXPIRED_KHR || result == EGL_CONDITION_SATISFIED_KHR);
1132     });
1133 
1134     thread.join();
1135 
1136     EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
1137 }
1138 
1139 // Test that waiting on sync object that hasn't been flushed yet, but is later flushed by another
1140 // thread, correctly returns when the fence is signalled without a timeout.
TEST_P(MultithreadingTest,CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)1141 TEST_P(MultithreadingTest, CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)
1142 {
1143     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1144     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1145 
1146     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
1147 
1148     std::mutex mutex;
1149     std::condition_variable condVar;
1150 
1151     enum class Step
1152     {
1153         Start,
1154         Thread0Clear,
1155         Thread1CreateFence,
1156         Thread0ClientWaitSync,
1157         Thread1Flush,
1158         Finish,
1159         Abort,
1160     };
1161     Step currentStep = Step::Start;
1162 
1163     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1164         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1165 
1166         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1167 
1168         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1169 
1170         // Do work.
1171         glClearColor(1.0, 0.0, 0.0, 1.0);
1172         glClear(GL_COLOR_BUFFER_BIT);
1173 
1174         // Wait for thread 1 to clear.
1175         threadSynchronization.nextStep(Step::Thread0Clear);
1176         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1177 
1178         // Wait on the sync object, but do *not* flush it, since the other thread will flush.
1179         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1180         threadSynchronization.nextStep(Step::Thread0ClientWaitSync);
1181         ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, eglClientWaitSyncKHR(dpy, sync, 0, kTimeout));
1182 
1183         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1184     };
1185 
1186     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1187         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1188 
1189         // Wait for thread 0 to clear.
1190         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
1191 
1192         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1193 
1194         // Do work.
1195         glClearColor(0.0, 1.0, 0.0, 1.0);
1196         glClear(GL_COLOR_BUFFER_BIT);
1197 
1198         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1199         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1200 
1201         // Wait for the thread 0 to eglClientWaitSyncKHR().
1202         threadSynchronization.nextStep(Step::Thread1CreateFence);
1203         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ClientWaitSync));
1204 
1205         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1206         angle::Sleep(500);
1207         glFlush();
1208 
1209         // Clean up
1210         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1211 
1212         threadSynchronization.nextStep(Step::Finish);
1213     };
1214 
1215     std::array<LockStepThreadFunc, 2> threadFuncs = {
1216         std::move(thread0),
1217         std::move(thread1),
1218     };
1219 
1220     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1221 
1222     ASSERT_NE(currentStep, Step::Abort);
1223 }
1224 
1225 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1226 // that.
testFenceWithOpenRenderPass(FenceTest test,FlushMethod flushMethod)1227 void MultithreadingTestES3::testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod)
1228 {
1229     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1230     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1231 
1232     constexpr uint32_t kWidth  = 100;
1233     constexpr uint32_t kHeight = 200;
1234 
1235     GLsync sync    = 0;
1236     GLuint texture = 0;
1237 
1238     std::mutex mutex;
1239     std::condition_variable condVar;
1240 
1241     enum class Step
1242     {
1243         Start,
1244         Thread0CreateFence,
1245         Thread1WaitFence,
1246         Thread0Finish,
1247         Finish,
1248         Abort,
1249     };
1250     Step currentStep = Step::Start;
1251 
1252     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1253         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1254 
1255         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1256 
1257         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1258 
1259         // Create a shared texture to test synchronization
1260         GLTexture color;
1261         texture = color;
1262 
1263         glBindTexture(GL_TEXTURE_2D, texture);
1264         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1265 
1266         GLFramebuffer fbo;
1267         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1268         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1269 
1270         // Draw to shared texture.
1271         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1272         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1273         ASSERT_GL_NO_ERROR();
1274 
1275         // Issue a fence.  A render pass is currently open, so the fence is not actually submitted
1276         // in the Vulkan backend.
1277         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1278         ASSERT_NE(sync, nullptr);
1279 
1280         // Wait for thread 1 to wait on it.
1281         threadSynchronization.nextStep(Step::Thread0CreateFence);
1282         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1WaitFence));
1283 
1284         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1285         angle::Sleep(500);
1286         switch (flushMethod)
1287         {
1288             case FlushMethod::Flush:
1289                 glFlush();
1290                 break;
1291             case FlushMethod::Finish:
1292                 glFinish();
1293                 break;
1294         }
1295 
1296         // Clean up
1297         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1298 
1299         threadSynchronization.nextStep(Step::Thread0Finish);
1300         threadSynchronization.waitForStep(Step::Finish);
1301     };
1302 
1303     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1304         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1305 
1306         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1307 
1308         // Wait for thread 0 to create the fence object.
1309         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateFence));
1310 
1311         // Test access to the fence object
1312         threadSynchronization.nextStep(Step::Thread1WaitFence);
1313 
1314         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1315         GLenum result               = GL_CONDITION_SATISFIED;
1316         switch (test)
1317         {
1318             case FenceTest::ClientWait:
1319                 result = glClientWaitSync(sync, 0, kTimeout);
1320                 break;
1321             case FenceTest::ServerWait:
1322                 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1323                 break;
1324             case FenceTest::GetStatus:
1325             {
1326                 GLint value;
1327                 glGetSynciv(sync, GL_SYNC_STATUS, 1, nullptr, &value);
1328                 if (value != GL_SIGNALED)
1329                 {
1330                     result = glClientWaitSync(sync, 0, kTimeout);
1331                 }
1332                 break;
1333             }
1334         }
1335         ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1336 
1337         // Verify the shared texture is drawn to.
1338         glBindTexture(GL_TEXTURE_2D, texture);
1339 
1340         GLFramebuffer fbo;
1341         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1342         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1343 
1344         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1345 
1346         // Clean up
1347         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1348 
1349         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Finish));
1350         threadSynchronization.nextStep(Step::Finish);
1351     };
1352 
1353     std::array<LockStepThreadFunc, 2> threadFuncs = {
1354         std::move(thread0),
1355         std::move(thread1),
1356     };
1357 
1358     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1359 
1360     ASSERT_NE(currentStep, Step::Abort);
1361 }
1362 
1363 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1364 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFlush)1365 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFlush)
1366 {
1367     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Flush);
1368 }
1369 
1370 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1371 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFlush)1372 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFlush)
1373 {
1374     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Flush);
1375 }
1376 
1377 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1378 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFlush)1379 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFlush)
1380 {
1381     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Flush);
1382 }
1383 
1384 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1385 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFinish)1386 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFinish)
1387 {
1388     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Finish);
1389 }
1390 
1391 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1392 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFinish)1393 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFinish)
1394 {
1395     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Finish);
1396 }
1397 
1398 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1399 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFinish)1400 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFinish)
1401 {
1402     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Finish);
1403 }
1404 
1405 // Test the following scenario:
1406 //
1407 // - Thread A opens a render pass, and flushes it.  In the Vulkan backend, this may make the flush
1408 //   deferred.
1409 // - Thread B opens a render pass and creates a fence.  In the Vulkan backend, this also defers the
1410 //   flush.
1411 // - Thread C waits on fence
1412 //
1413 // In the Vulkan backend, submission of the fence is implied by thread C's wait, and thread A may
1414 // also be flushed as collateral.  If the fence's serial is updated based on thread A's submission,
1415 // synchronization between B and C would be broken.
TEST_P(MultithreadingTestES3,ThreadCWaitBeforeThreadBSyncFinish)1416 TEST_P(MultithreadingTestES3, ThreadCWaitBeforeThreadBSyncFinish)
1417 {
1418     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1419     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1420 
1421     constexpr uint32_t kWidth  = 100;
1422     constexpr uint32_t kHeight = 200;
1423 
1424     GLsync sync    = 0;
1425     GLuint texture = 0;
1426 
1427     std::mutex mutex;
1428     std::condition_variable condVar;
1429 
1430     enum class Step
1431     {
1432         Start,
1433         Thread0DrawAndFlush,
1434         Thread1CreateFence,
1435         Thread2WaitFence,
1436         Thread2Finished,
1437         Finish,
1438         Abort,
1439     };
1440     Step currentStep = Step::Start;
1441 
1442     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1443         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1444 
1445         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1446 
1447         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1448 
1449         // Open a render pass and flush it.
1450         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1451         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1452         glFlush();
1453         ASSERT_GL_NO_ERROR();
1454 
1455         threadSynchronization.nextStep(Step::Thread0DrawAndFlush);
1456         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1457 
1458         // Clean up
1459         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1460     };
1461 
1462     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1463         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1464 
1465         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1466 
1467         // Wait for thread 0 to set up
1468         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DrawAndFlush));
1469 
1470         // Create a shared texture to test synchronization
1471         GLTexture color;
1472         texture = color;
1473 
1474         glBindTexture(GL_TEXTURE_2D, texture);
1475         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1476 
1477         GLFramebuffer fbo;
1478         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1479         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1480 
1481         // Draw to shared texture.
1482         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1483         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1484         ASSERT_GL_NO_ERROR();
1485 
1486         // Issue a fence.  A render pass is currently open, so the fence is not actually submitted
1487         // in the Vulkan backend.
1488         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1489         ASSERT_NE(sync, nullptr);
1490 
1491         // Wait for thread 1 to wait on it.
1492         threadSynchronization.nextStep(Step::Thread1CreateFence);
1493         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2WaitFence));
1494 
1495         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1496         angle::Sleep(500);
1497         glFlush();
1498 
1499         // Clean up
1500         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1501 
1502         threadSynchronization.nextStep(Step::Thread2Finished);
1503         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1504     };
1505 
1506     auto thread2 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1507         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1508 
1509         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1510 
1511         // Wait for thread 0 to create the fence object.
1512         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1513 
1514         // Test access to the fence object
1515         threadSynchronization.nextStep(Step::Thread2WaitFence);
1516 
1517         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
1518         GLenum result               = glClientWaitSync(sync, 0, kTimeout);
1519         ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1520 
1521         // Verify the shared texture is drawn to.
1522         glBindTexture(GL_TEXTURE_2D, texture);
1523 
1524         GLFramebuffer fbo;
1525         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1526         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1527 
1528         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1529 
1530         // Clean up
1531         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1532 
1533         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2Finished));
1534         threadSynchronization.nextStep(Step::Finish);
1535     };
1536 
1537     std::array<LockStepThreadFunc, 3> threadFuncs = {
1538         std::move(thread0),
1539         std::move(thread1),
1540         std::move(thread2),
1541     };
1542 
1543     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1544 
1545     ASSERT_NE(currentStep, Step::Abort);
1546 }
1547 
1548 // Test that having commands recorded but not submitted on one thread using a texture, does not
1549 // interfere with similar commands on another thread using the same texture.  Regression test for a
1550 // bug in the Vulkan backend where the first thread would batch updates to a descriptor set not
1551 // visible to the other thread, while the other thread picks up the (unupdated) descriptor set from
1552 // a shared cache.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads)1553 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads)
1554 {
1555     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1556     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1557 
1558     GLsync sync    = 0;
1559     GLuint texture = 0;
1560 
1561     constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1562 
1563     std::mutex mutex;
1564     std::condition_variable condVar;
1565 
1566     enum class Step
1567     {
1568         Start,
1569         Thread0CreateTextureAndDraw,
1570         Thread1DrawAndFlush,
1571         Finish,
1572         Abort,
1573     };
1574     Step currentStep = Step::Start;
1575 
1576     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1577         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1578 
1579         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1580 
1581         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1582 
1583         // Create a texture, and record a command that draws into it.
1584         GLTexture color;
1585         texture = color;
1586 
1587         glBindTexture(GL_TEXTURE_2D, texture);
1588         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1589         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1590         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1591         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1592 
1593         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1594         ASSERT_NE(sync, nullptr);
1595 
1596         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1597                          essl1_shaders::fs::Texture2D());
1598         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1599 
1600         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1601         // Vulkan backend.
1602         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1603         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1604 
1605         // Flush after thread 1
1606         EXPECT_PIXEL_COLOR_NEAR(
1607             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1608 
1609         // Clean up
1610         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1611 
1612         threadSynchronization.nextStep(Step::Finish);
1613     };
1614 
1615     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1616         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1617 
1618         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1619 
1620         // Wait for thread 0 to set up
1621         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1622 
1623         // Synchronize with the texture upload (but not the concurrent read)
1624         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1625 
1626         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1627         // descriptor sets used in the Vulkan backend are identical.
1628         glBindTexture(GL_TEXTURE_2D, texture);
1629         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1630                          essl1_shaders::fs::Texture2D());
1631         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1632 
1633         // Flush
1634         EXPECT_PIXEL_COLOR_NEAR(
1635             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1636 
1637         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1638 
1639         // Clean up
1640         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1641 
1642         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1643     };
1644 
1645     std::array<LockStepThreadFunc, 2> threadFuncs = {
1646         std::move(thread0),
1647         std::move(thread1),
1648     };
1649 
1650     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1651 
1652     ASSERT_NE(currentStep, Step::Abort);
1653 }
1654 
1655 // Similar to UnsynchronizedTextureReads, but the texture update is done through framebuffer write.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads2)1656 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads2)
1657 {
1658     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1659     ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1660 
1661     GLsync sync    = 0;
1662     GLuint texture = 0;
1663 
1664     std::mutex mutex;
1665     std::condition_variable condVar;
1666 
1667     enum class Step
1668     {
1669         Start,
1670         Thread0CreateTextureAndDraw,
1671         Thread1DrawAndFlush,
1672         Finish,
1673         Abort,
1674     };
1675     Step currentStep = Step::Start;
1676 
1677     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1678         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1679 
1680         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1681 
1682         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1683 
1684         // Create a texture, and record a command that draws into it.
1685         GLTexture color;
1686         texture = color;
1687 
1688         glBindTexture(GL_TEXTURE_2D, texture);
1689         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1690         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1691         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1692 
1693         GLFramebuffer fbo;
1694         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1695         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1696 
1697         glClearColor(1, 0, 0, 1);
1698         glClear(GL_COLOR_BUFFER_BIT);
1699         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1700         glBindFramebuffer(GL_FRAMEBUFFER, 0);
1701 
1702         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1703         ASSERT_NE(sync, nullptr);
1704 
1705         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1706                          essl1_shaders::fs::Texture2D());
1707         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1708 
1709         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1710         // Vulkan backend.
1711         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1712         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1713 
1714         // Flush after thread 1
1715         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1716 
1717         // Clean up
1718         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1719 
1720         threadSynchronization.nextStep(Step::Finish);
1721     };
1722 
1723     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1724         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1725 
1726         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1727 
1728         // Wait for thread 0 to set up
1729         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1730 
1731         // Synchronize with the texture update (but not the concurrent read)
1732         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1733 
1734         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1735         // descriptor sets used in the Vulkan backend are identical.
1736         glBindTexture(GL_TEXTURE_2D, texture);
1737         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1738                          essl1_shaders::fs::Texture2D());
1739         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1740 
1741         // Flush
1742         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1743 
1744         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1745 
1746         // Clean up
1747         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1748 
1749         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1750     };
1751 
1752     std::array<LockStepThreadFunc, 2> threadFuncs = {
1753         std::move(thread0),
1754         std::move(thread1),
1755     };
1756 
1757     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1758 
1759     ASSERT_NE(currentStep, Step::Abort);
1760 }
1761 
1762 // Similar to UnsynchronizedTextureReads, but the texture is used once.  This is because
1763 // UnsynchronizedTextureRead hits a different bug than it intends to test.  This test makes sure the
1764 // image is put in the right layout, by using it together with another texture (i.e. a different
1765 // descriptor set).
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads3)1766 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads3)
1767 {
1768     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1769 
1770     constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1771 
1772     GLuint texture = 0;
1773 
1774     std::mutex mutex;
1775     std::condition_variable condVar;
1776 
1777     enum class Step
1778     {
1779         Start,
1780         Thread0CreateTextureAndDraw,
1781         Thread1DrawAndFlush,
1782         Finish,
1783         Abort,
1784     };
1785     Step currentStep = Step::Start;
1786 
1787     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1788         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1789 
1790         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1791 
1792         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1793 
1794         // Create a texture, and record a command that draws into it.
1795         GLTexture color;
1796         texture = color;
1797 
1798         glActiveTexture(GL_TEXTURE0);
1799         glBindTexture(GL_TEXTURE_2D, texture);
1800         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1801         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1802         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1803         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1804 
1805         glActiveTexture(GL_TEXTURE1);
1806         GLTexture color2;
1807         glBindTexture(GL_TEXTURE_2D, color2);
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         ANGLE_GL_PROGRAM(setupTexture, essl1_shaders::vs::Texture2D(),
1814                          R"(precision mediump float;
1815 uniform sampler2D tex2D;
1816 uniform sampler2D tex2D2;
1817 varying vec2 v_texCoord;
1818 
1819 void main()
1820 {
1821     gl_FragColor = texture2D(tex2D, v_texCoord) + texture2D(tex2D2, v_texCoord);
1822 })");
1823         drawQuad(setupTexture, essl1_shaders::PositionAttrib(), 0.0f);
1824         ASSERT_GL_NO_ERROR();
1825 
1826         glFinish();
1827 
1828         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1829                          essl1_shaders::fs::Texture2D());
1830         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1831         ASSERT_GL_NO_ERROR();
1832 
1833         // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1834         // Vulkan backend.
1835         threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1836         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1837 
1838         // Flush after thread 1
1839         EXPECT_PIXEL_COLOR_NEAR(
1840             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1841 
1842         // Clean up
1843         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1844 
1845         threadSynchronization.nextStep(Step::Finish);
1846     };
1847 
1848     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1849         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1850 
1851         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1852 
1853         // Wait for thread 0 to set up
1854         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1855 
1856         // Draw with the same texture, in the same way as thread 0.  This ensures that the
1857         // descriptor sets used in the Vulkan backend are identical.
1858         glBindTexture(GL_TEXTURE_2D, texture);
1859         ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1860                          essl1_shaders::fs::Texture2D());
1861         drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1862         ASSERT_GL_NO_ERROR();
1863 
1864         // Flush
1865         EXPECT_PIXEL_COLOR_NEAR(
1866             0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1867 
1868         threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1869 
1870         // Clean up
1871         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1872 
1873         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1874     };
1875 
1876     std::array<LockStepThreadFunc, 2> threadFuncs = {
1877         std::move(thread0),
1878         std::move(thread1),
1879     };
1880 
1881     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1882 
1883     ASSERT_NE(currentStep, Step::Abort);
1884 }
1885 
1886 // Test framebuffer fetch program used between share groups.
testFramebufferFetch(DrawOrder drawOrder)1887 void MultithreadingTestES3::testFramebufferFetch(DrawOrder drawOrder)
1888 {
1889     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1890     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
1891 
1892     GLProgram framebufferFetchProgram;
1893 
1894     constexpr char kFS[] = R"(#version 300 es
1895 #extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
1896 layout(noncoherent, location = 0) inout highp vec4 o_color;
1897 
1898 uniform highp vec4 u_color;
1899 void main (void)
1900 {
1901     o_color += u_color;
1902 })";
1903 
1904     std::mutex mutex;
1905     std::condition_variable condVar;
1906 
1907     enum class Step
1908     {
1909         Start,
1910         Thread0PreCreateProgram,
1911         Thread1CreateProgram,
1912         Finish,
1913         Abort,
1914     };
1915     Step currentStep = Step::Start;
1916 
1917     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1918         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1919 
1920         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1921 
1922         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1923 
1924         // Open a render pass, if requested.
1925         if (drawOrder == DrawOrder::Before)
1926         {
1927             ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1928             drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1929             ASSERT_GL_NO_ERROR();
1930         }
1931 
1932         threadSynchronization.nextStep(Step::Thread0PreCreateProgram);
1933         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateProgram));
1934 
1935         // Render using the framebuffer fetch program
1936         if (drawOrder == DrawOrder::After)
1937         {
1938             ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1939             drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1940             ASSERT_GL_NO_ERROR();
1941         }
1942 
1943         glFramebufferFetchBarrierEXT();
1944 
1945         glUseProgram(framebufferFetchProgram);
1946         GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1947         glUniform4f(colorLocation, 1, 0, 0, 0);
1948         drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1949         ASSERT_GL_NO_ERROR();
1950 
1951         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
1952 
1953         threadSynchronization.nextStep(Step::Finish);
1954 
1955         // Clean up
1956         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1957     };
1958 
1959     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1960         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1961 
1962         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1963 
1964         // Wait for thread 0 to set up
1965         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0PreCreateProgram));
1966 
1967         // Create the framebuffer fetch program
1968         framebufferFetchProgram.makeRaster(essl3_shaders::vs::Simple(), kFS);
1969         glUseProgram(framebufferFetchProgram);
1970 
1971         // Notify the other thread to use it
1972         threadSynchronization.nextStep(Step::Thread1CreateProgram);
1973         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1974 
1975         glClearColor(0, 0, 0, 1);
1976         glClear(GL_COLOR_BUFFER_BIT);
1977 
1978         glFramebufferFetchBarrierEXT();
1979 
1980         glUseProgram(framebufferFetchProgram);
1981         GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1982         glUniform4f(colorLocation, 0, 0, 1, 0);
1983         drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1984         ASSERT_GL_NO_ERROR();
1985 
1986         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1987 
1988         // Clean up
1989         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1990     };
1991 
1992     std::array<LockStepThreadFunc, 2> threadFuncs = {
1993         std::move(thread0),
1994         std::move(thread1),
1995     };
1996 
1997     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1998 
1999     ASSERT_NE(currentStep, Step::Abort);
2000 }
2001 
2002 // Thread 1 creates the framebuffer fetch program.  Thread 0 proceeds to use it.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchBeforeRenderPass)2003 TEST_P(MultithreadingTestES3, CreateFramebufferFetchBeforeRenderPass)
2004 {
2005     testFramebufferFetch(DrawOrder::After);
2006 }
2007 
2008 // Thread 1 creates the framebuffer fetch program while thread 0 is mid render pass.  Thread 0
2009 // proceeds to use the framebuffer fetch program in the rest of its render pass.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchMidRenderPass)2010 TEST_P(MultithreadingTestES3, CreateFramebufferFetchMidRenderPass)
2011 {
2012     testFramebufferFetch(DrawOrder::Before);
2013 }
2014 
2015 // Test async monolithic pipeline creation in the Vulkan backend vs shared programs.  This test
2016 // makes one context/thread create a set of programs, then has another context/thread use them a few
2017 // times, and then the original context destroys them.
TEST_P(MultithreadingTestES3,ProgramUseAndDestroyInTwoContexts)2018 TEST_P(MultithreadingTestES3, ProgramUseAndDestroyInTwoContexts)
2019 {
2020     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2021 
2022     GLProgram programs[6];
2023 
2024     GLsync sync = 0;
2025 
2026     std::mutex mutex;
2027     std::condition_variable condVar;
2028 
2029     enum class Step
2030     {
2031         Start,
2032         Thread0CreatePrograms,
2033         Thread1UsePrograms,
2034         Finish,
2035         Abort,
2036     };
2037     Step currentStep = Step::Start;
2038 
2039     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2040         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2041 
2042         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2043 
2044         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2045 
2046         // Create the programs
2047         programs[0].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2048         programs[1].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2049         programs[2].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2050         programs[3].makeRaster(essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Checkered());
2051         programs[4].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
2052         programs[5].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
2053 
2054         EXPECT_TRUE(programs[0].valid());
2055         EXPECT_TRUE(programs[1].valid());
2056         EXPECT_TRUE(programs[2].valid());
2057         EXPECT_TRUE(programs[3].valid());
2058         EXPECT_TRUE(programs[4].valid());
2059         EXPECT_TRUE(programs[5].valid());
2060 
2061         threadSynchronization.nextStep(Step::Thread0CreatePrograms);
2062         // Wait for the other thread to use the programs
2063         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsePrograms));
2064 
2065         // Destroy them
2066         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
2067         programs[0].reset();
2068         programs[1].reset();
2069         programs[2].reset();
2070         programs[3].reset();
2071         programs[4].reset();
2072         programs[5].reset();
2073 
2074         threadSynchronization.nextStep(Step::Finish);
2075 
2076         // Clean up
2077         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2078     };
2079 
2080     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2081         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2082 
2083         // Wait for thread 0 to create the programs
2084         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatePrograms));
2085 
2086         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2087 
2088         // Use them a few times.
2089         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2090         drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2091         drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2092         drawQuad(programs[3], essl1_shaders::PositionAttrib(), 0.0f);
2093         drawQuad(programs[4], essl1_shaders::PositionAttrib(), 0.0f);
2094 
2095         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2096         drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2097         drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2098 
2099         drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2100 
2101         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2102         ASSERT_NE(sync, nullptr);
2103 
2104         // Notify the other thread to destroy the programs.
2105         threadSynchronization.nextStep(Step::Thread1UsePrograms);
2106         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2107 
2108         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
2109 
2110         // Clean up
2111         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2112     };
2113 
2114     std::array<LockStepThreadFunc, 2> threadFuncs = {
2115         std::move(thread0),
2116         std::move(thread1),
2117     };
2118 
2119     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
2120 
2121     ASSERT_NE(currentStep, Step::Abort);
2122 }
2123 
2124 // Tests that Context with High Priority will correctly sample Texture rendered by Share Context
2125 // with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriority)2126 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriority)
2127 {
2128     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2129 
2130     constexpr size_t kIterationCountMax = 10;
2131 
2132     const bool reduceLoad       = isSwiftshader();
2133     const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2134     const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2135 
2136     // Initialize contexts
2137     EGLWindow *window = getEGLWindow();
2138     EGLDisplay dpy    = window->getDisplay();
2139     EGLConfig config  = window->getConfig();
2140 
2141     // Large enough texture to catch timing problems.
2142     constexpr GLsizei kTexSize       = 1024;
2143     constexpr size_t kThreadCount    = 2;
2144     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2145     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2146 
2147     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2148 
2149     EGLint pbufferAttributes[kThreadCount][6] = {
2150         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2151         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2152 
2153     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2154                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2155     EGLint *extraAttributes = attributes;
2156     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2157     {
2158         attributes[2] = EGL_NONE;
2159     }
2160     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2161     {
2162         // Run tests with single priority anyway.
2163         extraAttributes += 2;
2164     }
2165 
2166     for (size_t t = 0; t < kThreadCount; ++t)
2167     {
2168         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2169         EXPECT_EGL_SUCCESS();
2170 
2171         attributes[1] = priorities[t];
2172         attributes[3] = mVirtualizationGroup++;
2173 
2174         ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2175         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2176     }
2177 
2178     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2179     std::mutex mutex;
2180     std::condition_variable condVar;
2181 
2182     enum class Step
2183     {
2184         Start,
2185         Thread0Init,
2186         Thread1Init,
2187         Thread0Draw,
2188         Thread1Draw,
2189         Finish = Thread0Draw + kIterationCountMax * 2,
2190         Abort,
2191     };
2192     Step currentStep = Step::Start;
2193 
2194     GLTexture texture;
2195     GLsync thread0DrawSyncObj;
2196 
2197     auto calculateTestColor = [](size_t i) {
2198         return GLColor(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2199     };
2200     auto makeStep = [](Step base, size_t i) {
2201         return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2202     };
2203 
2204     // Render to the texture.
2205     std::thread thread0 = std::thread([&]() {
2206         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2207 
2208         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2209         EXPECT_EGL_SUCCESS();
2210 
2211         glBindTexture(GL_TEXTURE_2D, texture);
2212         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2213                      nullptr);
2214         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2215         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2216 
2217         GLFramebuffer fbo;
2218         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2219         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2220         glViewport(0, 0, kTexSize, kTexSize);
2221 
2222         ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2223                          essl1_shaders::fs::UniformColor());
2224         GLint colorLocation =
2225             glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2226         ASSERT_NE(-1, colorLocation);
2227         glUseProgram(colorProgram);
2228 
2229         // Notify second thread that initialization is finished.
2230         threadSynchronization.nextStep(Step::Thread0Init);
2231         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2232 
2233         for (size_t i = 0; i < iterationCount; ++i)
2234         {
2235             // Simulate heavy work...
2236             glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2237             for (size_t j = 0; j < heavyDrawCount; ++j)
2238             {
2239                 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2240             }
2241 
2242             // Draw with test color.
2243             Vector4 color = calculateTestColor(i).toNormalizedVector();
2244             glUniform4f(colorLocation, color.x(), color.y(), color.z(), color.w());
2245             drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2246 
2247             thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2248             ASSERT_GL_NO_ERROR();
2249 
2250             // Notify second thread that draw is finished.
2251             threadSynchronization.nextStep(makeStep(Step::Thread0Draw, i));
2252             ASSERT_TRUE(threadSynchronization.waitForStep(
2253                 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i)));
2254         }
2255 
2256         EXPECT_GL_NO_ERROR();
2257         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2258         EXPECT_EGL_SUCCESS();
2259     });
2260 
2261     // Sample texture
2262     std::thread thread1 = std::thread([&]() {
2263         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2264 
2265         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2266         EXPECT_EGL_SUCCESS();
2267 
2268         glViewport(0, 0, kTexSize, kTexSize);
2269 
2270         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2271                          essl1_shaders::fs::Texture2D());
2272         glUseProgram(textureProgram);
2273 
2274         // Wait for first thread to finish initializing.
2275         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Init));
2276 
2277         glBindTexture(GL_TEXTURE_2D, texture);
2278 
2279         // Wait for first thread to draw using the shared texture.
2280         threadSynchronization.nextStep(Step::Thread1Init);
2281 
2282         for (size_t i = 0; i < iterationCount; ++i)
2283         {
2284             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Draw, i)));
2285 
2286             ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2287             glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2288             ASSERT_GL_NO_ERROR();
2289 
2290             // Should draw test color.
2291             drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2292 
2293             // Check test color in four corners.
2294             GLColor color = calculateTestColor(i);
2295             EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2296             EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2297             EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2298             EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2299 
2300             glDeleteSync(thread0DrawSyncObj);
2301             ASSERT_GL_NO_ERROR();
2302             thread0DrawSyncObj = nullptr;
2303 
2304             threadSynchronization.nextStep(
2305                 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i));
2306         }
2307 
2308         EXPECT_GL_NO_ERROR();
2309         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2310         EXPECT_EGL_SUCCESS();
2311     });
2312 
2313     thread0.join();
2314     thread1.join();
2315 
2316     ASSERT_NE(currentStep, Step::Abort);
2317 
2318     // Clean up
2319     for (size_t t = 0; t < kThreadCount; ++t)
2320     {
2321         eglDestroySurface(dpy, surface[t]);
2322         eglDestroyContext(dpy, ctx[t]);
2323     }
2324 }
2325 
2326 // Tests that newly created Context with High Priority will correctly sample Texture already
2327 // rendered by Share Context with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleInNewContextWithDifferentPriority)2328 TEST_P(MultithreadingTestES3, RenderThenSampleInNewContextWithDifferentPriority)
2329 {
2330     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2331     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2332 
2333     const bool reduceLoad       = isSwiftshader();
2334     const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2335 
2336     // Initialize contexts
2337     EGLWindow *window = getEGLWindow();
2338     EGLDisplay dpy    = window->getDisplay();
2339     EGLConfig config  = window->getConfig();
2340 
2341     // Large enough texture to catch timing problems.
2342     constexpr GLsizei kTexSize       = 1024;
2343     constexpr size_t kThreadCount    = 2;
2344     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2345     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2346 
2347     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2348 
2349     EGLint pbufferAttributes[kThreadCount][6] = {
2350         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2351         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2352 
2353     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2354                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2355     EGLint *extraAttributes = attributes;
2356     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2357     {
2358         attributes[2] = EGL_NONE;
2359     }
2360     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2361     {
2362         // Run tests with single priority anyway.
2363         extraAttributes += 2;
2364     }
2365 
2366     for (size_t t = 0; t < kThreadCount; ++t)
2367     {
2368         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2369         EXPECT_EGL_SUCCESS();
2370 
2371         attributes[1] = priorities[t];
2372         attributes[3] = mVirtualizationGroup++;
2373 
2374         // Second context will be created in a thread 1
2375         if (t == 0)
2376         {
2377             ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2378             EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2379         }
2380     }
2381 
2382     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2383     std::mutex mutex;
2384     std::condition_variable condVar;
2385 
2386     enum class Step
2387     {
2388         Start,
2389         Thread0Draw,
2390         Thread1Draw,
2391         Finish,
2392         Abort,
2393     };
2394     Step currentStep = Step::Start;
2395 
2396     // Create shared resources before threads to minimize timing delays.
2397     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2398     EXPECT_EGL_SUCCESS();
2399 
2400     ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2401     ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2402     ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2403     ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2404                      essl1_shaders::fs::Texture2D());
2405 
2406     GLTexture texture;
2407     glBindTexture(GL_TEXTURE_2D, texture);
2408     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2409                  nullptr);
2410     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2411     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2412 
2413     GLFramebuffer fbo;
2414     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2415     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2416     glViewport(0, 0, kTexSize, kTexSize);
2417 
2418     EXPECT_GL_NO_ERROR();
2419     EXPECT_EGL_TRUE(window->makeCurrent());
2420     EXPECT_EGL_SUCCESS();
2421 
2422     GLsync thread0DrawSyncObj;
2423 
2424     // Render to the texture.
2425     std::thread thread0 = std::thread([&]() {
2426         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2427 
2428         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2429         EXPECT_EGL_SUCCESS();
2430 
2431         // Enable additive blend
2432         glEnable(GL_BLEND);
2433         glBlendFunc(GL_ONE, GL_ONE);
2434 
2435         GLuint query;
2436         glGenQueries(1, &query);
2437         glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2438         ASSERT_GL_NO_ERROR();
2439 
2440         // Simulate heavy work...
2441         glUseProgram(redProgram);
2442         for (size_t j = 0; j < heavyDrawCount; ++j)
2443         {
2444             // Draw with Red color.
2445             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2446         }
2447 
2448         // Draw with Green color.
2449         glUseProgram(greenProgram);
2450         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2451 
2452         // This should force "flushToPrimary()"
2453         glEndQuery(GL_TIME_ELAPSED_EXT);
2454         glDeleteQueries(1, &query);
2455         ASSERT_GL_NO_ERROR();
2456 
2457         // Continue draw with Blue color after flush...
2458         glUseProgram(blueProgram);
2459         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2460 
2461         thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2462         ASSERT_GL_NO_ERROR();
2463 
2464         // Notify second thread that draw is finished.
2465         threadSynchronization.nextStep(Step::Thread0Draw);
2466         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2467 
2468         EXPECT_GL_NO_ERROR();
2469         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2470         EXPECT_EGL_SUCCESS();
2471     });
2472 
2473     // Sample texture
2474     std::thread thread1 = std::thread([&]() {
2475         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2476 
2477         // Wait for first thread to finish draw.
2478         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2479 
2480         // Create High priority Context when Low priority Context already rendered to the texture.
2481         ctx[1] = window->createContext(ctx[0], extraAttributes);
2482         EXPECT_NE(EGL_NO_CONTEXT, ctx[1]);
2483 
2484         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2485         EXPECT_EGL_SUCCESS();
2486 
2487         glViewport(0, 0, kTexSize, kTexSize);
2488 
2489         ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2490         glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2491         ASSERT_GL_NO_ERROR();
2492 
2493         // Should draw test color.
2494         glUseProgram(textureProgram);
2495         glBindTexture(GL_TEXTURE_2D, texture);
2496         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2497 
2498         // Check test color in four corners.
2499         GLColor color = GLColor::white;
2500         EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2501         EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2502         EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2503         EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2504 
2505         glDeleteSync(thread0DrawSyncObj);
2506         ASSERT_GL_NO_ERROR();
2507         thread0DrawSyncObj = nullptr;
2508 
2509         threadSynchronization.nextStep(Step::Finish);
2510 
2511         EXPECT_GL_NO_ERROR();
2512         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2513         EXPECT_EGL_SUCCESS();
2514     });
2515 
2516     thread0.join();
2517     thread1.join();
2518 
2519     ASSERT_NE(currentStep, Step::Abort);
2520 
2521     // Clean up
2522     for (size_t t = 0; t < kThreadCount; ++t)
2523     {
2524         eglDestroySurface(dpy, surface[t]);
2525         eglDestroyContext(dpy, ctx[t]);
2526     }
2527 }
2528 
2529 // Tests that Context with High Priority will correctly sample EGLImage target Texture rendered by
2530 // other Context with Low Priority into EGLImage source texture.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriorityUsingEGLImage)2531 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriorityUsingEGLImage)
2532 {
2533     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2534     ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
2535     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_EGL_image"));
2536     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2537 
2538     const bool reduceLoad       = isSwiftshader();
2539     const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2540 
2541     // Initialize contexts
2542     EGLWindow *window = getEGLWindow();
2543     EGLDisplay dpy    = window->getDisplay();
2544     EGLConfig config  = window->getConfig();
2545 
2546     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image_base"));
2547     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
2548 
2549     // Large enough texture to catch timing problems.
2550     constexpr GLsizei kTexSize       = 1024;
2551     constexpr size_t kThreadCount    = 2;
2552     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2553     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2554 
2555     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2556 
2557     EGLint pbufferAttributes[kThreadCount][6] = {
2558         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2559         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2560 
2561     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2562                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2563     EGLint *extraAttributes = attributes;
2564     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2565     {
2566         attributes[2] = EGL_NONE;
2567     }
2568     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2569     {
2570         // Run tests with single priority anyway.
2571         extraAttributes += 2;
2572     }
2573 
2574     for (size_t t = 0; t < kThreadCount; ++t)
2575     {
2576         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2577         EXPECT_EGL_SUCCESS();
2578 
2579         attributes[1] = priorities[t];
2580         attributes[3] = mVirtualizationGroup++;
2581 
2582         // Contexts not shared
2583         ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2584         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2585     }
2586 
2587     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2588     std::mutex mutex;
2589     std::condition_variable condVar;
2590 
2591     enum class Step
2592     {
2593         Start,
2594         Thread1Init,
2595         Thread0Draw,
2596         Thread1Draw,
2597         Finish,
2598         Abort,
2599     };
2600     Step currentStep = Step::Start;
2601 
2602     EGLImage image  = EGL_NO_IMAGE_KHR;
2603     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
2604 
2605     // Render to the EGLImage source texture.
2606     std::thread thread0 = std::thread([&]() {
2607         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2608 
2609         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2610         EXPECT_EGL_SUCCESS();
2611 
2612         // Create source texture.
2613         GLTexture texture;
2614         glBindTexture(GL_TEXTURE_2D, texture);
2615         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2616                      nullptr);
2617         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2618         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2619 
2620         GLFramebuffer fbo;
2621         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2622         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2623         glViewport(0, 0, kTexSize, kTexSize);
2624 
2625         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2626         ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2627         ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2628 
2629         // Wait for second thread to finish initializing.
2630         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2631 
2632         // Enable additive blend
2633         glEnable(GL_BLEND);
2634         glBlendFunc(GL_ONE, GL_ONE);
2635 
2636         GLuint query;
2637         glGenQueries(1, &query);
2638         glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2639         ASSERT_GL_NO_ERROR();
2640 
2641         // Simulate heavy work...
2642         glUseProgram(redProgram);
2643         for (size_t j = 0; j < heavyDrawCount; ++j)
2644         {
2645             // Draw with Red color.
2646             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2647         }
2648 
2649         // Draw with Green color.
2650         glUseProgram(greenProgram);
2651         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2652 
2653         // This should force "flushToPrimary()"
2654         glEndQuery(GL_TIME_ELAPSED_EXT);
2655         glDeleteQueries(1, &query);
2656         ASSERT_GL_NO_ERROR();
2657 
2658         // Continue draw with Blue color after flush...
2659         glUseProgram(blueProgram);
2660         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2661 
2662         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
2663         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
2664 
2665         // Create EGLImage.
2666         image = eglCreateImageKHR(
2667             dpy, ctx[0], EGL_GL_TEXTURE_2D_KHR,
2668             reinterpret_cast<EGLClientBuffer>(static_cast<uintptr_t>(texture)), nullptr);
2669         ASSERT_EGL_SUCCESS();
2670 
2671         // Notify second thread that draw is finished.
2672         threadSynchronization.nextStep(Step::Thread0Draw);
2673         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2674 
2675         EXPECT_GL_NO_ERROR();
2676         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2677         EXPECT_EGL_SUCCESS();
2678     });
2679 
2680     // Sample texture
2681     std::thread thread1 = std::thread([&]() {
2682         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2683 
2684         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2685         EXPECT_EGL_SUCCESS();
2686 
2687         glViewport(0, 0, kTexSize, kTexSize);
2688 
2689         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2690                          essl1_shaders::fs::Texture2D());
2691         glUseProgram(textureProgram);
2692 
2693         // Create target texture.
2694         GLTexture texture;
2695         glBindTexture(GL_TEXTURE_2D, texture);
2696         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2697         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2698 
2699         // Wait for first thread to draw into the source texture.
2700         threadSynchronization.nextStep(Step::Thread1Init);
2701         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2702 
2703         // Wait for draw to complete
2704         ASSERT_TRUE(eglWaitSyncKHR(dpy, sync, 0));
2705 
2706         // Specify target texture from EGLImage.
2707         glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
2708         ASSERT_GL_NO_ERROR();
2709 
2710         // Should draw test color.
2711         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2712         ASSERT_GL_NO_ERROR();
2713 
2714         // Check test color in four corners.
2715         GLColor color = GLColor::white;
2716         EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2717         EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2718         EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2719         EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2720 
2721         EXPECT_EGL_TRUE(eglDestroyImageKHR(dpy, image));
2722         image = EGL_NO_IMAGE_KHR;
2723 
2724         EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
2725         sync = EGL_NO_SYNC_KHR;
2726 
2727         threadSynchronization.nextStep(Step::Finish);
2728 
2729         EXPECT_GL_NO_ERROR();
2730         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2731         EXPECT_EGL_SUCCESS();
2732     });
2733 
2734     thread0.join();
2735     thread1.join();
2736 
2737     ASSERT_NE(currentStep, Step::Abort);
2738 
2739     // Clean up
2740     for (size_t t = 0; t < kThreadCount; ++t)
2741     {
2742         eglDestroySurface(dpy, surface[t]);
2743         eglDestroyContext(dpy, ctx[t]);
2744     }
2745 }
2746 
2747 // Tests mixing commands of Contexts with different Priorities in a single Command Buffers (Vulkan).
TEST_P(MultithreadingTestES3,ContextPriorityMixing)2748 TEST_P(MultithreadingTestES3, ContextPriorityMixing)
2749 {
2750     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2751     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2752 
2753     constexpr size_t kIterationCountMax = 10;
2754 
2755     const bool reduceLoad       = isSwiftshader();
2756     const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2757     const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2758 
2759     // Initialize contexts
2760     EGLWindow *window = getEGLWindow();
2761     EGLDisplay dpy    = window->getDisplay();
2762     EGLConfig config  = window->getConfig();
2763 
2764     // Large enough texture to catch timing problems.
2765     constexpr GLsizei kTexSize       = 1024;
2766     constexpr size_t kThreadCount    = 2;
2767     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2768     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2769 
2770     EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2771 
2772     EGLint pbufferAttributes[kThreadCount][6] = {
2773         {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2774         {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2775 
2776     EGLint attributes[]     = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2777                                EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2778     EGLint *extraAttributes = attributes;
2779     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2780     {
2781         attributes[2] = EGL_NONE;
2782     }
2783     if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2784     {
2785         // Run tests with single priority anyway.
2786         extraAttributes += 2;
2787     }
2788 
2789     for (size_t t = 0; t < kThreadCount; ++t)
2790     {
2791         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2792         EXPECT_EGL_SUCCESS();
2793 
2794         attributes[1] = priorities[t];
2795         attributes[3] = mVirtualizationGroup++;
2796 
2797         // Contexts not shared
2798         ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2799         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2800     }
2801 
2802     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2803     std::mutex mutex;
2804     std::condition_variable condVar;
2805 
2806     enum class Step
2807     {
2808         Start,
2809         Thread1DrawColor,
2810         Thread0Iterate,
2811         Finish = Thread1DrawColor + kIterationCountMax * 2,
2812         Abort,
2813     };
2814     Step currentStep = Step::Start;
2815 
2816     auto makeStep = [](Step base, size_t i) {
2817         return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2818     };
2819 
2820     // Triggers commands submission.
2821     std::thread thread0 = std::thread([&]() {
2822         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2823 
2824         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2825         EXPECT_EGL_SUCCESS();
2826 
2827         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2828 
2829         for (size_t i = 0; i < iterationCount; ++i)
2830         {
2831             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread1DrawColor, i)));
2832 
2833             glUseProgram(redProgram);
2834             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2835             ASSERT_GL_NO_ERROR();
2836 
2837             // This should perform commands submission.
2838             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2839             EXPECT_EGL_SUCCESS();
2840 
2841             threadSynchronization.nextStep(makeStep(Step::Thread0Iterate, i));
2842 
2843             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2844             EXPECT_EGL_SUCCESS();
2845         }
2846 
2847         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2848 
2849         EXPECT_GL_NO_ERROR();
2850         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2851         EXPECT_EGL_SUCCESS();
2852     });
2853 
2854     // Render and then sample texture.
2855     std::thread thread1 = std::thread([&]() {
2856         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2857 
2858         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2859         EXPECT_EGL_SUCCESS();
2860 
2861         glDisable(GL_DEPTH_TEST);
2862         glViewport(0, 0, kTexSize, kTexSize);
2863 
2864         GLTexture texture;
2865         glBindTexture(GL_TEXTURE_2D, texture);
2866         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2867                      nullptr);
2868         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2869         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2870 
2871         GLFramebuffer fbo;
2872         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2873         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2874 
2875         ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2876                          essl1_shaders::fs::UniformColor());
2877         GLint colorLocation =
2878             glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2879         ASSERT_NE(-1, colorLocation);
2880 
2881         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2882                          essl1_shaders::fs::Texture2D());
2883 
2884         for (size_t i = 0; i < iterationCount; ++i)
2885         {
2886             glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2887             glUseProgram(colorProgram);
2888 
2889             GLuint query;
2890             glGenQueries(1, &query);
2891             glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2892             ASSERT_GL_NO_ERROR();
2893 
2894             // Simulate heavy work...
2895             glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2896             for (size_t j = 0; j < heavyDrawCount; ++j)
2897             {
2898                 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2899             }
2900 
2901             // Draw with test color.
2902             GLColor color(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2903             Vector4 colorF = color.toNormalizedVector();
2904             glUniform4f(colorLocation, colorF.x(), colorF.y(), colorF.z(), colorF.w());
2905             drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2906             ASSERT_GL_NO_ERROR();
2907 
2908             // This should force "flushToPrimary()"
2909             glEndQuery(GL_TIME_ELAPSED_EXT);
2910             glDeleteQueries(1, &query);
2911             ASSERT_GL_NO_ERROR();
2912 
2913             threadSynchronization.nextStep(makeStep(Step::Thread1DrawColor, i));
2914             ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Iterate, i)));
2915 
2916             glBindFramebuffer(GL_FRAMEBUFFER, 0);
2917             glUseProgram(textureProgram);
2918 
2919             // Should draw test color.
2920             drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2921             ASSERT_GL_NO_ERROR();
2922 
2923             // Check test color in four corners.
2924             EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2925             EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2926             EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2927             EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2928         }
2929 
2930         threadSynchronization.nextStep(Step::Finish);
2931 
2932         EXPECT_GL_NO_ERROR();
2933         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2934         EXPECT_EGL_SUCCESS();
2935     });
2936 
2937     thread0.join();
2938     thread1.join();
2939 
2940     ASSERT_NE(currentStep, Step::Abort);
2941 
2942     // Clean up
2943     for (size_t t = 0; t < kThreadCount; ++t)
2944     {
2945         eglDestroySurface(dpy, surface[t]);
2946         eglDestroyContext(dpy, ctx[t]);
2947     }
2948 }
2949 
2950 // Test that it is possible to upload textures in one thread and use them in another with
2951 // synchronization.
TEST_P(MultithreadingTestES3,MultithreadedTextureUploadAndDraw)2952 TEST_P(MultithreadingTestES3, MultithreadedTextureUploadAndDraw)
2953 {
2954     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2955 
2956     constexpr size_t kTexSize = 4;
2957     GLTexture texture1;
2958     GLTexture texture2;
2959     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
2960     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
2961 
2962     // Sync primitives
2963     GLsync sync = nullptr;
2964     std::mutex mutex;
2965     std::condition_variable condVar;
2966 
2967     enum class Step
2968     {
2969         Start,
2970         Thread0UploadFinish,
2971         Finish,
2972         Abort,
2973     };
2974     Step currentStep = Step::Start;
2975 
2976     // Threads to upload and draw with textures.
2977     auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2978         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
2979         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2980 
2981         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2982 
2983         // Two mipmap textures are defined here. They are used for drawing in the other thread.
2984         glBindTexture(GL_TEXTURE_2D, texture1);
2985         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2986                      textureColors1.data());
2987         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
2988                      GL_UNSIGNED_BYTE, textureColors1.data());
2989         glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
2990                      GL_UNSIGNED_BYTE, textureColors1.data());
2991         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2992         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2993         ASSERT_GL_NO_ERROR();
2994 
2995         glBindTexture(GL_TEXTURE_2D, texture2);
2996         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2997                      textureColors2.data());
2998         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
2999                      GL_UNSIGNED_BYTE, textureColors2.data());
3000         glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
3001                      GL_UNSIGNED_BYTE, textureColors2.data());
3002         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3003         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3004         ASSERT_GL_NO_ERROR();
3005 
3006         // Create a sync object to be used for the draw thread.
3007         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3008         glFlush();
3009         ASSERT_NE(sync, nullptr);
3010 
3011         threadSynchronization.nextStep(Step::Thread0UploadFinish);
3012         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3013     };
3014 
3015     auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3016         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3017         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0UploadFinish));
3018 
3019         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3020 
3021         // Wait for the sync object to be signaled.
3022         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3023         ASSERT_GL_NO_ERROR();
3024 
3025         // Draw using the textures from the texture upload thread.
3026         ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
3027                          essl1_shaders::fs::Texture2D());
3028         glUseProgram(textureProgram);
3029 
3030         glBindTexture(GL_TEXTURE_2D, texture1);
3031         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3032         glFlush();
3033         ASSERT_GL_NO_ERROR();
3034         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3035 
3036         glBindTexture(GL_TEXTURE_2D, texture2);
3037         drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3038         glFlush();
3039         ASSERT_GL_NO_ERROR();
3040         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3041 
3042         threadSynchronization.nextStep(Step::Finish);
3043     };
3044 
3045     std::array<LockStepThreadFunc, 2> threadFuncs = {
3046         std::move(thread0Upload),
3047         std::move(thread1Draw),
3048     };
3049 
3050     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3051 
3052     ASSERT_NE(currentStep, Step::Abort);
3053 }
3054 
3055 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3056 // previous context, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnNewThread)3057 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnNewThread)
3058 {
3059     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3060 
3061     constexpr size_t kTexSize = 4;
3062     GLTexture texture1;
3063     GLTexture texture2;
3064     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3065     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3066 
3067     EGLWindow *window = getEGLWindow();
3068     EGLDisplay dpy    = window->getDisplay();
3069 
3070     std::thread thread = std::thread([&]() {
3071         // Create a context and upload the textures.
3072         EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
3073         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3074 
3075         glBindTexture(GL_TEXTURE_2D, texture1);
3076         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3077                      textureColors1.data());
3078         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3079                      GL_UNSIGNED_BYTE, textureColors1.data());
3080         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3081         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3082         ASSERT_GL_NO_ERROR();
3083 
3084         glBindTexture(GL_TEXTURE_2D, texture2);
3085         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3086                      textureColors2.data());
3087         glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3088                      GL_UNSIGNED_BYTE, textureColors2.data());
3089         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3090         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3091         ASSERT_GL_NO_ERROR();
3092 
3093         // Create a new context and use the uploaded textures.
3094         EGLContext ctx2 = createMultithreadedContext(window, ctx1);
3095         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
3096 
3097         GLFramebuffer fbo;
3098         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3099 
3100         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3101         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3102 
3103         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3104         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3105 
3106         // Destroy the contexts.
3107         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
3108         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3109     });
3110 
3111     thread.join();
3112 }
3113 
3114 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3115 // main thread, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnMainThread)3116 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnMainThread)
3117 {
3118     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3119 
3120     EGLWindow *window = getEGLWindow();
3121     EGLDisplay dpy    = window->getDisplay();
3122 
3123     // Upload the textures.
3124     constexpr size_t kTexSize = 4;
3125     GLTexture texture1;
3126     GLTexture texture2;
3127     std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3128     std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3129 
3130     glBindTexture(GL_TEXTURE_2D, texture1);
3131     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3132                  textureColors1.data());
3133     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3134                  GL_UNSIGNED_BYTE, textureColors1.data());
3135     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3136     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3137     ASSERT_GL_NO_ERROR();
3138 
3139     glBindTexture(GL_TEXTURE_2D, texture2);
3140     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3141                  textureColors2.data());
3142     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3143                  GL_UNSIGNED_BYTE, textureColors2.data());
3144     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3145     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3146     ASSERT_GL_NO_ERROR();
3147 
3148     GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3149 
3150     std::thread thread = std::thread([&]() {
3151         // Create a context.
3152         EGLContext ctx1 = createMultithreadedContext(window, window->getContext());
3153         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3154 
3155         // Wait for the sync object to be signaled.
3156         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3157         ASSERT_GL_NO_ERROR();
3158 
3159         // Use the uploaded textures in the main thread.
3160         GLFramebuffer fbo;
3161         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3162 
3163         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3164         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3165 
3166         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3167         EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3168 
3169         // Destroy the context.
3170         EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3171     });
3172 
3173     thread.join();
3174 }
3175 
3176 // Test when lots of upload happens on a different thread at the same time as the main thread doing
3177 // draws.
TEST_P(MultithreadingTestES3,SimultaneousUploadAndDraw)3178 TEST_P(MultithreadingTestES3, SimultaneousUploadAndDraw)
3179 {
3180     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3181 
3182     // The following shader is used to create busy work while the worker thread is doing something.
3183     // It intentionally spreads its uniforms and inputs so the main thread has to make many GL
3184     // calls.
3185     constexpr char kBusyDrawVS[] = R"(#version 300 es
3186 uniform mediump float x0;
3187 uniform mediump float y0;
3188 uniform mediump float x1;
3189 uniform mediump float y1;
3190 
3191 in mediump float r;
3192 in mediump float g;
3193 in mediump float b;
3194 in mediump float a;
3195 
3196 out mediump vec4 color;
3197 
3198 void main()
3199 {
3200     // gl_VertexID    x    y
3201     //      0        -1   -1
3202     //      1         1   -1
3203     //      2        -1    1
3204     //      3         1    1
3205     int bit0 = gl_VertexID & 1;
3206     int bit1 = gl_VertexID >> 1;
3207     gl_Position.x = bit0 == 0 ? x0 : x1;
3208     gl_Position.y = bit1 == 0 ? y0 : y1;
3209     gl_Position.z = 0.;
3210     gl_Position.w = 1.;
3211 
3212     color = vec4(r, g, b, a);
3213 })";
3214     constexpr char kBusyDrawFS[] = R"(#version 300 es
3215 
3216 in mediump vec4 color;
3217 out mediump vec4 colorOut;
3218 
3219 void main()
3220 {
3221     colorOut = color;
3222 })";
3223 
3224     // The following shader is used to consume the results of texture uploads, ensuring appropriate
3225     // synchronization.
3226     constexpr char kTextureDrawVS[] = R"(#version 300 es
3227 out mediump vec2 uv;
3228 
3229 void main()
3230 {
3231     // gl_VertexID    x    y
3232     //      0        -1   -1
3233     //      1         1   -1
3234     //      2        -1    1
3235     //      3         1    1
3236     int bit0 = gl_VertexID & 1;
3237     int bit1 = gl_VertexID >> 1;
3238     gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1);
3239     uv = vec2(bit0, bit1);
3240 })";
3241     constexpr char kTextureDrawFS[] = R"(#version 300 es
3242 
3243 uniform mediump sampler2D s0;
3244 uniform mediump sampler2D s1;
3245 uniform mediump sampler2D s2;
3246 uniform mediump sampler2D s3;
3247 uniform mediump sampler2D s4;
3248 uniform mediump sampler2D s5;
3249 uniform mediump sampler2D s6;
3250 uniform mediump sampler2D s7;
3251 uniform mediump sampler2D s8;
3252 uniform mediump sampler2D s9;
3253 
3254 in mediump vec2 uv;
3255 out mediump vec4 colorOut;
3256 
3257 void main()
3258 {
3259     highp vec4 result = texture(s0, uv) +
3260                         texture(s1, uv) +
3261                         texture(s2, uv) +
3262                         texture(s3, uv) +
3263                         texture(s4, uv) +
3264                         texture(s5, uv) +
3265                         texture(s6, uv) +
3266                         texture(s7, uv) +
3267                         texture(s8, uv) +
3268                         texture(s9, uv);
3269     result /= 10.;
3270 
3271     colorOut = result;
3272 })";
3273 
3274     constexpr uint32_t kTextureCount = 10;
3275     GLuint textures[kTextureCount];
3276 
3277     ASSERT(IsGLExtensionEnabled("GL_KHR_texture_compression_astc_ldr") ||
3278            IsGLExtensionEnabled("GL_EXT_texture_compression_bptc"));
3279     // Note ASTC may be emulated in ANGLE, so check for BPTC first
3280     const bool hasBPTC = IsGLExtensionEnabled("GL_EXT_texture_compression_bptc");
3281     const GLenum compressedFormat =
3282         hasBPTC ? GL_COMPRESSED_RGBA_BPTC_UNORM_EXT : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
3283 
3284     std::vector<uint8_t> textureData[kTextureCount];
3285 
3286     constexpr int kSurfaceWidth  = 256;
3287     constexpr int kSurfaceHeight = 512;
3288     constexpr int kTexSize       = 1024;
3289 
3290     // Sync primitives
3291     GLsync sync = nullptr;
3292     std::mutex mutex;
3293     std::condition_variable condVar;
3294 
3295     enum class Step
3296     {
3297         Start,
3298         Thread1Ready,
3299         Thread0UploadFinish,
3300         Finish,
3301         Abort,
3302     };
3303     Step currentStep = Step::Start;
3304 
3305     // Threads to upload and draw with textures.
3306     auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3307         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3308         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3309 
3310         // Wait for the other thread to set everything up
3311         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3312 
3313         // Perform uploads while the other thread does draws
3314         for (uint32_t i = 0; i < kTextureCount; ++i)
3315         {
3316             glBindTexture(GL_TEXTURE_2D, textures[i]);
3317             glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTexSize, kTexSize, compressedFormat,
3318                                       static_cast<GLsizei>(textureData[i].size()),
3319                                       textureData[i].data());
3320         }
3321         ASSERT_GL_NO_ERROR();
3322 
3323         // Create a sync object to be used for the draw thread.
3324         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3325         ASSERT_NE(sync, nullptr);
3326         ASSERT_GL_NO_ERROR();
3327 
3328         threadSynchronization.nextStep(Step::Thread0UploadFinish);
3329         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3330     };
3331 
3332     auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3333         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3334         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3335 
3336         ANGLE_GL_PROGRAM(busyDrawProgram, kBusyDrawVS, kBusyDrawFS);
3337 
3338         // Set up the test.  Don't let the other thread work yet.
3339         glUseProgram(busyDrawProgram);
3340         GLuint busyDrawX0Loc = glGetUniformLocation(busyDrawProgram, "x0");
3341         GLuint busyDrawY0Loc = glGetUniformLocation(busyDrawProgram, "y0");
3342         GLuint busyDrawX1Loc = glGetUniformLocation(busyDrawProgram, "x1");
3343         GLuint busyDrawY1Loc = glGetUniformLocation(busyDrawProgram, "y1");
3344         GLuint busyDrawRLoc  = glGetAttribLocation(busyDrawProgram, "r");
3345         GLuint busyDrawGLoc  = glGetAttribLocation(busyDrawProgram, "g");
3346         GLuint busyDrawBLoc  = glGetAttribLocation(busyDrawProgram, "b");
3347         GLuint busyDrawALoc  = glGetAttribLocation(busyDrawProgram, "a");
3348 
3349         ANGLE_GL_PROGRAM(textureDrawProgram, kTextureDrawVS, kTextureDrawFS);
3350         GLuint textureDrawSamplerLoc[kTextureCount] = {};
3351 
3352         glUseProgram(textureDrawProgram);
3353         glGenTextures(kTextureCount, textures);
3354         for (uint32_t i = 0; i < kTextureCount; ++i)
3355         {
3356             std::ostringstream name;
3357             name << "s" << i;
3358 
3359             textureDrawSamplerLoc[i] = glGetUniformLocation(textureDrawProgram, name.str().c_str());
3360 
3361             glBindTexture(GL_TEXTURE_2D, textures[i]);
3362             glTexStorage2D(GL_TEXTURE_2D, 1, compressedFormat, kTexSize, kTexSize);
3363 
3364             // Both ASTC 4x4 and BPTC have 1 byte per pixel.  The textures' contents are arbitrary
3365             // but distinct.
3366             textureData[i].resize(kTexSize * kTexSize);
3367             for (int y = 0; y < kTexSize; ++y)
3368             {
3369                 for (int x = 0; x < kTexSize; ++x)
3370                 {
3371                     textureData[i][y * kTexSize + x] = (i * 50 + y + x) % 255;
3372                 }
3373             }
3374         }
3375         ASSERT_GL_NO_ERROR();
3376 
3377         // Now that everything is set up, let the upload thread work while this thread does draws.
3378         threadSynchronization.nextStep(Step::Thread1Ready);
3379 
3380         int w = kSurfaceWidth;
3381         int h = kSurfaceHeight;
3382 
3383         glClear(GL_COLOR_BUFFER_BIT);
3384         glViewport(0, 0, w, h);
3385         glUseProgram(busyDrawProgram);
3386         for (uint32_t y = 0; y < 8; ++y)
3387         {
3388             for (uint32_t x = 0; x < 8; ++x)
3389             {
3390                 float width  = w / 4;
3391                 float height = h / 8;
3392 
3393                 glUniform1f(busyDrawX0Loc, x * width / w - 1);
3394                 glUniform1f(busyDrawY0Loc, y * height / h);
3395                 glUniform1f(busyDrawX1Loc, (x + 1) * width / w - 1);
3396                 glUniform1f(busyDrawY1Loc, (y + 1) * height / h);
3397 
3398                 glVertexAttrib1f(busyDrawRLoc, x / 8.0f);
3399                 glVertexAttrib1f(busyDrawGLoc, y / 8.0f);
3400                 glVertexAttrib1f(busyDrawBLoc, 0);
3401                 glVertexAttrib1f(busyDrawALoc, 1);
3402 
3403                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3404             }
3405         }
3406         ASSERT_GL_NO_ERROR();
3407 
3408         // Wait for the other thread to finish with uploads.
3409         threadSynchronization.waitForStep(Step::Thread0UploadFinish);
3410 
3411         // Wait for fence and use all textures in a draw.
3412         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3413 
3414         glUseProgram(textureDrawProgram);
3415         for (uint32_t i = 0; i < kTextureCount; ++i)
3416         {
3417             glActiveTexture(GL_TEXTURE0 + i);
3418             glBindTexture(GL_TEXTURE_2D, textures[i]);
3419             glUniform1i(textureDrawSamplerLoc[i], i);
3420         }
3421         glViewport(0, 0, w, h / 2);
3422         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3423         ASSERT_GL_NO_ERROR();
3424 
3425         threadSynchronization.nextStep(Step::Finish);
3426 
3427         // Verify results
3428         for (uint32_t y = 0; y < 8; ++y)
3429         {
3430             for (uint32_t x = 0; x < 8; ++x)
3431             {
3432                 int width  = w / 8;
3433                 int height = h / 16;
3434 
3435                 EXPECT_PIXEL_COLOR_NEAR(x * width + width / 2, h - (y * height + height / 2),
3436                                         GLColor(x * 255 / 8, (7 - y) * 255 / 8, 0, 255), 1);
3437             }
3438         }
3439         ASSERT_GL_NO_ERROR();
3440 
3441         for (uint32_t x = 0; x < 8; ++x)
3442         {
3443             // The compressed data is gibberish, just ensure it's not all black.
3444             EXPECT_PIXEL_NE(x * w / 8, h / 4, 0, 0, 0, 0);
3445         }
3446         ASSERT_GL_NO_ERROR();
3447     };
3448 
3449     std::array<LockStepThreadFunc, 2> threadFuncs = {
3450         std::move(thread0Upload),
3451         std::move(thread1Draw),
3452     };
3453 
3454     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
3455                                threadFuncs.data());
3456 
3457     ASSERT_NE(currentStep, Step::Abort);
3458 }
3459 
3460 // Test that calling glUniformBlockBinding on one context affects all contexts.
TEST_P(MultithreadingTestES3,UniformBlockBinding)3461 TEST_P(MultithreadingTestES3, UniformBlockBinding)
3462 {
3463     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3464 
3465     constexpr char kVS[] = R"(#version 300 es
3466 void main()
3467 {
3468     vec2 pos = vec2(0.0);
3469     switch (gl_VertexID) {
3470         case 0: pos = vec2(-1.0, -1.0); break;
3471         case 1: pos = vec2(3.0, -1.0); break;
3472         case 2: pos = vec2(-1.0, 3.0); break;
3473     };
3474     gl_Position = vec4(pos, 0.0, 1.0);
3475 })";
3476     constexpr char kFS[] = R"(#version 300 es
3477 out mediump vec4 colorOut;
3478 
3479 layout(std140) uniform buffer { mediump vec4 color; };
3480 
3481 void main()
3482 {
3483     colorOut = color;
3484 })";
3485 
3486     GLProgram program;
3487     GLint uniformBufferIndex;
3488 
3489     // Sync primitives
3490     std::mutex mutex;
3491     std::condition_variable condVar;
3492 
3493     enum class Step
3494     {
3495         Start,
3496         Thread1Ready,
3497         Thread0BindingChanged,
3498         Thread1FinishedDrawing,
3499         Finish,
3500         Abort,
3501     };
3502     Step currentStep = Step::Start;
3503 
3504     // Threads to create programs and draw with different uniform blocks.
3505     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3506         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3507         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3508 
3509         // Create buffers bound to bindings 1 and 2
3510         constexpr std::array<float, 4> kRed              = {1, 0, 0, 1};
3511         constexpr std::array<float, 4> kTransparentGreen = {0, 1, 0, 0};
3512         GLBuffer red, transparentGreen;
3513         glBindBuffer(GL_UNIFORM_BUFFER, red);
3514         glBufferData(GL_UNIFORM_BUFFER, sizeof(kRed), kRed.data(), GL_STATIC_DRAW);
3515         glBindBuffer(GL_UNIFORM_BUFFER, transparentGreen);
3516         glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentGreen), kTransparentGreen.data(),
3517                      GL_STATIC_DRAW);
3518 
3519         glBindBufferBase(GL_UNIFORM_BUFFER, 1, transparentGreen);
3520         glBindBufferBase(GL_UNIFORM_BUFFER, 2, red);
3521 
3522         // Wait for the other thread to set everything up
3523         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3524 
3525         // Issue a draw call.  The buffer should be transparent green now
3526         glClearColor(0, 0, 0, 0);
3527         glClear(GL_COLOR_BUFFER_BIT);
3528         glEnable(GL_BLEND);
3529         glBlendFunc(GL_ONE, GL_ONE);
3530 
3531         glUseProgram(program);
3532         glDrawArrays(GL_TRIANGLES, 0, 3);
3533 
3534         // Change the binding
3535         glUniformBlockBinding(program, uniformBufferIndex, 1);
3536         ASSERT_GL_NO_ERROR();
3537 
3538         // Let the other thread work before any deferred operations for the binding change above are
3539         // processed in this context.
3540         threadSynchronization.nextStep(Step::Thread0BindingChanged);
3541         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3542 
3543         // Draw again, it should accumulate blue and the buffer should become magenta.
3544         glDrawArrays(GL_TRIANGLES, 0, 3);
3545 
3546         // Verify results
3547         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
3548         ASSERT_GL_NO_ERROR();
3549 
3550         threadSynchronization.nextStep(Step::Finish);
3551     };
3552 
3553     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3554         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3555         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3556 
3557         // Create buffers bound to bindings 1 and 2
3558         constexpr std::array<float, 4> kBlue           = {0, 0, 1, 1};
3559         constexpr std::array<float, 4> kTransparentRed = {1, 0, 0, 0};
3560         GLBuffer blue, transparentRed;
3561         glBindBuffer(GL_UNIFORM_BUFFER, blue);
3562         glBufferData(GL_UNIFORM_BUFFER, sizeof(kBlue), kBlue.data(), GL_STATIC_DRAW);
3563         glBindBuffer(GL_UNIFORM_BUFFER, transparentRed);
3564         glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentRed), kTransparentRed.data(),
3565                      GL_STATIC_DRAW);
3566 
3567         glBindBufferBase(GL_UNIFORM_BUFFER, 1, blue);
3568         glBindBufferBase(GL_UNIFORM_BUFFER, 2, transparentRed);
3569 
3570         // Create the program
3571         program.makeRaster(kVS, kFS);
3572         glUseProgram(program);
3573         uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
3574 
3575         // Configure the buffer binding to binding 2
3576         glUniformBlockBinding(program, uniformBufferIndex, 2);
3577         ASSERT_GL_NO_ERROR();
3578 
3579         // Issue a draw call.  The buffer should be transparent red now
3580         glClearColor(0, 0, 0, 0);
3581         glClear(GL_COLOR_BUFFER_BIT);
3582         glEnable(GL_BLEND);
3583         glBlendFunc(GL_ONE, GL_ONE);
3584 
3585         glDrawArrays(GL_TRIANGLES, 0, 3);
3586 
3587         // Now that everything is set up, let the other thread continue
3588         threadSynchronization.nextStep(Step::Thread1Ready);
3589         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0BindingChanged));
3590 
3591         // The other thread has changed the binding.  Draw again, it should accumulate blue and the
3592         // buffer should become magenta.
3593         glDrawArrays(GL_TRIANGLES, 0, 3);
3594 
3595         // Verify results
3596         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
3597         ASSERT_GL_NO_ERROR();
3598 
3599         // Tell the other thread to finish up.
3600         threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3601         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3602     };
3603 
3604     std::array<LockStepThreadFunc, 2> threadFuncs = {
3605         std::move(thread0),
3606         std::move(thread1),
3607     };
3608 
3609     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3610 
3611     ASSERT_NE(currentStep, Step::Abort);
3612 }
3613 
3614 // Test that observers are notified of a change in foveation state of a texture
TEST_P(MultithreadingTestES3,SharedFoveatedTexture)3615 TEST_P(MultithreadingTestES3, SharedFoveatedTexture)
3616 {
3617     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3618     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_QCOM_texture_foveated"));
3619 
3620     // Shared texture
3621     GLTexture texture;
3622 
3623     // Sync primitives
3624     std::mutex mutex;
3625     std::condition_variable condVar;
3626 
3627     enum class Step
3628     {
3629         Start,
3630         Thread0Draw,
3631         Thread1Draw,
3632         Thread0ConfiguredTextureFoveation,
3633         Finish,
3634         Abort,
3635     };
3636     Step currentStep = Step::Start;
3637 
3638     // Thread to configure texture foveation.
3639     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3640         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3641         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3642 
3643         // Create non-foveated framebuffer and attach shared texture as color attachment
3644         GLFramebuffer framebuffer;
3645         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3646         glActiveTexture(GL_TEXTURE0);
3647         glBindTexture(GL_TEXTURE_2D, texture);
3648         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
3649                      GL_UNSIGNED_BYTE, nullptr);
3650         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3651         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3652         EXPECT_GL_NO_ERROR();
3653         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3654         ASSERT_GL_NO_ERROR();
3655         EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3656 
3657         // Render before configuring foveation on the texture
3658         ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
3659         glUseProgram(greenProgram);
3660 
3661         // Draw
3662         drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
3663         EXPECT_GL_NO_ERROR();
3664 
3665         // Verify results
3666         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
3667         ASSERT_GL_NO_ERROR();
3668 
3669         threadSynchronization.nextStep(Step::Thread0Draw);
3670         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
3671 
3672         // Configure foveation for the texture
3673         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM,
3674                         GL_FOVEATION_ENABLE_BIT_QCOM);
3675         EXPECT_GL_NO_ERROR();
3676         glTextureFoveationParametersQCOM(texture, 0, 0, 0.0f, 0.0f, 8.0f, 8.0f, 0.0f);
3677         EXPECT_GL_NO_ERROR();
3678 
3679         threadSynchronization.nextStep(Step::Thread0ConfiguredTextureFoveation);
3680         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3681     };
3682 
3683     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3684         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3685         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3686 
3687         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
3688 
3689         // Create non-foveated framebuffer and attach shared texture as color attachment
3690         GLFramebuffer framebuffer;
3691         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3692         glActiveTexture(GL_TEXTURE0);
3693         glBindTexture(GL_TEXTURE_2D, texture);
3694         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3695         ASSERT_GL_NO_ERROR();
3696         EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3697 
3698         // Render before configuring foveation on the texture
3699         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
3700         glUseProgram(redProgram);
3701 
3702         // Draw
3703         drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
3704         EXPECT_GL_NO_ERROR();
3705 
3706         // Verify results
3707         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3708         ASSERT_GL_NO_ERROR();
3709 
3710         threadSynchronization.nextStep(Step::Thread1Draw);
3711         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ConfiguredTextureFoveation));
3712 
3713         // Render after texture foveation was configured
3714         ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
3715         glUseProgram(blueProgram);
3716 
3717         // Draw
3718         drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
3719         EXPECT_GL_NO_ERROR();
3720 
3721         // Verify results
3722         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
3723         ASSERT_GL_NO_ERROR();
3724 
3725         threadSynchronization.nextStep(Step::Finish);
3726     };
3727 
3728     std::array<LockStepThreadFunc, 2> threadFuncs = {
3729         std::move(thread0),
3730         std::move(thread1),
3731     };
3732 
3733     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3734 
3735     ASSERT_NE(currentStep, Step::Abort);
3736 }
3737 
3738 // Test GL_EXT_sRGB_write_control works as expected when multiple contexts are used
TEST_P(MultithreadingTestES3,SharedSrgbTextureMultipleContexts)3739 TEST_P(MultithreadingTestES3, SharedSrgbTextureMultipleContexts)
3740 {
3741     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3742     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB_write_control"));
3743 
3744     constexpr angle::GLColor encodedToSrgbColor(64, 127, 191, 255);
3745     constexpr angle::GLColor inputColor(13, 54, 133, 255);
3746 
3747     EGLWindow *window   = getEGLWindow();
3748     EGLDisplay dpy      = window->getDisplay();
3749     EGLContext context1 = window->createContext(EGL_NO_CONTEXT, nullptr);
3750     EGLContext context2 = window->createContext(context1, nullptr);
3751 
3752     // Shared texture
3753     GLTexture texture;
3754 
3755     // Shared program
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         Thread0Draw1,
3766         Thread1Draw1,
3767         Thread0Draw2,
3768         Thread1Draw2,
3769         Finish,
3770         Abort,
3771     };
3772     Step currentStep = Step::Start;
3773 
3774     // Thread0 rendering to shared texture.
3775     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3776         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3777 
3778         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context1));
3779         EXPECT_EGL_SUCCESS();
3780 
3781         glBindTexture(GL_TEXTURE_2D, texture);
3782         glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT,
3783                      GL_UNSIGNED_BYTE, nullptr);
3784 
3785         GLFramebuffer framebuffer;
3786         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3787         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3788 
3789         program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
3790         ASSERT_NE(0u, program);
3791 
3792         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
3793         ASSERT_NE(-1, colorLocation);
3794 
3795         glUseProgram(program);
3796         glUniform4fv(colorLocation, 1, inputColor.toNormalizedVector().data());
3797 
3798         glDisable(GL_FRAMEBUFFER_SRGB_EXT);
3799         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3800         EXPECT_PIXEL_COLOR_NEAR(0, 0, inputColor, 1.0);
3801 
3802         threadSynchronization.nextStep(Step::Thread0Draw1);
3803         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw1));
3804 
3805         glEnable(GL_FRAMEBUFFER_SRGB_EXT);
3806         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3807         EXPECT_PIXEL_COLOR_NEAR(0, 0, encodedToSrgbColor, 1.0);
3808 
3809         threadSynchronization.nextStep(Step::Thread0Draw2);
3810         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw2));
3811 
3812         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
3813         EXPECT_EGL_SUCCESS();
3814 
3815         threadSynchronization.nextStep(Step::Finish);
3816     };
3817 
3818     // Thread1 rendering to shared texture.
3819     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3820         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3821         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context2));
3822         EXPECT_EGL_SUCCESS();
3823 
3824         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw1));
3825 
3826         glBindTexture(GL_TEXTURE_2D, texture);
3827         GLFramebuffer framebuffer;
3828         glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3829         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3830 
3831         ASSERT_NE(0u, program);
3832         GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
3833         ASSERT_NE(-1, colorLocation);
3834 
3835         glUseProgram(program);
3836         glUniform4fv(colorLocation, 1, inputColor.toNormalizedVector().data());
3837 
3838         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3839         EXPECT_PIXEL_COLOR_NEAR(0, 0, encodedToSrgbColor, 1.0);
3840 
3841         threadSynchronization.nextStep(Step::Thread1Draw1);
3842         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw2));
3843 
3844         glBindTexture(GL_TEXTURE_2D, texture);
3845         glDisable(GL_FRAMEBUFFER_SRGB_EXT);
3846         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3847         EXPECT_PIXEL_COLOR_NEAR(0, 0, inputColor, 1.0);
3848 
3849         threadSynchronization.nextStep(Step::Thread1Draw2);
3850         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3851 
3852         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
3853         EXPECT_EGL_SUCCESS();
3854     };
3855 
3856     std::array<LockStepThreadFunc, 2> threadFuncs = {
3857         std::move(thread0),
3858         std::move(thread1),
3859     };
3860 
3861     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3862 
3863     // Cleanup
3864     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context1));
3865     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));
3866     EXPECT_EGL_SUCCESS();
3867 
3868     ASSERT_NE(currentStep, Step::Abort);
3869 }
3870 
3871 // Test that a program linked in one context can be bound in another context while link may be
3872 // happening in parallel.
TEST_P(MultithreadingTest,ProgramLinkAndBind)3873 TEST_P(MultithreadingTest, ProgramLinkAndBind)
3874 {
3875     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3876 
3877     GLuint vs;
3878     GLuint redfs;
3879     GLuint greenfs;
3880 
3881     GLuint program;
3882 
3883     // Sync primitives
3884     std::mutex mutex;
3885     std::condition_variable condVar;
3886 
3887     enum class Step
3888     {
3889         Start,
3890         Thread1Ready,
3891         Thread0ProgramLinked,
3892         Thread1FinishedDrawing,
3893         Finish,
3894         Abort,
3895     };
3896     Step currentStep = Step::Start;
3897 
3898     // Threads to create programs and draw.
3899     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3900         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3901         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3902 
3903         // Wait for thread 1 to bind the program before linking it
3904         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3905 
3906         glUseProgram(program);
3907 
3908         // Link a program, but don't resolve link.
3909         glDetachShader(program, greenfs);
3910         glAttachShader(program, redfs);
3911         glLinkProgram(program);
3912 
3913         // Let the other thread bind and use the program.
3914         threadSynchronization.nextStep(Step::Thread0ProgramLinked);
3915         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3916 
3917         // Draw in this context too
3918         drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3919 
3920         // Verify results
3921         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3922         ASSERT_GL_NO_ERROR();
3923 
3924         threadSynchronization.nextStep(Step::Finish);
3925     };
3926 
3927     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3928         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
3929         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3930 
3931         vs      = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
3932         redfs   = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Red());
3933         greenfs = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Green());
3934         program = glCreateProgram();
3935 
3936         glAttachShader(program, vs);
3937         glAttachShader(program, greenfs);
3938         glLinkProgram(program);
3939         ASSERT_NE(CheckLinkStatusAndReturnProgram(program, true), 0u);
3940 
3941         // Bind the program before it's relinked.  Otherwise the program is resolved before the
3942         // binding happens.
3943         glUseProgram(program);
3944 
3945         threadSynchronization.nextStep(Step::Thread1Ready);
3946         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ProgramLinked));
3947 
3948         // Unbind and rebind for extra testing
3949         glUseProgram(0);
3950         glUseProgram(program);
3951 
3952         // Issue a draw call
3953         drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3954 
3955         // Verify results
3956         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3957         ASSERT_GL_NO_ERROR();
3958 
3959         // Tell the other thread to finish up.
3960         threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3961         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3962     };
3963 
3964     std::array<LockStepThreadFunc, 2> threadFuncs = {
3965         std::move(thread0),
3966         std::move(thread1),
3967     };
3968 
3969     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3970 
3971     ASSERT_NE(currentStep, Step::Abort);
3972 }
3973 
3974 // Test that two contexts in share group can generate, delete and bind buffers for themselves in
3975 // parallel.
TEST_P(MultithreadingTestES3,SimultaneousBufferBindAndGen)3976 TEST_P(MultithreadingTestES3, SimultaneousBufferBindAndGen)
3977 {
3978     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3979 
3980     constexpr char kFS[] = R"(#version 300 es
3981 precision mediump float;
3982 
3983 layout(std140) uniform Block
3984 {
3985     vec4 colorIn;
3986 };
3987 
3988 out vec4 color;
3989 
3990 void main()
3991 {
3992     color = colorIn;
3993 })";
3994 
3995     constexpr int kSurfaceWidth  = 32;
3996     constexpr int kSurfaceHeight = 128;
3997 
3998     // Sync primitives
3999     std::mutex mutex;
4000     std::condition_variable condVar;
4001 
4002     enum class Step
4003     {
4004         Start,
4005         Thread0Ready,
4006         Thread1Ready,
4007         Finish,
4008         Abort,
4009     };
4010     Step currentStep = Step::Start;
4011 
4012     auto threadFunc = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context, uint32_t index) {
4013         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
4014         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4015 
4016         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4017 
4018         // Make sure the two threads start work around the same time
4019         if (index == 0)
4020         {
4021             threadSynchronization.nextStep(Step::Thread0Ready);
4022             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
4023         }
4024         else
4025         {
4026             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
4027             threadSynchronization.nextStep(Step::Thread1Ready);
4028         }
4029 
4030         std::vector<GLuint> buffers(kSurfaceWidth * kSurfaceHeight);
4031 
4032         glEnable(GL_SCISSOR_TEST);
4033         for (int y = 0; y < kSurfaceHeight; ++y)
4034         {
4035             for (int x = 0; x < kSurfaceWidth; ++x)
4036             {
4037                 GLuint &buffer            = buffers[y * kSurfaceWidth + x];
4038                 const float bufferData[4] = {
4039                     ((y * kSurfaceWidth + x + index * 100) % 255) / 255.0f,
4040                     ((y * kSurfaceWidth + x + index * 100 + 1) % 255) / 255.0f,
4041                     ((y * kSurfaceWidth + x + index * 100 + 2) % 255) / 255.0f,
4042                     ((y * kSurfaceWidth + x + index * 100 + 3) % 255) / 255.0f,
4043                 };
4044 
4045                 // Generate one buffer per pixel and shade the pixel with it.
4046                 glGenBuffers(1, &buffer);
4047                 glBindBuffer(GL_UNIFORM_BUFFER, buffers[y * kSurfaceWidth + x]);
4048                 glBufferData(GL_UNIFORM_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
4049                 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
4050 
4051                 glScissor(x, y, 1, 1);
4052                 drawQuad(program, essl3_shaders::PositionAttrib(), 0);
4053 
4054                 if ((x + y) % 2 == 0)
4055                 {
4056                     glDeleteBuffers(1, &buffer);
4057                     buffer = 0;
4058                 }
4059             }
4060         }
4061 
4062         // Verify the results
4063         auto verify = [&](int x, int y) {
4064             const GLColor expect((y * kSurfaceWidth + x + index * 100) % 255,
4065                                  (y * kSurfaceWidth + x + index * 100 + 1) % 255,
4066                                  (y * kSurfaceWidth + x + index * 100 + 2) % 255,
4067                                  (y * kSurfaceWidth + x + index * 100 + 3) % 255);
4068             EXPECT_PIXEL_COLOR_EQ(x, y, expect);
4069         };
4070 
4071         verify(0, 0);
4072         verify(0, kSurfaceHeight - 1);
4073         verify(kSurfaceWidth - 1, 0);
4074         verify(kSurfaceWidth - 1, kSurfaceHeight - 1);
4075         verify(kSurfaceWidth / 2, kSurfaceHeight / 2);
4076         ASSERT_GL_NO_ERROR();
4077 
4078         if (index == 0)
4079         {
4080             threadSynchronization.nextStep(Step::Finish);
4081         }
4082         else
4083         {
4084             ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
4085         }
4086     };
4087 
4088     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4089         threadFunc(dpy, surface, context, 0);
4090     };
4091     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4092         threadFunc(dpy, surface, context, 1);
4093     };
4094 
4095     std::array<LockStepThreadFunc, 2> threadFuncs = {
4096         std::move(thread0),
4097         std::move(thread1),
4098     };
4099 
4100     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
4101                                threadFuncs.data());
4102 
4103     ASSERT_NE(currentStep, Step::Abort);
4104 }
4105 
4106 // Test that ref counting is thread-safe when the same buffer is used in multiple threads.
TEST_P(MultithreadingTestES3,SimultaneousBufferBind)4107 TEST_P(MultithreadingTestES3, SimultaneousBufferBind)
4108 {
4109     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
4110 
4111     constexpr char kFS[] = R"(#version 300 es
4112 precision mediump float;
4113 
4114 layout(std140) uniform Block
4115 {
4116     vec4 colorIn;
4117 };
4118 
4119 out vec4 color;
4120 
4121 void main()
4122 {
4123     color = colorIn;
4124 })";
4125 
4126     constexpr int kSurfaceWidth  = 32;
4127     constexpr int kSurfaceHeight = 128;
4128 
4129     GLuint buffer;
4130     GLsync sync = nullptr;
4131 
4132     // Sync primitives
4133     std::mutex mutex;
4134     std::condition_variable condVar;
4135 
4136     enum class Step
4137     {
4138         Start,
4139         Thread0Ready,
4140         Thread1Ready,
4141         Finish,
4142         Abort,
4143     };
4144     Step currentStep = Step::Start;
4145 
4146     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4147         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
4148         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4149 
4150         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4151 
4152         // Create the buffer in this context
4153         glGenBuffers(1, &buffer);
4154 
4155         constexpr float kBufferData[4] = {
4156             10.0f / 255.0f,
4157             50.0f / 255.0f,
4158             130.0f / 255.0f,
4159             220.0f / 255.0f,
4160         };
4161         glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4162         glBufferData(GL_UNIFORM_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW);
4163 
4164         sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
4165 
4166         // Make sure the two threads start work around the same time
4167         threadSynchronization.nextStep(Step::Thread0Ready);
4168         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
4169 
4170         // Bind and unbind the buffer many times.  If ref counting is not thread safe, chances are
4171         // the ref count would be incorrect in the end.  This can result in the buffer prematurely
4172         // getting deleted.
4173         for (uint32_t i = 0; i < 8000; ++i)
4174         {
4175             glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? 0 : buffer);
4176         }
4177         ASSERT_GL_NO_ERROR();
4178 
4179         threadSynchronization.nextStep(Step::Finish);
4180     };
4181     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4182         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
4183         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4184 
4185         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4186 
4187         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
4188         threadSynchronization.nextStep(Step::Thread1Ready);
4189 
4190         glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
4191 
4192         // Bind and unbind the buffer many times.
4193         for (uint32_t i = 0; i < 4000; ++i)
4194         {
4195             glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? buffer : 0);
4196         }
4197 
4198         // Draw with it to make sure buffer is still valid and not accidentally deleted due to bad
4199         // ref counting.
4200         glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4201         glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
4202         drawQuad(program, essl3_shaders::PositionAttrib(), 0);
4203 
4204         // Verify the results
4205         const GLColor expect(10, 50, 130, 220);
4206         EXPECT_PIXEL_RECT_EQ(0, 0, kSurfaceWidth, kSurfaceHeight, expect);
4207         ASSERT_GL_NO_ERROR();
4208 
4209         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
4210     };
4211 
4212     std::array<LockStepThreadFunc, 2> threadFuncs = {
4213         std::move(thread0),
4214         std::move(thread1),
4215     };
4216 
4217     RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
4218                                threadFuncs.data());
4219 
4220     ASSERT_NE(currentStep, Step::Abort);
4221 }
4222 ANGLE_INSTANTIATE_TEST(
4223     MultithreadingTest,
4224     ES2_METAL(),
4225     ES3_METAL(),
4226     ES2_OPENGL(),
4227     ES3_OPENGL(),
4228     ES2_OPENGLES(),
4229     ES3_OPENGLES(),
4230     ES3_VULKAN(),
4231     ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4232     ES3_VULKAN_SWIFTSHADER()
4233         .enable(Feature::AsyncCommandQueue)
4234         .enable(Feature::SlowAsyncCommandQueueForTesting),
4235     ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4236     ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4237     ES3_VULKAN_SWIFTSHADER()
4238         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4239         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4240     ES3_VULKAN_SWIFTSHADER()
4241         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4242         .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4243     ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4244     ES3_VULKAN_SWIFTSHADER()
4245         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4246         .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4247     ES3_VULKAN_SWIFTSHADER()
4248         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4249         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4250         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4251     ES2_D3D11(),
4252     ES3_D3D11());
4253 
4254 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultithreadingTestES3);
4255 ANGLE_INSTANTIATE_TEST(
4256     MultithreadingTestES3,
4257     ES3_OPENGL(),
4258     ES3_OPENGLES(),
4259     ES3_VULKAN(),
4260     ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4261     ES3_VULKAN_SWIFTSHADER()
4262         .enable(Feature::AsyncCommandQueue)
4263         .enable(Feature::SlowAsyncCommandQueueForTesting),
4264     ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4265     ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4266     ES3_VULKAN_SWIFTSHADER()
4267         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4268         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4269     ES3_VULKAN_SWIFTSHADER()
4270         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4271         .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4272     ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4273     ES3_VULKAN_SWIFTSHADER()
4274         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4275         .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4276     ES3_VULKAN_SWIFTSHADER()
4277         .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4278         .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4279         .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4280     ES3_D3D11());
4281 
4282 }  // namespace angle
4283