1 //
2 // Copyright 2016 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 // EGLContextSharingTest.cpp:
7 // Tests relating to shared Contexts.
8
9 #include <gtest/gtest.h>
10
11 #include "test_utils/ANGLETest.h"
12 #include "test_utils/angle_test_configs.h"
13 #include "test_utils/gl_raii.h"
14 #include "util/EGLWindow.h"
15
16 #include <condition_variable>
17 #include <mutex>
18 #include <thread>
19
20 using namespace angle;
21
22 namespace
23 {
24
SafeDestroyContext(EGLDisplay display,EGLContext & context)25 EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)
26 {
27 EGLBoolean result = EGL_TRUE;
28 if (context != EGL_NO_CONTEXT)
29 {
30 result = eglDestroyContext(display, context);
31 context = EGL_NO_CONTEXT;
32 }
33 return result;
34 }
35
36 class EGLContextSharingTest : public ANGLETest
37 {
38 public:
EGLContextSharingTest()39 EGLContextSharingTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}
40
testTearDown()41 void testTearDown() override
42 {
43 glDeleteTextures(1, &mTexture);
44
45 EGLDisplay display = getEGLWindow()->getDisplay();
46
47 if (display != EGL_NO_DISPLAY)
48 {
49 for (auto &context : mContexts)
50 {
51 SafeDestroyContext(display, context);
52 }
53 }
54
55 // Set default test state to not give an error on shutdown.
56 getEGLWindow()->makeCurrent();
57 }
58
59 EGLContext mContexts[2];
60 GLuint mTexture;
61 };
62
63 // Tests that creating resources works after freeing the share context.
TEST_P(EGLContextSharingTest,BindTextureAfterShareContextFree)64 TEST_P(EGLContextSharingTest, BindTextureAfterShareContextFree)
65 {
66 EGLDisplay display = getEGLWindow()->getDisplay();
67 EGLConfig config = getEGLWindow()->getConfig();
68 EGLSurface surface = getEGLWindow()->getSurface();
69
70 const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
71 getEGLWindow()->getClientMajorVersion(), EGL_NONE};
72
73 mContexts[0] = eglCreateContext(display, config, nullptr, contextAttribs);
74 ASSERT_EGL_SUCCESS();
75 ASSERT_TRUE(mContexts[0] != EGL_NO_CONTEXT);
76 mContexts[1] = eglCreateContext(display, config, mContexts[1], contextAttribs);
77 ASSERT_EGL_SUCCESS();
78 ASSERT_TRUE(mContexts[1] != EGL_NO_CONTEXT);
79
80 ASSERT_EGL_TRUE(SafeDestroyContext(display, mContexts[0]));
81 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
82 ASSERT_EGL_SUCCESS();
83
84 glGenTextures(1, &mTexture);
85 glBindTexture(GL_TEXTURE_2D, mTexture);
86 ASSERT_GL_NO_ERROR();
87 }
88
89 // Tests the creation of contexts using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupContextCreation)90 TEST_P(EGLContextSharingTest, DisplayShareGroupContextCreation)
91 {
92 EGLDisplay display = getEGLWindow()->getDisplay();
93 EGLConfig config = getEGLWindow()->getConfig();
94
95 const EGLint inShareGroupContextAttribs[] = {
96 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
97
98 // Test creating two contexts in the global share group
99 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
100 mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);
101
102 if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
103 {
104 // Make sure an error is generated and early-exit
105 ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
106 ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);
107 return;
108 }
109
110 ASSERT_EGL_SUCCESS();
111
112 ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
113 ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
114 eglDestroyContext(display, mContexts[0]);
115
116 // Try creating a context that is not in the global share group but tries to share with a
117 // context that is
118 const EGLint notInShareGroupContextAttribs[] = {
119 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};
120 mContexts[1] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);
121 ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
122 ASSERT_TRUE(mContexts[1] == EGL_NO_CONTEXT);
123 }
124
125 // Tests the sharing of textures using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupObjectSharing)126 TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)
127 {
128 EGLDisplay display = getEGLWindow()->getDisplay();
129 if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
130 {
131 std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
132 << std::endl;
133 return;
134 }
135
136 EGLConfig config = getEGLWindow()->getConfig();
137 EGLSurface surface = getEGLWindow()->getSurface();
138
139 const EGLint inShareGroupContextAttribs[] = {
140 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
141
142 // Create two contexts in the global share group but not in the same context share group
143 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
144 mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
145
146 ASSERT_EGL_SUCCESS();
147
148 ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
149 ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
150
151 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
152 ASSERT_EGL_SUCCESS();
153
154 // Create a texture and buffer in ctx 0
155 GLuint textureFromCtx0 = 0;
156 glGenTextures(1, &textureFromCtx0);
157 glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
158 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
159 glBindTexture(GL_TEXTURE_2D, 0);
160 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
161
162 GLuint bufferFromCtx0 = 0;
163 glGenBuffers(1, &bufferFromCtx0);
164 glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);
165 glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);
166 glBindBuffer(GL_ARRAY_BUFFER, 0);
167 ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
168
169 ASSERT_GL_NO_ERROR();
170
171 // Switch to context 1 and verify that the texture is accessible but the buffer is not
172 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
173 ASSERT_EGL_SUCCESS();
174
175 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
176
177 ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));
178 glDeleteBuffers(1, &bufferFromCtx0);
179 ASSERT_GL_NO_ERROR();
180
181 // Call readpixels on the texture to verify that the backend has proper support
182 GLuint fbo = 0;
183 glGenFramebuffers(1, &fbo);
184 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
185 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);
186
187 GLubyte pixel[4];
188 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
189 ASSERT_GL_NO_ERROR();
190
191 glDeleteFramebuffers(1, &fbo);
192
193 glDeleteTextures(1, &textureFromCtx0);
194 ASSERT_GL_NO_ERROR();
195 ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
196
197 // Switch back to context 0 and delete the buffer
198 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
199 ASSERT_EGL_SUCCESS();
200
201 ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
202 glDeleteBuffers(1, &bufferFromCtx0);
203 ASSERT_GL_NO_ERROR();
204 }
205
206 // Tests that shared textures using EGL_ANGLE_display_texture_share_group are released when the last
207 // context is destroyed
TEST_P(EGLContextSharingTest,DisplayShareGroupReleasedWithLastContext)208 TEST_P(EGLContextSharingTest, DisplayShareGroupReleasedWithLastContext)
209 {
210 EGLDisplay display = getEGLWindow()->getDisplay();
211 if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
212 {
213 std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
214 << std::endl;
215 return;
216 }
217
218 EGLConfig config = getEGLWindow()->getConfig();
219 EGLSurface surface = getEGLWindow()->getSurface();
220
221 const EGLint inShareGroupContextAttribs[] = {
222 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
223
224 // Create two contexts in the global share group but not in the same context share group
225 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
226 mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
227
228 // Create a texture and buffer in ctx 0
229 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
230 GLuint textureFromCtx0 = 0;
231 glGenTextures(1, &textureFromCtx0);
232 glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
233 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
234 glBindTexture(GL_TEXTURE_2D, 0);
235 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
236
237 // Switch to context 1 and verify that the texture is accessible
238 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
239 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
240
241 // Destroy both contexts, the texture should be cleaned up automatically
242 ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[0]));
243 ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[1]));
244
245 // Create a new context and verify it cannot access the texture previously created
246 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
247
248 ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
249 }
250
251 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
252 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,TextureLifetime)253 TEST_P(EGLContextSharingTest, TextureLifetime)
254 {
255 EGLWindow *eglWindow = getEGLWindow();
256 EGLConfig config = getEGLWindow()->getConfig();
257 EGLDisplay display = getEGLWindow()->getDisplay();
258
259 // Create a pbuffer surface for use with a shared context.
260 EGLSurface surface = eglWindow->getSurface();
261 EGLContext mainContext = eglWindow->getContext();
262
263 // Initialize a shared context.
264 mContexts[0] = eglCreateContext(display, config, mainContext, nullptr);
265 ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
266
267 // Create a Texture on the shared context.
268 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
269
270 constexpr GLsizei kTexSize = 2;
271 const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
272 GLColor::yellow};
273 GLTexture tex;
274 glBindTexture(GL_TEXTURE_2D, tex);
275 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
276 kTexData);
277 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
278 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
279
280 // Make the main Context current and draw with the texture.
281 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
282
283 glBindTexture(GL_TEXTURE_2D, tex);
284 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
285 glUseProgram(program);
286
287 // No uniform update because the update seems to hide the error on Vulkan.
288
289 // Enqueue the draw call.
290 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
291 EXPECT_GL_NO_ERROR();
292
293 // Delete the texture in the main context to orphan it.
294 // Do not read back the data to keep the commands in the graph.
295 tex.reset();
296
297 // Bind and delete the test context. This should trigger texture garbage collection.
298 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
299 SafeDestroyContext(display, mContexts[0]);
300
301 // Bind the main context to clean up the test.
302 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
303 }
304
305 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
306 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,SamplerLifetime)307 TEST_P(EGLContextSharingTest, SamplerLifetime)
308 {
309 EGLWindow *eglWindow = getEGLWindow();
310 EGLConfig config = getEGLWindow()->getConfig();
311 EGLDisplay display = getEGLWindow()->getDisplay();
312
313 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
314 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_KHR_create_context"));
315
316 // Create a pbuffer surface for use with a shared context.
317 EGLSurface surface = eglWindow->getSurface();
318 EGLContext mainContext = eglWindow->getContext();
319
320 std::vector<EGLint> contextAttributes;
321 contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
322 contextAttributes.push_back(getClientMajorVersion());
323 contextAttributes.push_back(EGL_NONE);
324
325 // Initialize a shared context.
326 mContexts[0] = eglCreateContext(display, config, mainContext, contextAttributes.data());
327 ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
328
329 // Create a Texture on the shared context. Also create a Sampler object.
330 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
331
332 constexpr GLsizei kTexSize = 2;
333 const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
334 GLColor::yellow};
335 GLTexture tex;
336 glBindTexture(GL_TEXTURE_2D, tex);
337 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
338 kTexData);
339
340 GLSampler sampler;
341 glBindSampler(0, sampler);
342 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
343 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
344
345 // Make the main Context current and draw with the texture and sampler.
346 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
347
348 glBindTexture(GL_TEXTURE_2D, tex);
349 glBindSampler(0, sampler);
350 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
351 glUseProgram(program);
352
353 // No uniform update because the update seems to hide the error on Vulkan.
354
355 // Enqueue the draw call.
356 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
357 EXPECT_GL_NO_ERROR();
358
359 // Delete the texture and sampler in the main context to orphan them.
360 // Do not read back the data to keep the commands in the graph.
361 tex.reset();
362 sampler.reset();
363
364 // Bind and delete the test context. This should trigger texture garbage collection.
365 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
366 SafeDestroyContext(display, mContexts[0]);
367
368 // Bind the main context to clean up the test.
369 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
370 }
371
372 // Test that deleting an object reading from a shared object in one context doesn't cause the other
373 // context to crash. Mostly focused on the Vulkan back-end where we track resource dependencies in
374 // a graph.
TEST_P(EGLContextSharingTest,DeleteReaderOfSharedTexture)375 TEST_P(EGLContextSharingTest, DeleteReaderOfSharedTexture)
376 {
377 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
378
379 // Initialize contexts
380 EGLWindow *window = getEGLWindow();
381 EGLDisplay dpy = window->getDisplay();
382 EGLConfig config = window->getConfig();
383
384 constexpr size_t kThreadCount = 2;
385 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
386 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
387
388 EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
389
390 for (size_t t = 0; t < kThreadCount; ++t)
391 {
392 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
393 EXPECT_EGL_SUCCESS();
394
395 ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0]);
396 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
397 }
398
399 // Initialize test resources. They are done outside the threads to reduce the sources of
400 // errors and thus deadlock-free teardown.
401 ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
402
403 // Shared texture to read from.
404 constexpr GLsizei kTexSize = 1;
405 const GLColor kTexData = GLColor::red;
406
407 GLTexture sharedTex;
408 glBindTexture(GL_TEXTURE_2D, sharedTex);
409 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
410 &kTexData);
411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
412 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
413
414 // Resources for each context.
415 GLRenderbuffer renderbuffer[kThreadCount];
416 GLFramebuffer fbo[kThreadCount];
417 GLProgram program[kThreadCount];
418
419 for (size_t t = 0; t < kThreadCount; ++t)
420 {
421 ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[t], surface[t], ctx[t]));
422
423 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[t]);
424 constexpr int kRenderbufferSize = 4;
425 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
426
427 glBindFramebuffer(GL_FRAMEBUFFER, fbo[t]);
428 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
429 renderbuffer[t]);
430
431 glBindTexture(GL_TEXTURE_2D, sharedTex);
432 program[t].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
433 ASSERT_TRUE(program[t].valid());
434 }
435
436 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
437
438 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
439 std::mutex mutex;
440 std::condition_variable condVar;
441
442 enum class Step
443 {
444 Start,
445 Thread0Draw,
446 Thread1Draw,
447 Thread0Delete,
448 Finish,
449 Abort,
450 };
451 Step currentStep = Step::Start;
452
453 // Helper functions to synchronize the threads so that the operations are executed in the
454 // specific order the test is written for.
455 auto waitForStep = [&](Step waitStep) -> bool {
456 std::unique_lock<std::mutex> lock(mutex);
457 while (currentStep != waitStep)
458 {
459 // If necessary, abort execution as the other thread has encountered a GL error.
460 if (currentStep == Step::Abort)
461 {
462 return false;
463 }
464 condVar.wait(lock);
465 }
466
467 return true;
468 };
469 auto nextStep = [&](Step newStep) {
470 {
471 std::unique_lock<std::mutex> lock(mutex);
472 currentStep = newStep;
473 }
474 condVar.notify_one();
475 };
476
477 class AbortOnFailure
478 {
479 public:
480 AbortOnFailure(Step *currentStep, std::mutex *mutex, std::condition_variable *condVar)
481 : mCurrentStep(currentStep), mMutex(mutex), mCondVar(condVar)
482 {}
483
484 ~AbortOnFailure()
485 {
486 bool isAborting = false;
487 {
488 std::unique_lock<std::mutex> lock(*mMutex);
489 isAborting = *mCurrentStep != Step::Finish;
490
491 if (isAborting)
492 {
493 *mCurrentStep = Step::Abort;
494 }
495 }
496 mCondVar->notify_all();
497 }
498
499 private:
500 Step *mCurrentStep;
501 std::mutex *mMutex;
502 std::condition_variable *mCondVar;
503 };
504
505 std::thread deletingThread = std::thread([&]() {
506 AbortOnFailure abortOnFailure(¤tStep, &mutex, &condVar);
507
508 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
509 EXPECT_EGL_SUCCESS();
510
511 ASSERT_TRUE(waitForStep(Step::Start));
512
513 // Draw using the shared texture.
514 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
515
516 // Wait for the other thread to also draw using the shared texture.
517 nextStep(Step::Thread0Draw);
518 ASSERT_TRUE(waitForStep(Step::Thread1Draw));
519
520 // Delete this thread's framebuffer (reader of the shared texture).
521 fbo[0].reset();
522
523 // Flush to make sure the graph nodes associated with this context are deleted.
524 glFlush();
525
526 // Wait for the other thread to use the shared texture again before unbinding the
527 // context (so no implicit flush happens).
528 nextStep(Step::Thread0Delete);
529 ASSERT_TRUE(waitForStep(Step::Finish));
530
531 EXPECT_GL_NO_ERROR();
532 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
533 EXPECT_EGL_SUCCESS();
534 });
535
536 std::thread continuingThread = std::thread([&]() {
537 AbortOnFailure abortOnFailure(¤tStep, &mutex, &condVar);
538
539 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
540 EXPECT_EGL_SUCCESS();
541
542 // Wait for first thread to draw using the shared texture.
543 ASSERT_TRUE(waitForStep(Step::Thread0Draw));
544
545 // Draw using the shared texture.
546 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
547
548 // Wait for the other thread to delete its framebuffer.
549 nextStep(Step::Thread1Draw);
550 ASSERT_TRUE(waitForStep(Step::Thread0Delete));
551
552 // Write to the shared texture differently, so a dependency is created from the previous
553 // readers of the shared texture (the two framebuffers of the two threads) to the new
554 // commands being recorded for the shared texture.
555 //
556 // If the backend attempts to create a dependency from nodes associated with the
557 // previous readers of the texture to the new node that will contain the following
558 // commands, there will be a use-after-free error.
559 const GLColor kTexData2 = GLColor::green;
560 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
561 &kTexData2);
562 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
563
564 nextStep(Step::Finish);
565
566 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
567 EXPECT_EGL_SUCCESS();
568 });
569
570 deletingThread.join();
571 continuingThread.join();
572
573 ASSERT_NE(currentStep, Step::Abort);
574
575 // Clean up
576 for (size_t t = 0; t < kThreadCount; ++t)
577 {
578 eglDestroySurface(dpy, surface[t]);
579 eglDestroyContext(dpy, ctx[t]);
580 }
581 }
582 } // anonymous namespace
583
584 ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,
585 ES2_D3D9(),
586 ES2_D3D11(),
587 ES3_D3D11(),
588 ES2_OPENGL(),
589 ES3_OPENGL(),
590 ES2_VULKAN(),
591 ES3_VULKAN());
592