xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLMultiContextTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2021 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 // EGLMultiContextTest.cpp:
7 //   Tests relating to multiple non-shared Contexts.
8 
9 #include <gtest/gtest.h>
10 
11 #include "test_utils/ANGLETest.h"
12 #include "test_utils/MultiThreadSteps.h"
13 #include "test_utils/angle_test_configs.h"
14 #include "test_utils/gl_raii.h"
15 #include "util/EGLWindow.h"
16 #include "util/test_utils.h"
17 
18 using namespace angle;
19 
20 namespace
21 {
22 
SafeDestroyContext(EGLDisplay display,EGLContext & context)23 EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)
24 {
25     EGLBoolean result = EGL_TRUE;
26     if (context != EGL_NO_CONTEXT)
27     {
28         result  = eglDestroyContext(display, context);
29         context = EGL_NO_CONTEXT;
30     }
31     return result;
32 }
33 
34 class EGLMultiContextTest : public ANGLETest<>
35 {
36   public:
EGLMultiContextTest()37     EGLMultiContextTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}
38 
testTearDown()39     void testTearDown() override
40     {
41         glDeleteTextures(1, &mTexture);
42 
43         EGLDisplay display = getEGLWindow()->getDisplay();
44 
45         if (display != EGL_NO_DISPLAY)
46         {
47             for (auto &context : mContexts)
48             {
49                 SafeDestroyContext(display, context);
50             }
51         }
52 
53         // Set default test state to not give an error on shutdown.
54         getEGLWindow()->makeCurrent();
55     }
56 
chooseConfig(EGLDisplay dpy,EGLConfig * config) const57     bool chooseConfig(EGLDisplay dpy, EGLConfig *config) const
58     {
59         bool result          = false;
60         EGLint count         = 0;
61         EGLint clientVersion = EGL_OPENGL_ES3_BIT;
62         EGLint attribs[]     = {EGL_RED_SIZE,
63                                 8,
64                                 EGL_GREEN_SIZE,
65                                 8,
66                                 EGL_BLUE_SIZE,
67                                 8,
68                                 EGL_ALPHA_SIZE,
69                                 8,
70                                 EGL_RENDERABLE_TYPE,
71                                 clientVersion,
72                                 EGL_SURFACE_TYPE,
73                                 EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
74                                 EGL_NONE};
75 
76         result = eglChooseConfig(dpy, attribs, config, 1, &count);
77         EXPECT_EGL_TRUE(result && (count > 0));
78         return result;
79     }
80 
createContext(EGLDisplay dpy,EGLConfig config,EGLContext * context)81     bool createContext(EGLDisplay dpy, EGLConfig config, EGLContext *context)
82     {
83         bool result      = false;
84         EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, 3, EGL_NONE};
85 
86         *context = eglCreateContext(dpy, config, nullptr, attribs);
87         result   = (*context != EGL_NO_CONTEXT);
88         EXPECT_TRUE(result);
89         return result;
90     }
91 
createPbufferSurface(EGLDisplay dpy,EGLConfig config,EGLint width,EGLint height,EGLSurface * surface)92     bool createPbufferSurface(EGLDisplay dpy,
93                               EGLConfig config,
94                               EGLint width,
95                               EGLint height,
96                               EGLSurface *surface)
97     {
98         bool result      = false;
99         EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
100 
101         *surface = eglCreatePbufferSurface(dpy, config, attribs);
102         result   = (*surface != EGL_NO_SURFACE);
103         EXPECT_TRUE(result);
104         return result;
105     }
106 
107     enum class FenceTest
108     {
109         ClientWait,
110         ServerWait,
111         GetStatus,
112     };
113     enum class FlushMethod
114     {
115         Flush,
116         Finish,
117     };
118     void testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod);
119 
120     EGLContext mContexts[2];
121     GLuint mTexture;
122 };
123 
124 // Test that calling eglDeleteContext on a context that is not current succeeds.
TEST_P(EGLMultiContextTest,TestContextDestroySimple)125 TEST_P(EGLMultiContextTest, TestContextDestroySimple)
126 {
127     EGLWindow *window = getEGLWindow();
128     EGLDisplay dpy    = window->getDisplay();
129 
130     EGLContext context1 = window->createContext(EGL_NO_CONTEXT, nullptr);
131     EGLContext context2 = window->createContext(EGL_NO_CONTEXT, nullptr);
132 
133     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, context1));
134     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));
135     EXPECT_EGL_SUCCESS();
136 
137     // Cleanup
138     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
139     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context1));
140     EXPECT_EGL_SUCCESS();
141 }
142 
143 // Test that an error is generated when using EGL objects after calling eglTerminate.
TEST_P(EGLMultiContextTest,NegativeTestAfterEglTerminate)144 TEST_P(EGLMultiContextTest, NegativeTestAfterEglTerminate)
145 {
146     EGLWindow *window = getEGLWindow();
147     EGLDisplay dpy    = window->getDisplay();
148 
149     EGLConfig config = EGL_NO_CONFIG_KHR;
150     EXPECT_TRUE(chooseConfig(dpy, &config));
151 
152     EGLContext context = EGL_NO_CONTEXT;
153     EXPECT_TRUE(createContext(dpy, config, &context));
154     ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
155 
156     EGLSurface drawSurface = EGL_NO_SURFACE;
157     EXPECT_TRUE(createPbufferSurface(dpy, config, 2560, 1080, &drawSurface));
158     ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
159 
160     EGLSurface readSurface = EGL_NO_SURFACE;
161     EXPECT_TRUE(createPbufferSurface(dpy, config, 2560, 1080, &readSurface));
162     ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
163 
164     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, drawSurface, readSurface, context));
165     EXPECT_EGL_SUCCESS();
166 
167     // Terminate the display
168     EXPECT_EGL_TRUE(eglTerminate(dpy));
169     EXPECT_EGL_SUCCESS();
170 
171     // Try to use invalid handles
172     EGLint value;
173     eglQuerySurface(dpy, drawSurface, EGL_SWAP_BEHAVIOR, &value);
174     EXPECT_EGL_ERROR(EGL_BAD_SURFACE);
175     eglQuerySurface(dpy, readSurface, EGL_HEIGHT, &value);
176     EXPECT_EGL_ERROR(EGL_BAD_SURFACE);
177 
178     // Cleanup
179     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
180     EXPECT_EGL_SUCCESS();
181     window->destroyGL();
182 }
183 
184 // Test that a compute shader running in one thread will still work when rendering is happening in
185 // another thread (with non-shared contexts).  The non-shared context will still share a Vulkan
186 // command buffer.
TEST_P(EGLMultiContextTest,ComputeShaderOkayWithRendering)187 TEST_P(EGLMultiContextTest, ComputeShaderOkayWithRendering)
188 {
189     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
190     ANGLE_SKIP_TEST_IF(!isVulkanRenderer());
191     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 || getClientMinorVersion() < 1);
192 
193     // Initialize contexts
194     EGLWindow *window = getEGLWindow();
195     EGLDisplay dpy    = window->getDisplay();
196     EGLConfig config  = window->getConfig();
197 
198     constexpr size_t kThreadCount    = 2;
199     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
200     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
201 
202     EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
203 
204     for (size_t t = 0; t < kThreadCount; ++t)
205     {
206         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
207         EXPECT_EGL_SUCCESS();
208 
209         ctx[t] = window->createContext(EGL_NO_CONTEXT, nullptr);
210         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
211     }
212 
213     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
214     std::mutex mutex;
215     std::condition_variable condVar;
216 
217     enum class Step
218     {
219         Thread0Start,
220         Thread0DispatchedCompute,
221         Thread1Drew,
222         Thread0DispatchedComputeAgain,
223         Finish,
224         Abort,
225     };
226     Step currentStep = Step::Thread0Start;
227 
228     // This first thread dispatches a compute shader.  It immediately starts.
229     std::thread deletingThread = std::thread([&]() {
230         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
231 
232         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
233         EXPECT_EGL_SUCCESS();
234 
235         // Potentially wait to be signalled to start.
236         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Start));
237 
238         // Wake up and do next step: Create, detach, and dispatch a compute shader program.
239         constexpr char kCS[]  = R"(#version 310 es
240 layout(local_size_x=1) in;
241 void main()
242 {
243 })";
244         GLuint computeProgram = glCreateProgram();
245         GLuint cs             = CompileShader(GL_COMPUTE_SHADER, kCS);
246         EXPECT_NE(0u, cs);
247 
248         glAttachShader(computeProgram, cs);
249         glDeleteShader(cs);
250         glLinkProgram(computeProgram);
251         GLint linkStatus;
252         glGetProgramiv(computeProgram, GL_LINK_STATUS, &linkStatus);
253         EXPECT_GL_TRUE(linkStatus);
254         glDetachShader(computeProgram, cs);
255         EXPECT_GL_NO_ERROR();
256         glUseProgram(computeProgram);
257 
258         glDispatchCompute(8, 4, 2);
259         EXPECT_GL_NO_ERROR();
260 
261         // Signal the second thread and wait for it to draw and flush.
262         threadSynchronization.nextStep(Step::Thread0DispatchedCompute);
263         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Drew));
264 
265         // Wake up and do next step: Dispatch the same compute shader again.
266         glDispatchCompute(8, 4, 2);
267 
268         // Signal the second thread and wait for it to draw and flush again.
269         threadSynchronization.nextStep(Step::Thread0DispatchedComputeAgain);
270         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
271 
272         // Wake up and do next step: Dispatch the same compute shader again, and force flush the
273         // underlying command buffer.
274         glDispatchCompute(8, 4, 2);
275         glFinish();
276 
277         // Clean-up and exit this thread.
278         EXPECT_GL_NO_ERROR();
279         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
280         EXPECT_EGL_SUCCESS();
281     });
282 
283     // This second thread renders.  It starts once the other thread does its first nextStep()
284     std::thread continuingThread = std::thread([&]() {
285         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
286 
287         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
288         EXPECT_EGL_SUCCESS();
289 
290         // Wait for first thread to create and dispatch a compute shader.
291         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedCompute));
292 
293         // Wake up and do next step: Create graphics resources, draw, and force flush the
294         // underlying command buffer.
295         GLTexture texture;
296         glBindTexture(GL_TEXTURE_2D, texture);
297         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
298         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
299         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
300 
301         GLRenderbuffer renderbuffer;
302         GLFramebuffer fbo;
303         glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
304         constexpr int kRenderbufferSize = 4;
305         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
306         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
307         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
308                                   renderbuffer);
309         glBindTexture(GL_TEXTURE_2D, texture);
310 
311         GLProgram graphicsProgram;
312         graphicsProgram.makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
313         ASSERT_TRUE(graphicsProgram.valid());
314 
315         drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
316         glFinish();
317 
318         // Signal the first thread and wait for it to dispatch a compute shader again.
319         threadSynchronization.nextStep(Step::Thread1Drew);
320         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DispatchedComputeAgain));
321 
322         // Wake up and do next step: Draw and force flush the underlying command buffer again.
323         drawQuad(graphicsProgram.get(), essl1_shaders::PositionAttrib(), 0.5f);
324         glFinish();
325 
326         // Signal the first thread and wait exit this thread.
327         threadSynchronization.nextStep(Step::Finish);
328 
329         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
330         EXPECT_EGL_SUCCESS();
331     });
332 
333     deletingThread.join();
334     continuingThread.join();
335 
336     ASSERT_NE(currentStep, Step::Abort);
337 
338     // Clean up
339     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
340     for (size_t t = 0; t < kThreadCount; ++t)
341     {
342         eglDestroySurface(dpy, surface[t]);
343         eglDestroyContext(dpy, ctx[t]);
344     }
345 }
346 
347 // Test that repeated EGL init + terminate with improper cleanup doesn't cause an OOM crash.
348 // To reproduce the OOM error -
349 //     1. Increase the loop count to a large number
350 //     2. Remove the call to "eglReleaseThread" in the for loop
TEST_P(EGLMultiContextTest,RepeatedEglInitAndTerminate)351 TEST_P(EGLMultiContextTest, RepeatedEglInitAndTerminate)
352 {
353     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
354 
355     // Release all resources in parent thread
356     getEGLWindow()->destroyGL();
357 
358     EGLDisplay dpy;
359     EGLSurface srf;
360     EGLContext ctx;
361     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
362                           EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, GetParam().getDeviceType(),
363                           EGL_NONE};
364 
365     for (int i = 0; i < 50; i++)  // Note: this test is fairly slow b/303089709
366     {
367         std::thread thread = std::thread([&]() {
368             dpy = eglGetPlatformDisplayEXT(
369                 EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
370             EXPECT_TRUE(dpy != EGL_NO_DISPLAY);
371             EXPECT_EGL_TRUE(eglInitialize(dpy, nullptr, nullptr));
372 
373             EGLConfig config = EGL_NO_CONFIG_KHR;
374             EXPECT_TRUE(chooseConfig(dpy, &config));
375 
376             EXPECT_TRUE(createPbufferSurface(dpy, config, 2560, 1080, &srf));
377             ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
378 
379             EXPECT_TRUE(createContext(dpy, config, &ctx));
380             EXPECT_EGL_TRUE(eglMakeCurrent(dpy, srf, srf, ctx));
381 
382             // Clear and read back to make sure thread uses context.
383             glClearColor(1.0, 0.0, 0.0, 1.0);
384             glClear(GL_COLOR_BUFFER_BIT);
385             EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
386 
387             eglTerminate(dpy);
388             EXPECT_EGL_SUCCESS();
389             eglReleaseThread();
390             EXPECT_EGL_SUCCESS();
391             dpy = EGL_NO_DISPLAY;
392             srf = EGL_NO_SURFACE;
393             ctx = EGL_NO_CONTEXT;
394         });
395 
396         thread.join();
397     }
398 }
399 
400 // Test that thread B can reuse the unterminated display created by thread A
401 // even after thread A is destroyed.
TEST_P(EGLMultiContextTest,ReuseUnterminatedDisplay)402 TEST_P(EGLMultiContextTest, ReuseUnterminatedDisplay)
403 {
404     // Release all resources in parent thread
405     getEGLWindow()->destroyGL();
406 
407     EGLDisplay dpy;
408     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
409                           EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, GetParam().getDeviceType(),
410                           EGL_NONE};
411 
412     std::thread threadA = std::thread([&]() {
413         dpy = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
414                                        reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
415         EXPECT_TRUE(dpy != EGL_NO_DISPLAY);
416         EXPECT_EGL_TRUE(eglInitialize(dpy, nullptr, nullptr));
417     });
418     threadA.join();
419 
420     std::thread threadB = std::thread([&]() {
421         EGLSurface srf;
422         EGLContext ctx;
423         EGLConfig config = EGL_NO_CONFIG_KHR;
424         // If threadA's termination caused "dpy" to be incorrectly terminated all EGL APIs below
425         // staring with eglChooseConfig(...) will error out with an EGL_NOT_INITIALIZED error.
426         EXPECT_TRUE(chooseConfig(dpy, &config));
427 
428         EXPECT_TRUE(createPbufferSurface(dpy, config, 2560, 1080, &srf));
429         ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
430 
431         EXPECT_TRUE(createContext(dpy, config, &ctx));
432         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, srf, srf, ctx));
433 
434         // Clear and read back to make sure thread uses context.
435         glClearColor(1.0, 0.0, 0.0, 1.0);
436         glClear(GL_COLOR_BUFFER_BIT);
437         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
438 
439         srf = EGL_NO_SURFACE;
440         ctx = EGL_NO_CONTEXT;
441         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, srf, srf, ctx));
442         eglTerminate(dpy);
443         EXPECT_EGL_SUCCESS();
444         EXPECT_EGL_SUCCESS();
445         dpy = EGL_NO_DISPLAY;
446     });
447     threadB.join();
448 }
449 
450 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
451 // that.  Note that only validatity of the fence operations are tested here.  The test could
452 // potentially be enhanced with EGL images similarly to how
453 // MultithreadingTestES3::testFenceWithOpenRenderPass tests correctness of synchronization through
454 // a shared texture.
testFenceWithOpenRenderPass(FenceTest test,FlushMethod flushMethod)455 void EGLMultiContextTest::testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod)
456 {
457     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
458 
459     constexpr uint32_t kWidth  = 100;
460     constexpr uint32_t kHeight = 200;
461 
462     EGLSyncKHR sync = EGL_NO_SYNC_KHR;
463 
464     std::mutex mutex;
465     std::condition_variable condVar;
466 
467     enum class Step
468     {
469         Start,
470         Thread0CreateFence,
471         Thread1WaitFence,
472         Finish,
473         Abort,
474     };
475     Step currentStep = Step::Start;
476 
477     auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
478         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
479 
480         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
481 
482         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
483 
484         // Issue a draw
485         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
486         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
487         ASSERT_GL_NO_ERROR();
488 
489         // Issue a fence.  A render pass is currently open, but it should be closed in the Vulkan
490         // backend.
491         sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
492         EXPECT_NE(sync, EGL_NO_SYNC_KHR);
493 
494         // Wait for thread 1 to wait on it.
495         threadSynchronization.nextStep(Step::Thread0CreateFence);
496         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1WaitFence));
497 
498         // Wait a little to give thread 1 time to wait on the sync object before flushing it.
499         angle::Sleep(500);
500         switch (flushMethod)
501         {
502             case FlushMethod::Flush:
503                 glFlush();
504                 break;
505             case FlushMethod::Finish:
506                 glFinish();
507                 break;
508         }
509 
510         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
511 
512         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
513 
514         // Clean up
515         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
516     };
517 
518     auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
519         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
520 
521         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
522 
523         // Wait for thread 0 to create the fence object.
524         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateFence));
525 
526         // Test access to the fence object
527         threadSynchronization.nextStep(Step::Thread1WaitFence);
528 
529         constexpr GLuint64 kTimeout = 2'000'000'000;  // 2 seconds
530         EGLint result               = EGL_CONDITION_SATISFIED_KHR;
531         switch (test)
532         {
533             case FenceTest::ClientWait:
534                 result = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
535                 break;
536             case FenceTest::ServerWait:
537                 ASSERT_TRUE(eglWaitSyncKHR(dpy, sync, 0));
538                 break;
539             case FenceTest::GetStatus:
540             {
541                 EGLint value;
542                 EXPECT_EGL_TRUE(eglGetSyncAttribKHR(dpy, sync, EGL_SYNC_STATUS_KHR, &value));
543                 if (value != EGL_SIGNALED_KHR)
544                 {
545                     result = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
546                 }
547                 break;
548             }
549         }
550         ASSERT_TRUE(result == EGL_CONDITION_SATISFIED_KHR);
551 
552         // Issue a draw
553         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
554         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
555         ASSERT_GL_NO_ERROR();
556 
557         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::green);
558 
559         // Clean up
560         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
561 
562         threadSynchronization.nextStep(Step::Finish);
563     };
564 
565     std::array<LockStepThreadFunc, 2> threadFuncs = {
566         std::move(thread0),
567         std::move(thread1),
568     };
569 
570     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
571 
572     ASSERT_NE(currentStep, Step::Abort);
573 }
574 
575 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
576 // that.
TEST_P(EGLMultiContextTest,ThreadBClientWaitBeforeThreadASyncFlush)577 TEST_P(EGLMultiContextTest, ThreadBClientWaitBeforeThreadASyncFlush)
578 {
579     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Flush);
580 }
581 
582 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
583 // that.
TEST_P(EGLMultiContextTest,ThreadBServerWaitBeforeThreadASyncFlush)584 TEST_P(EGLMultiContextTest, ThreadBServerWaitBeforeThreadASyncFlush)
585 {
586     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Flush);
587 }
588 
589 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
590 // that.
TEST_P(EGLMultiContextTest,ThreadBGetStatusBeforeThreadASyncFlush)591 TEST_P(EGLMultiContextTest, ThreadBGetStatusBeforeThreadASyncFlush)
592 {
593     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Flush);
594 }
595 
596 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
597 // that.
TEST_P(EGLMultiContextTest,ThreadBClientWaitBeforeThreadASyncFinish)598 TEST_P(EGLMultiContextTest, ThreadBClientWaitBeforeThreadASyncFinish)
599 {
600     testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Finish);
601 }
602 
603 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
604 // that.
TEST_P(EGLMultiContextTest,ThreadBServerWaitBeforeThreadASyncFinish)605 TEST_P(EGLMultiContextTest, ThreadBServerWaitBeforeThreadASyncFinish)
606 {
607     testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Finish);
608 }
609 
610 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
611 // that.
TEST_P(EGLMultiContextTest,ThreadBGetStatusBeforeThreadASyncFinish)612 TEST_P(EGLMultiContextTest, ThreadBGetStatusBeforeThreadASyncFinish)
613 {
614     testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Finish);
615 }
616 
617 // Test that thread B can submit while three other threads are waiting for GPU to finish.
TEST_P(EGLMultiContextTest,ThreadBCanSubmitWhileThreadAWaiting)618 TEST_P(EGLMultiContextTest, ThreadBCanSubmitWhileThreadAWaiting)
619 {
620     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
621     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_buffer_storage"));
622 
623     constexpr uint32_t kWidth  = 100;
624     constexpr uint32_t kHeight = 200;
625 
626     std::mutex mutex;
627     std::condition_variable condVar;
628 
629     enum class Step
630     {
631         Start,
632         ThreadAMapBufferBufferRange,
633         ThreadBSubmit,
634         Finish,
635         Abort,
636     };
637 
638     Step currentStep = Step::Start;
639     std::atomic<size_t> threadCountMakingMapBufferCall(0);
640     std::atomic<size_t> threadCountFinishedMapBufferCall(0);
641     constexpr size_t kMaxThreadCountMakingMapBufferCall = 3;
642     auto threadA = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
643         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
644         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
645 
646         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
647         // Issue a draw
648         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
649         drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
650         // Issue a compute shader write to a buffer
651         constexpr char kCS[] = R"(#version 310 es
652                                 layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
653                                 layout(std140, binding = 0) buffer block {
654                                     uvec4 data;
655                                 } outBlock;
656                                 void main()
657                                 {
658                                     outBlock.data += uvec4(1);
659                                 })";
660         ANGLE_GL_COMPUTE_PROGRAM(computeProgram, kCS);
661         glUseProgram(computeProgram);
662         constexpr std::array<uint32_t, 4> kInitData = {};
663         GLBuffer coherentBuffer;
664         glBindBuffer(GL_SHADER_STORAGE_BUFFER, coherentBuffer);
665         glBufferStorageEXT(GL_SHADER_STORAGE_BUFFER, sizeof(kInitData), kInitData.data(),
666                            GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT_EXT |
667                                GL_MAP_COHERENT_BIT_EXT);
668         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, coherentBuffer);
669         glDispatchCompute(100, 100, 100);
670         EXPECT_GL_NO_ERROR();
671 
672         // Map the buffers for read. This should trigger driver wait for GPU to finish
673         glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT_EXT);
674         glBindBuffer(GL_SHADER_STORAGE_BUFFER, coherentBuffer);
675         if (++threadCountMakingMapBufferCall == kMaxThreadCountMakingMapBufferCall)
676         {
677             threadSynchronization.nextStep(Step::ThreadAMapBufferBufferRange);
678         }
679         // Wait for thread B to start submit commands.
680         ASSERT_TRUE(threadSynchronization.waitForStep(Step::ThreadBSubmit));
681         glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(kInitData),
682                          GL_MAP_READ_BIT | GL_MAP_PERSISTENT_BIT_EXT | GL_MAP_COHERENT_BIT_EXT);
683         ASSERT_GL_NO_ERROR();
684         ++threadCountFinishedMapBufferCall;
685 
686         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
687         EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
688         // Clean up
689         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
690     };
691 
692     auto threadB = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
693         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
694         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
695 
696         // Prepare for draw and then wait until threadA is ready to wait
697         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
698         glScissor(0, 0, 1, 1);
699         ASSERT_TRUE(threadSynchronization.waitForStep(Step::ThreadAMapBufferBufferRange));
700 
701         // Test submit in a loop
702         threadSynchronization.nextStep(Step::ThreadBSubmit);
703         for (int loop = 0;
704              loop < 16 && threadCountFinishedMapBufferCall < kMaxThreadCountMakingMapBufferCall;
705              loop++)
706         {
707             drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
708             glFinish();
709             EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::green);
710         }
711 
712         // Clean up
713         ASSERT_GL_NO_ERROR();
714         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
715         threadSynchronization.nextStep(Step::Finish);
716     };
717 
718     std::array<LockStepThreadFunc, kMaxThreadCountMakingMapBufferCall + 1> threadFuncs;
719     for (size_t i = 0; i < kMaxThreadCountMakingMapBufferCall; i++)
720     {
721         threadFuncs[i] = std::move(threadA);
722     }
723     threadFuncs[kMaxThreadCountMakingMapBufferCall] = std::move(threadB);
724 
725     RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
726     ASSERT_NE(currentStep, Step::Abort);
727 }
728 
729 // Test that if there are any placeholder objects when the programs don't use any resources
730 // (such as textures), they can correctly be used in non-shared contexts (without causing
731 // double-free).
TEST_P(EGLMultiContextTest,NonSharedContextsReuseDescritorSetLayoutHandle)732 TEST_P(EGLMultiContextTest, NonSharedContextsReuseDescritorSetLayoutHandle)
733 {
734     EGLWindow *window   = getEGLWindow();
735     EGLDisplay dpy      = window->getDisplay();
736     EGLSurface surface  = window->getSurface();
737     EGLContext context1 = window->createContext(EGL_NO_CONTEXT, nullptr);
738     EGLContext context2 = window->createContext(EGL_NO_CONTEXT, nullptr);
739 
740     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context1));
741     EXPECT_EGL_SUCCESS();
742 
743     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
744     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
745     ASSERT_GL_NO_ERROR();
746 
747     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context2));
748     EXPECT_EGL_SUCCESS();
749 
750     ANGLE_GL_PROGRAM(program1, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
751     drawQuad(program1, essl1_shaders::PositionAttrib(), 0.5f);
752     ASSERT_GL_NO_ERROR();
753 
754     // Cleanup
755     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
756     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context1));
757     EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));
758     EXPECT_EGL_SUCCESS();
759 }
760 
761 }  // anonymous namespace
762 
763 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLMultiContextTest);
764 ANGLE_INSTANTIATE_TEST_ES31(EGLMultiContextTest);
765