• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&currentStep, &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(&currentStep, &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