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(¤tStep, &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(¤tStep, &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(¤tStep, &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(¤tStep, &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(¤tStep, &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(¤tStep, &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