xref: /aosp_15_r20/external/angle/src/tests/egl_tests/EGLContextSharingTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 "common/tls.h"
12 #include "test_utils/ANGLETest.h"
13 #include "test_utils/MultiThreadSteps.h"
14 #include "test_utils/angle_test_configs.h"
15 #include "test_utils/gl_raii.h"
16 #include "util/EGLWindow.h"
17 #include "util/OSWindow.h"
18 #include "util/test_utils.h"
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] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
60     GLuint mTexture;
61 };
62 
63 class EGLContextSharingTestNoFixture : public EGLContextSharingTest
64 {
65   public:
EGLContextSharingTestNoFixture()66     EGLContextSharingTestNoFixture() : EGLContextSharingTest() {}
67 
testSetUp()68     void testSetUp() override
69     {
70         mOsWindow     = OSWindow::New();
71         mMajorVersion = GetParam().majorVersion;
72     }
73 
testTearDown()74     void testTearDown() override
75     {
76         if (mDisplay != EGL_NO_DISPLAY)
77         {
78             eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
79 
80             if (mSurface != EGL_NO_SURFACE)
81             {
82                 eglDestroySurface(mDisplay, mSurface);
83                 ASSERT_EGL_SUCCESS();
84                 mSurface = EGL_NO_SURFACE;
85             }
86 
87             for (auto &context : mContexts)
88             {
89                 SafeDestroyContext(mDisplay, context);
90             }
91 
92             eglTerminate(mDisplay);
93             mDisplay = EGL_NO_DISPLAY;
94             ASSERT_EGL_SUCCESS();
95             eglReleaseThread();
96             ASSERT_EGL_SUCCESS();
97         }
98 
99         mOsWindow->destroy();
100         OSWindow::Delete(&mOsWindow);
101         ASSERT_EGL_SUCCESS() << "Error during test TearDown";
102     }
103 
chooseConfig(EGLConfig * config) const104     bool chooseConfig(EGLConfig *config) const
105     {
106         bool result          = false;
107         EGLint count         = 0;
108         EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
109         EGLint attribs[]     = {EGL_RED_SIZE,
110                                 8,
111                                 EGL_GREEN_SIZE,
112                                 8,
113                                 EGL_BLUE_SIZE,
114                                 8,
115                                 EGL_ALPHA_SIZE,
116                                 8,
117                                 EGL_RENDERABLE_TYPE,
118                                 clientVersion,
119                                 EGL_SURFACE_TYPE,
120                                 EGL_WINDOW_BIT | EGL_PBUFFER_BIT,
121                                 EGL_NONE};
122 
123         result = eglChooseConfig(mDisplay, attribs, config, 1, &count);
124         EXPECT_EGL_TRUE(result && (count > 0));
125         return result;
126     }
127 
createContext(EGLConfig config,EGLContext * context,EGLContext share_context=EGL_NO_CONTEXT)128     bool createContext(EGLConfig config,
129                        EGLContext *context,
130                        EGLContext share_context = EGL_NO_CONTEXT)
131     {
132         bool result      = false;
133         EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion,
134                             EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, mVirtualizationGroup++,
135                             EGL_NONE};
136 
137         if (!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANGLE_context_virtualization"))
138         {
139             attribs[2] = EGL_NONE;
140         }
141 
142         *context = eglCreateContext(mDisplay, config, share_context, attribs);
143         result   = (*context != EGL_NO_CONTEXT);
144         EXPECT_TRUE(result);
145         return result;
146     }
147 
createWindowSurface(EGLConfig config,EGLNativeWindowType win,EGLSurface * surface)148     bool createWindowSurface(EGLConfig config, EGLNativeWindowType win, EGLSurface *surface)
149     {
150         bool result      = false;
151         EGLint attribs[] = {EGL_NONE};
152 
153         *surface = eglCreateWindowSurface(mDisplay, config, win, attribs);
154         result   = (*surface != EGL_NO_SURFACE);
155         EXPECT_TRUE(result);
156         return result;
157     }
158 
createPbufferSurface(EGLDisplay dpy,EGLConfig config,EGLint width,EGLint height,EGLSurface * surface)159     bool createPbufferSurface(EGLDisplay dpy,
160                               EGLConfig config,
161                               EGLint width,
162                               EGLint height,
163                               EGLSurface *surface)
164     {
165         bool result      = false;
166         EGLint attribs[] = {EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE};
167 
168         *surface = eglCreatePbufferSurface(dpy, config, attribs);
169         result   = (*surface != EGL_NO_SURFACE);
170         EXPECT_TRUE(result);
171         return result;
172     }
173 
174     OSWindow *mOsWindow;
175     EGLDisplay mDisplay  = EGL_NO_DISPLAY;
176     EGLSurface mSurface  = EGL_NO_SURFACE;
177     const EGLint kWidth  = 64;
178     const EGLint kHeight = 64;
179     EGLint mMajorVersion = 0;
180     std::atomic<EGLint> mVirtualizationGroup;
181 };
182 
183 // Tests that creating resources works after freeing the share context.
TEST_P(EGLContextSharingTest,BindTextureAfterShareContextFree)184 TEST_P(EGLContextSharingTest, BindTextureAfterShareContextFree)
185 {
186     EGLDisplay display = getEGLWindow()->getDisplay();
187     EGLConfig config   = getEGLWindow()->getConfig();
188     EGLSurface surface = getEGLWindow()->getSurface();
189 
190     const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
191                                      getEGLWindow()->getClientMajorVersion(), EGL_NONE};
192 
193     mContexts[0] = eglCreateContext(display, config, nullptr, contextAttribs);
194     ASSERT_EGL_SUCCESS();
195     ASSERT_TRUE(mContexts[0] != EGL_NO_CONTEXT);
196     mContexts[1] = eglCreateContext(display, config, mContexts[1], contextAttribs);
197     ASSERT_EGL_SUCCESS();
198     ASSERT_TRUE(mContexts[1] != EGL_NO_CONTEXT);
199 
200     ASSERT_EGL_TRUE(SafeDestroyContext(display, mContexts[0]));
201     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
202     ASSERT_EGL_SUCCESS();
203 
204     glGenTextures(1, &mTexture);
205     glBindTexture(GL_TEXTURE_2D, mTexture);
206     ASSERT_GL_NO_ERROR();
207 }
208 
209 // Tests the creation of contexts using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupContextCreation)210 TEST_P(EGLContextSharingTest, DisplayShareGroupContextCreation)
211 {
212     EGLDisplay display = getEGLWindow()->getDisplay();
213     EGLConfig config   = getEGLWindow()->getConfig();
214 
215     const EGLint inShareGroupContextAttribs[] = {
216         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
217 
218     // Check whether extension's supported to avoid clearing the EGL error state
219     // after failed context creation.
220     bool extensionEnabled =
221         IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group");
222 
223     // Test creating two contexts in the global share group
224     mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
225     mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);
226 
227     if (!extensionEnabled)
228     {
229         // Make sure an error is generated and early-exit
230         ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
231         ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);
232         return;
233     }
234 
235     ASSERT_EGL_SUCCESS();
236 
237     ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
238     ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
239     eglDestroyContext(display, mContexts[0]);
240     mContexts[0] = EGL_NO_CONTEXT;
241 
242     // Try creating a context that is not in the global share group but tries to share with a
243     // context that is
244     const EGLint notInShareGroupContextAttribs[] = {
245         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};
246     mContexts[0] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);
247     ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
248     ASSERT_TRUE(mContexts[0] == EGL_NO_CONTEXT);
249 }
250 
251 // Tests the sharing of textures using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupObjectSharing)252 TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)
253 {
254     EGLDisplay display = getEGLWindow()->getDisplay();
255     ANGLE_SKIP_TEST_IF(
256         !IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"));
257 
258     EGLConfig config   = getEGLWindow()->getConfig();
259     EGLSurface surface = getEGLWindow()->getSurface();
260 
261     const EGLint inShareGroupContextAttribs[] = {
262         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
263 
264     // Create two contexts in the global share group but not in the same context share group
265     mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
266     mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
267 
268     ASSERT_EGL_SUCCESS();
269 
270     ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
271     ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
272 
273     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
274     ASSERT_EGL_SUCCESS();
275 
276     // Create a texture and buffer in ctx 0
277     GLTexture textureFromCtx0;
278     glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
279     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
280     glBindTexture(GL_TEXTURE_2D, 0);
281     ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
282 
283     GLBuffer bufferFromCtx0;
284     glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);
285     glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);
286     glBindBuffer(GL_ARRAY_BUFFER, 0);
287     ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
288 
289     ASSERT_GL_NO_ERROR();
290 
291     // Switch to context 1 and verify that the texture is accessible but the buffer is not
292     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
293     ASSERT_EGL_SUCCESS();
294 
295     ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
296 
297     ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));
298     ASSERT_GL_NO_ERROR();
299 
300     // Call readpixels on the texture to verify that the backend has proper support
301     GLFramebuffer fbo;
302     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
303     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);
304 
305     GLubyte pixel[4];
306     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
307     ASSERT_GL_NO_ERROR();
308 
309     // Switch back to context 0 and delete the buffer
310     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
311     ASSERT_EGL_SUCCESS();
312 }
313 
314 // Tests that shared textures using EGL_ANGLE_display_texture_share_group are released when the last
315 // context is destroyed
TEST_P(EGLContextSharingTest,DisplayShareGroupReleasedWithLastContext)316 TEST_P(EGLContextSharingTest, DisplayShareGroupReleasedWithLastContext)
317 {
318     EGLDisplay display = getEGLWindow()->getDisplay();
319     ANGLE_SKIP_TEST_IF(
320         !IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"));
321     EGLConfig config   = getEGLWindow()->getConfig();
322     EGLSurface surface = getEGLWindow()->getSurface();
323 
324     const EGLint inShareGroupContextAttribs[] = {
325         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
326 
327     // Create two contexts in the global share group but not in the same context share group
328     mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
329     mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
330 
331     // Create a texture and buffer in ctx 0
332     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
333     GLTexture textureFromCtx0;
334     glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
335     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
336     glBindTexture(GL_TEXTURE_2D, 0);
337     ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
338 
339     // Switch to context 1 and verify that the texture is accessible
340     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
341     ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
342 
343     // Destroy both contexts, the texture should be cleaned up automatically
344     ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[0]));
345     mContexts[0] = EGL_NO_CONTEXT;
346     ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[1]));
347     mContexts[1] = EGL_NO_CONTEXT;
348 
349     // Unmake current, so the context can be released.
350     ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
351 
352     // Create a new context and verify it cannot access the texture previously created
353     mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
354     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
355 
356     ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
357 }
358 
359 // Tests that after creating a texture using EGL_ANGLE_display_texture_share_group,
360 // and deleting the Context and the egl::ShareGroup who own a texture staged updates,
361 // the texture staged updates are flushed, and the Context and egl::ShareGroup can be destroyed
362 // successfully, and the texture can still be accessed from the global display texture share group
TEST_P(EGLContextSharingTest,DisplayShareGroupReleaseShareGroupThatOwnsStagedUpdates)363 TEST_P(EGLContextSharingTest, DisplayShareGroupReleaseShareGroupThatOwnsStagedUpdates)
364 {
365     EGLDisplay display = getEGLWindow()->getDisplay();
366     ANGLE_SKIP_TEST_IF(
367         !IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"));
368 
369     EGLConfig config   = getEGLWindow()->getConfig();
370     EGLSurface surface = getEGLWindow()->getSurface();
371 
372     const EGLint inShareGroupContextAttribs[] = {
373         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
374 
375     // Create two contexts in the global share group, but not in the same context share group
376     EGLContext context1 = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
377     EGLContext context2 = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
378 
379     // Create a texture in context1 and stage a texture update
380     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context1));
381     constexpr GLsizei kTexSize                   = 2;
382     const GLColor kBlueData[kTexSize * kTexSize] = {GLColor::blue, GLColor::blue, GLColor::blue,
383                                                     GLColor::blue};
384     GLTexture textureFromCtx0;
385     glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
386     // This will stage a texture update in context1's SharedGroup::BufferPool
387     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kBlueData);
388 
389     // Destroy context 1, this also destroys context1's SharedGroup and BufferPool
390     // The texture staged update in context1's SharedGroup BufferPool will be flushed
391     SafeDestroyContext(display, context1);
392 
393     // Switch to context2 and verify that the texture is accessible
394     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
395     ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
396 
397     // Sample from textureFromCtx0 and check it works properly
398     glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
399     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
400     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
401     ANGLE_GL_PROGRAM(program1, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
402     glUseProgram(program1);
403     drawQuad(program1, essl1_shaders::PositionAttrib(), 0.5f);
404     EXPECT_GL_NO_ERROR();
405     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
406 
407     // Destroy context2
408     eglDestroyContext(display, context2);
409 }
410 
411 // Tests that after creating a texture using EGL_ANGLE_display_texture_share_group,
412 // and use it for sampling, and then deleting the Context (which destroys shareGroup) works. If
413 // anything cached in ShareGroup, it should be handled nicely if texture can outlive ShareGroup (for
414 // example, bugs like angleproject:7466).
TEST_P(EGLContextSharingTest,DisplayShareGroupReleaseShareGroupThenDestroyTexture)415 TEST_P(EGLContextSharingTest, DisplayShareGroupReleaseShareGroupThenDestroyTexture)
416 {
417     EGLDisplay display = getEGLWindow()->getDisplay();
418     ANGLE_SKIP_TEST_IF(
419         !IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"));
420 
421     EGLConfig config   = getEGLWindow()->getConfig();
422     EGLSurface surface = getEGLWindow()->getSurface();
423 
424     const EGLint inShareGroupContextAttribs[] = {
425         EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
426 
427     // Create two contexts in the global share group, but not in the same context share group
428     EGLContext context1 = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
429     EGLContext context2 = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
430 
431     // Create a texture in context1 and stage a texture update
432     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context1));
433     constexpr GLsizei kTexSize                   = 2;
434     const GLColor kBlueData[kTexSize * kTexSize] = {GLColor::blue, GLColor::blue, GLColor::blue,
435                                                     GLColor::blue};
436     {
437         GLTexture textureFromCtx1;
438         glBindTexture(GL_TEXTURE_2D, textureFromCtx1);
439         // This will stage a texture update in context1's SharedGroup::BufferPool
440         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kBlueData);
441 
442         // Sample from textureFromCtx1 and check it works properly
443         glBindTexture(GL_TEXTURE_2D, textureFromCtx1);
444         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
445         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
446         ANGLE_GL_PROGRAM(program1, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
447         glUseProgram(program1);
448         drawQuad(program1, essl1_shaders::PositionAttrib(), 0.5f);
449         EXPECT_GL_NO_ERROR();
450         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
451 
452         // Destroy context 1, this also destroys context1's SharedGroup and BufferPool
453         // The texture staged update in context1's SharedGroup BufferPool will be flushed
454         SafeDestroyContext(display, context1);
455 
456         // Switch to context2 and verify that the texture is accessible
457         ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, context2));
458         ASSERT_GL_TRUE(glIsTexture(textureFromCtx1));
459 
460         // textureFromCtx1 should be destroyed when leaving this scope
461     }
462 
463     // Destroy context2
464     eglDestroyContext(display, context2);
465 }
466 
467 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
468 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,TextureLifetime)469 TEST_P(EGLContextSharingTest, TextureLifetime)
470 {
471     EGLWindow *eglWindow = getEGLWindow();
472     EGLConfig config     = getEGLWindow()->getConfig();
473     EGLDisplay display   = getEGLWindow()->getDisplay();
474 
475     // Create a pbuffer surface for use with a shared context.
476     EGLSurface surface     = eglWindow->getSurface();
477     EGLContext mainContext = eglWindow->getContext();
478 
479     // Initialize a shared context.
480     mContexts[0] = eglCreateContext(display, config, mainContext, nullptr);
481     ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
482 
483     // Create a Texture on the shared context.
484     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
485 
486     constexpr GLsizei kTexSize                  = 2;
487     const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
488                                                    GLColor::yellow};
489     GLTexture tex;
490     glBindTexture(GL_TEXTURE_2D, tex);
491     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
492                  kTexData);
493     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
494     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
495 
496     // Make the main Context current and draw with the texture.
497     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
498 
499     glBindTexture(GL_TEXTURE_2D, tex);
500     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
501     glUseProgram(program);
502 
503     // No uniform update because the update seems to hide the error on Vulkan.
504 
505     // Enqueue the draw call.
506     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
507     EXPECT_GL_NO_ERROR();
508 
509     // Delete the texture in the main context to orphan it.
510     // Do not read back the data to keep the commands in the graph.
511     tex.reset();
512 
513     // Bind and delete the test context. This should trigger texture garbage collection.
514     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
515     SafeDestroyContext(display, mContexts[0]);
516 
517     // Bind the main context to clean up the test.
518     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
519 }
520 
521 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
522 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,SamplerLifetime)523 TEST_P(EGLContextSharingTest, SamplerLifetime)
524 {
525     EGLWindow *eglWindow = getEGLWindow();
526     EGLConfig config     = getEGLWindow()->getConfig();
527     EGLDisplay display   = getEGLWindow()->getDisplay();
528 
529     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
530     ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_KHR_create_context"));
531 
532     // Create a pbuffer surface for use with a shared context.
533     EGLSurface surface     = eglWindow->getSurface();
534     EGLContext mainContext = eglWindow->getContext();
535 
536     std::vector<EGLint> contextAttributes;
537     contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
538     contextAttributes.push_back(getClientMajorVersion());
539     contextAttributes.push_back(EGL_NONE);
540 
541     // Initialize a shared context.
542     mContexts[0] = eglCreateContext(display, config, mainContext, contextAttributes.data());
543     ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
544 
545     // Create a Texture on the shared context. Also create a Sampler object.
546     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
547 
548     constexpr GLsizei kTexSize                  = 2;
549     const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
550                                                    GLColor::yellow};
551     GLTexture tex;
552     glBindTexture(GL_TEXTURE_2D, tex);
553     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
554                  kTexData);
555 
556     GLSampler sampler;
557     glBindSampler(0, sampler);
558     glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
559     glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
560 
561     // Make the main Context current and draw with the texture and sampler.
562     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
563 
564     glBindTexture(GL_TEXTURE_2D, tex);
565     glBindSampler(0, sampler);
566     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
567     glUseProgram(program);
568 
569     // No uniform update because the update seems to hide the error on Vulkan.
570 
571     // Enqueue the draw call.
572     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
573     EXPECT_GL_NO_ERROR();
574 
575     // Delete the texture and sampler in the main context to orphan them.
576     // Do not read back the data to keep the commands in the graph.
577     tex.reset();
578     sampler.reset();
579 
580     // Bind and delete the test context. This should trigger texture garbage collection.
581     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
582     SafeDestroyContext(display, mContexts[0]);
583 
584     // Bind the main context to clean up the test.
585     ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
586 }
587 
588 // Test that deleting an object reading from a shared object in one context doesn't cause the other
589 // context to crash.  Mostly focused on the Vulkan back-end where we track resource dependencies in
590 // a graph.
TEST_P(EGLContextSharingTest,DeleteReaderOfSharedTexture)591 TEST_P(EGLContextSharingTest, DeleteReaderOfSharedTexture)
592 {
593     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
594     // GL Fences require GLES 3.0+
595     ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
596 
597     // Initialize contexts
598     EGLWindow *window = getEGLWindow();
599     EGLDisplay dpy    = window->getDisplay();
600     EGLConfig config  = window->getConfig();
601 
602     constexpr size_t kThreadCount    = 2;
603     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
604     EGLContext ctx[kThreadCount]     = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
605 
606     EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
607 
608     for (size_t t = 0; t < kThreadCount; ++t)
609     {
610         surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
611         EXPECT_EGL_SUCCESS();
612 
613         ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], nullptr);
614         EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
615     }
616 
617     // Initialize test resources.  They are done outside the threads to reduce the sources of
618     // errors and thus deadlock-free teardown.
619     ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
620 
621     // Shared texture to read from.
622     constexpr GLsizei kTexSize = 1;
623     const GLColor kTexData     = GLColor::red;
624 
625     GLTexture sharedTex;
626     glBindTexture(GL_TEXTURE_2D, sharedTex);
627     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
628                  &kTexData);
629     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
630     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
631 
632     // Resources for each context.
633     GLRenderbuffer renderbuffer[kThreadCount];
634     GLFramebuffer fbo[kThreadCount];
635     GLProgram program[kThreadCount];
636 
637     for (size_t t = 0; t < kThreadCount; ++t)
638     {
639         ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[t], surface[t], ctx[t]));
640 
641         glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[t]);
642         constexpr int kRenderbufferSize = 4;
643         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
644 
645         glBindFramebuffer(GL_FRAMEBUFFER, fbo[t]);
646         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
647                                   renderbuffer[t]);
648 
649         glBindTexture(GL_TEXTURE_2D, sharedTex);
650         program[t].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
651         ASSERT_TRUE(program[t].valid());
652     }
653 
654     EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
655 
656     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
657     std::mutex mutex;
658     std::condition_variable condVar;
659     std::atomic<GLsync> deletingThreadSyncObj;
660     std::atomic<GLsync> continuingThreadSyncObj;
661 
662     enum class Step
663     {
664         Start,
665         Thread0Draw,
666         Thread1Draw,
667         Thread0Delete,
668         Finish,
669         Abort,
670     };
671     Step currentStep = Step::Start;
672 
673     std::thread deletingThread = std::thread([&]() {
674         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
675 
676         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
677         EXPECT_EGL_SUCCESS();
678 
679         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
680 
681         // Draw using the shared texture.
682         drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
683 
684         deletingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
685         ASSERT_GL_NO_ERROR();
686         // Force the fence to be created
687         glFlush();
688 
689         // Wait for the other thread to also draw using the shared texture.
690         threadSynchronization.nextStep(Step::Thread0Draw);
691         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
692 
693         ASSERT_TRUE(continuingThreadSyncObj != nullptr);
694         glWaitSync(continuingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
695         ASSERT_GL_NO_ERROR();
696         glDeleteSync(continuingThreadSyncObj);
697         ASSERT_GL_NO_ERROR();
698         continuingThreadSyncObj = nullptr;
699 
700         // Delete this thread's framebuffer (reader of the shared texture).
701         fbo[0].reset();
702 
703         // Wait for the other thread to use the shared texture again before unbinding the
704         // context (so no implicit flush happens).
705         threadSynchronization.nextStep(Step::Thread0Delete);
706         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
707 
708         EXPECT_GL_NO_ERROR();
709         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
710         EXPECT_EGL_SUCCESS();
711     });
712 
713     std::thread continuingThread = std::thread([&]() {
714         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
715 
716         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
717         EXPECT_EGL_SUCCESS();
718 
719         // Wait for first thread to draw using the shared texture.
720         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
721 
722         ASSERT_TRUE(deletingThreadSyncObj != nullptr);
723         glWaitSync(deletingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
724         ASSERT_GL_NO_ERROR();
725         glDeleteSync(deletingThreadSyncObj);
726         ASSERT_GL_NO_ERROR();
727         deletingThreadSyncObj = nullptr;
728 
729         // Draw using the shared texture.
730         drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
731 
732         continuingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
733         ASSERT_GL_NO_ERROR();
734         // Force the fence to be created
735         glFlush();
736 
737         // Wait for the other thread to delete its framebuffer.
738         threadSynchronization.nextStep(Step::Thread1Draw);
739         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Delete));
740 
741         // Write to the shared texture differently, so a dependency is created from the previous
742         // readers of the shared texture (the two framebuffers of the two threads) to the new
743         // commands being recorded for the shared texture.
744         //
745         // If the backend attempts to create a dependency from nodes associated with the
746         // previous readers of the texture to the new node that will contain the following
747         // commands, there will be a use-after-free error.
748         const GLColor kTexData2 = GLColor::green;
749         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
750                      &kTexData2);
751         drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
752 
753         threadSynchronization.nextStep(Step::Finish);
754 
755         EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
756         EXPECT_EGL_SUCCESS();
757     });
758 
759     deletingThread.join();
760     continuingThread.join();
761 
762     ASSERT_NE(currentStep, Step::Abort);
763 
764     // Clean up
765     for (size_t t = 0; t < kThreadCount; ++t)
766     {
767         eglDestroySurface(dpy, surface[t]);
768         eglDestroyContext(dpy, ctx[t]);
769     }
770 }
771 
772 // Tests that Context will be destroyed in thread cleanup callback and it is safe to call GLES APIs.
TEST_P(EGLContextSharingTest,ThreadCleanupCallback)773 TEST_P(EGLContextSharingTest, ThreadCleanupCallback)
774 {
775     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
776     ANGLE_SKIP_TEST_IF(!IsAndroid());
777 
778     EGLWindow *window = getEGLWindow();
779     EGLDisplay dpy    = window->getDisplay();
780     EGLConfig config  = window->getConfig();
781 
782     EGLContext context = window->createContext(EGL_NO_CONTEXT, nullptr);
783     EXPECT_NE(context, EGL_NO_CONTEXT);
784 
785     EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
786     EGLSurface surface         = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
787     EXPECT_NE(surface, EGL_NO_SURFACE);
788 
789     auto registerThreadCleanupCallback = []() {
790 #if defined(ANGLE_PLATFORM_ANDROID)
791         static pthread_once_t once = PTHREAD_ONCE_INIT;
792         static TLSIndex tlsIndex   = TLS_INVALID_INDEX;
793 
794         auto createThreadCleanupTLSIndex = []() {
795             auto threadCleanupCallback = [](void *) {
796                 // From the point of view of this test Context is still current.
797                 glFinish();
798                 // Test expects that Context will be destroyed here.
799                 eglReleaseThread();
800             };
801             tlsIndex = CreateTLSIndex(threadCleanupCallback);
802         };
803         pthread_once(&once, createThreadCleanupTLSIndex);
804         ASSERT(tlsIndex != TLS_INVALID_INDEX);
805 
806         // Set any non nullptr value.
807         SetTLSValue(tlsIndex, &once);
808 #endif
809     };
810 
811     std::thread thread([&]() {
812         registerThreadCleanupCallback();
813 
814         eglMakeCurrent(dpy, surface, surface, context);
815         ASSERT_EGL_SUCCESS();
816 
817         // Clear and read back to make sure thread uses context.
818         glClearColor(1.0, 0.0, 0.0, 1.0);
819         glClear(GL_COLOR_BUFFER_BIT);
820         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
821 
822         // Destroy context and surface while still current.
823         eglDestroyContext(dpy, context);
824         ASSERT_EGL_SUCCESS();
825         eglDestroySurface(dpy, surface);
826         ASSERT_EGL_SUCCESS();
827     });
828 
829     thread.join();
830 
831     // Check if context was actually destroyed.
832     EGLint val;
833     EXPECT_EGL_FALSE(eglQueryContext(dpy, context, EGL_CONTEXT_CLIENT_TYPE, &val));
834     EXPECT_EQ(eglGetError(), EGL_BAD_CONTEXT);
835 }
836 
837 // Tests that Context will be automatically unmade from current on thread exit.
TEST_P(EGLContextSharingTest,UnmakeFromCurrentOnThreadExit)838 TEST_P(EGLContextSharingTest, UnmakeFromCurrentOnThreadExit)
839 {
840     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
841     ANGLE_SKIP_TEST_IF(!IsAndroid());
842 
843     EGLWindow *window = getEGLWindow();
844     EGLDisplay dpy    = window->getDisplay();
845     EGLConfig config  = window->getConfig();
846 
847     EGLContext context = window->createContext(EGL_NO_CONTEXT, nullptr);
848     EXPECT_NE(context, EGL_NO_CONTEXT);
849 
850     EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
851     EGLSurface surface         = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
852     EXPECT_NE(surface, EGL_NO_SURFACE);
853 
854     std::mutex mutex;
855     std::condition_variable condVar;
856     enum class Step
857     {
858         Start,
859         ThreadMakeCurrent,
860         Finish,
861         Abort,
862     };
863     Step currentStep = Step::Start;
864     ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
865 
866     std::thread thread([&]() {
867         eglMakeCurrent(dpy, surface, surface, context);
868         ASSERT_EGL_SUCCESS();
869 
870         threadSynchronization.nextStep(Step::ThreadMakeCurrent);
871         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
872 
873         // Clear and read back to make sure thread uses context.
874         glClearColor(1.0, 0.0, 0.0, 1.0);
875         glClear(GL_COLOR_BUFFER_BIT);
876         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
877     });
878 
879     // Wait while Context is made current in the thread.
880     ASSERT_TRUE(threadSynchronization.waitForStep(Step::ThreadMakeCurrent));
881 
882     // This should fail because context is already current in other thread.
883     eglMakeCurrent(dpy, surface, surface, context);
884     EXPECT_EQ(eglGetError(), EGL_BAD_ACCESS);
885 
886     // Finish the thread.
887     threadSynchronization.nextStep(Step::Finish);
888     thread.join();
889 
890     // This should succeed, because thread is finished so context is no longer current.
891     eglMakeCurrent(dpy, surface, surface, context);
892     EXPECT_EGL_SUCCESS();
893     eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
894     ASSERT_EGL_SUCCESS();
895 
896     // Destroy context and surface.
897     eglDestroyContext(dpy, context);
898     ASSERT_EGL_SUCCESS();
899     eglDestroySurface(dpy, surface);
900     ASSERT_EGL_SUCCESS();
901 }
902 
903 // Test that an inactive but alive thread doesn't prevent memory cleanup.
TEST_P(EGLContextSharingTestNoFixture,InactiveThreadDoesntPreventCleanup)904 TEST_P(EGLContextSharingTestNoFixture, InactiveThreadDoesntPreventCleanup)
905 {
906     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(),
907                           EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE, GetParam().getDeviceType(),
908                           EGL_NONE};
909 
910     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
911     std::mutex mutex;
912     std::condition_variable condVar;
913 
914     enum class Step
915     {
916         Start,
917         Thread0Initialize,
918         Thread1MakeCurrent,
919         Thread0MakeCurrent,
920         Thread1Render,
921         Thread0Terminate,
922         Finish,
923         Abort,
924     };
925     Step currentStep = Step::Start;
926 
927     std::thread thread0 = std::thread([&]() {
928         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
929 
930         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
931 
932         mDisplay = eglGetPlatformDisplayEXT(
933             EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
934         EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
935         EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
936 
937         threadSynchronization.nextStep(Step::Thread0Initialize);
938         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1MakeCurrent));
939 
940         EGLContext ctx;
941         EGLSurface srf;
942         EGLConfig config = EGL_NO_CONFIG_KHR;
943         EXPECT_TRUE(chooseConfig(&config));
944         EXPECT_TRUE(createContext(config, &ctx));
945 
946         EXPECT_TRUE(createPbufferSurface(mDisplay, config, 1280, 720, &srf));
947         ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
948 
949         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, srf, srf, ctx));
950         threadSynchronization.nextStep(Step::Thread0MakeCurrent);
951         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Render));
952 
953         eglTerminate(mDisplay);
954         EXPECT_EGL_SUCCESS();
955         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
956         threadSynchronization.nextStep(Step::Thread0Terminate);
957         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
958 
959         // Wait a little to simulate an inactive but alive thread.
960         angle::Sleep(100);
961     });
962 
963     std::thread thread1 = std::thread([&]() {
964         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
965 
966         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Initialize));
967 
968         EGLContext ctx;
969         EGLSurface srf;
970         EGLConfig config = EGL_NO_CONFIG_KHR;
971         EXPECT_TRUE(chooseConfig(&config));
972         EXPECT_TRUE(createContext(config, &ctx));
973 
974         EXPECT_TRUE(createPbufferSurface(mDisplay, config, 1280, 720, &srf));
975         ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
976 
977         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, srf, srf, ctx));
978 
979         threadSynchronization.nextStep(Step::Thread1MakeCurrent);
980         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrent));
981 
982         // Clear and read back to make sure thread uses context and surface.
983         glClearColor(1.0, 0.0, 0.0, 1.0);
984         glClear(GL_COLOR_BUFFER_BIT);
985         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
986 
987         threadSynchronization.nextStep(Step::Thread1Render);
988         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Terminate));
989 
990         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
991         threadSynchronization.nextStep(Step::Finish);
992     });
993 
994     thread1.join();
995     thread0.join();
996 
997     ASSERT_NE(currentStep, Step::Abort);
998 }
999 
1000 // Test that eglTerminate() with a thread doesn't cause other threads to crash.
TEST_P(EGLContextSharingTestNoFixture,EglTerminateMultiThreaded)1001 TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultiThreaded)
1002 {
1003     // http://anglebug.com/42264731
1004     // The following EGL calls led to a crash in eglMakeCurrent():
1005     //
1006     // Thread A: eglMakeCurrent(context A)
1007     // Thread B: eglDestroyContext(context A)
1008     //        B: eglTerminate() <<--- this release context A
1009     // Thread A: eglMakeCurrent(context B)
1010 
1011     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
1012     mDisplay           = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
1013                                                   reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1014     EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1015     EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1016 
1017     EGLConfig config = EGL_NO_CONFIG_KHR;
1018     EXPECT_TRUE(chooseConfig(&config));
1019 
1020     mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
1021     EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
1022     ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
1023 
1024     EXPECT_TRUE(createContext(config, &mContexts[0]));
1025     EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
1026 
1027     // Must be after the eglMakeCurrent() so renderer string is initialized.
1028     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1029     // TODO(http://anglebug.com/42264822): Fails with OpenGL ES backend.
1030     ANGLE_SKIP_TEST_IF(IsOpenGLES());
1031 
1032     EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1033 
1034     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
1035     std::mutex mutex;
1036     std::condition_variable condVar;
1037 
1038     enum class Step
1039     {
1040         Start,
1041         Thread0Clear,
1042         Thread1Terminate,
1043         Thread0MakeCurrentContext1,
1044         Finish,
1045         Abort,
1046     };
1047     Step currentStep = Step::Start;
1048 
1049     std::thread thread0 = std::thread([&]() {
1050         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1051 
1052         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1053 
1054         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
1055 
1056         // Clear and read back to make sure thread 0 uses context 0.
1057         glClearColor(1.0, 0.0, 0.0, 1.0);
1058         glClear(GL_COLOR_BUFFER_BIT);
1059         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
1060 
1061         // Wait for thread 1 to clear.
1062         threadSynchronization.nextStep(Step::Thread0Clear);
1063         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
1064 
1065         // First Display was terminated, so we need to create a new one to create a new Context.
1066         mDisplay = eglGetPlatformDisplayEXT(
1067             EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1068         EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1069         EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1070         config = EGL_NO_CONFIG_KHR;
1071         EXPECT_TRUE(chooseConfig(&config));
1072         EXPECT_TRUE(createContext(config, &mContexts[1]));
1073 
1074         // Thread1's terminate call will make mSurface an invalid handle, recreate a new surface
1075         EXPECT_TRUE(createPbufferSurface(mDisplay, config, 1280, 720, &mSurface));
1076         ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
1077 
1078         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
1079 
1080         // Clear and read back to make sure thread 0 uses context 1.
1081         glClearColor(1.0, 1.0, 0.0, 1.0);
1082         glClear(GL_COLOR_BUFFER_BIT);
1083         EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
1084 
1085         // Cleanup
1086         EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1087         EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
1088         eglDestroySurface(mDisplay, mSurface);
1089         mSurface = EGL_NO_SURFACE;
1090         eglTerminate(mDisplay);
1091         mDisplay = EGL_NO_DISPLAY;
1092         EXPECT_EGL_SUCCESS();
1093 
1094         threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
1095         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1096     });
1097 
1098     std::thread thread1 = std::thread([&]() {
1099         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1100 
1101         // Wait for thread 0 to clear.
1102         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
1103 
1104         // Destroy context 0 while thread1 has it current.
1105         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1106         EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[0]));
1107         EXPECT_EGL_SUCCESS();
1108         eglTerminate(mDisplay);
1109         mDisplay = EGL_NO_DISPLAY;
1110         EXPECT_EGL_SUCCESS();
1111 
1112         // Wait for the thread 0 to make a new context and glClear().
1113         threadSynchronization.nextStep(Step::Thread1Terminate);
1114         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
1115 
1116         threadSynchronization.nextStep(Step::Finish);
1117     });
1118 
1119     thread0.join();
1120     thread1.join();
1121 
1122     ASSERT_NE(currentStep, Step::Abort);
1123 }
1124 
1125 // Test that eglDestoryContext() can be called multiple times on the same Context without causing
1126 // errors.
TEST_P(EGLContextSharingTestNoFixture,EglDestoryContextManyTimesSameContext)1127 TEST_P(EGLContextSharingTestNoFixture, EglDestoryContextManyTimesSameContext)
1128 {
1129     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
1130     mDisplay           = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
1131                                                   reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1132     EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1133     EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1134 
1135     EGLConfig config = EGL_NO_CONFIG_KHR;
1136     EXPECT_TRUE(chooseConfig(&config));
1137 
1138     mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
1139     EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
1140     ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
1141 
1142     EXPECT_TRUE(createContext(config, &mContexts[0]));
1143     EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
1144 
1145     // Must be after the eglMakeCurrent() so renderer string is initialized.
1146     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1147     // TODO(http://anglebug.com/42264822): Fails with OpenGL ES backend.
1148     ANGLE_SKIP_TEST_IF(IsOpenGLES());
1149 
1150     EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1151 
1152     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
1153     std::mutex mutex;
1154     std::condition_variable condVar;
1155 
1156     enum class Step
1157     {
1158         Start,
1159         Thread0Clear,
1160         Thread1Terminate,
1161         Thread0MakeCurrentContext1,
1162         Finish,
1163         Abort,
1164     };
1165     Step currentStep = Step::Start;
1166 
1167     std::thread thread0 = std::thread([&]() {
1168         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1169 
1170         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1171 
1172         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
1173 
1174         // Clear and read back to make sure thread 0 uses context 0.
1175         glClearColor(1.0, 0.0, 0.0, 1.0);
1176         glClear(GL_COLOR_BUFFER_BIT);
1177         EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
1178 
1179         // Wait for thread 1 to clear.
1180         threadSynchronization.nextStep(Step::Thread0Clear);
1181         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
1182 
1183         // First Display was terminated, so we need to create a new one to create a new Context.
1184         mDisplay = eglGetPlatformDisplayEXT(
1185             EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1186         EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1187         EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1188         config = EGL_NO_CONFIG_KHR;
1189         EXPECT_TRUE(chooseConfig(&config));
1190         EXPECT_TRUE(createContext(config, &mContexts[1]));
1191 
1192         // Thread1's terminate call will make mSurface an invalid handle, recreate a new surface
1193         EXPECT_TRUE(createPbufferSurface(mDisplay, config, 1280, 720, &mSurface));
1194         ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
1195 
1196         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
1197         EXPECT_EGL_SUCCESS();
1198 
1199         // Clear and read back to make sure thread 0 uses context 1.
1200         glClearColor(1.0, 1.0, 0.0, 1.0);
1201         glClear(GL_COLOR_BUFFER_BIT);
1202         EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
1203 
1204         // Cleanup
1205         EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1206         EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
1207         eglDestroySurface(mDisplay, mSurface);
1208         mSurface = EGL_NO_SURFACE;
1209         eglTerminate(mDisplay);
1210         mDisplay = EGL_NO_DISPLAY;
1211         EXPECT_EGL_SUCCESS();
1212 
1213         threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
1214         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1215     });
1216 
1217     std::thread thread1 = std::thread([&]() {
1218         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1219 
1220         // Wait for thread 0 to clear.
1221         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
1222 
1223         EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1224 
1225         // Destroy context 0 5 times while thread1 has it current.
1226         EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1227         EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1228         EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1229         EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1230         EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1231         mContexts[0] = EGL_NO_CONTEXT;
1232 
1233         eglDestroySurface(mDisplay, mSurface);
1234         mSurface = EGL_NO_SURFACE;
1235         eglTerminate(mDisplay);
1236         mDisplay = EGL_NO_DISPLAY;
1237         EXPECT_EGL_SUCCESS();
1238 
1239         // Wait for the thread 0 to make a new context and glClear().
1240         threadSynchronization.nextStep(Step::Thread1Terminate);
1241         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
1242 
1243         threadSynchronization.nextStep(Step::Finish);
1244     });
1245 
1246     thread0.join();
1247     thread1.join();
1248 
1249     ASSERT_NE(currentStep, Step::Abort);
1250 }
1251 
1252 // Test that eglTerminate() can be called multiple times on the same Display while Contexts are
1253 // still current without causing errors.
TEST_P(EGLContextSharingTestNoFixture,EglTerminateMultipleTimes)1254 TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultipleTimes)
1255 {
1256     // https://bugs.chromium.org/p/skia/issues/detail?id=12413#c4
1257     // The following sequence caused a crash with the D3D backend in the Skia infra:
1258     //   eglDestroyContext(ctx0)
1259     //   eglDestroySurface(srf0)
1260     //   eglTerminate(shared-display)
1261     //   eglDestroyContext(ctx1) // completes the cleanup from the above terminate
1262     //   eglDestroySurface(srf1)
1263     //   eglTerminate(shared-display)
1264 
1265     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
1266     mDisplay           = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
1267                                                   reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1268     EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1269     EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1270 
1271     EGLConfig config = EGL_NO_CONFIG_KHR;
1272     EXPECT_TRUE(chooseConfig(&config));
1273 
1274     mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
1275     EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
1276     EXPECT_TRUE(mSurface != EGL_NO_SURFACE);
1277     ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
1278 
1279     EXPECT_TRUE(createContext(config, &mContexts[0]));
1280     EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
1281     EXPECT_TRUE(createContext(config, &mContexts[1]));
1282     EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
1283 
1284     // Must be after the eglMakeCurrent() so renderer string is initialized.
1285     // TODO(http://anglebug.com/42264822): Fails with Mac + OpenGL backend.
1286     ANGLE_SKIP_TEST_IF(IsMac() && IsOpenGL());
1287 
1288     EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1289 
1290     eglDestroySurface(mDisplay, mSurface);
1291     mSurface = EGL_NO_SURFACE;
1292     EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
1293     mContexts[0] = EGL_NO_CONTEXT;
1294     eglTerminate(mDisplay);
1295     EXPECT_EGL_SUCCESS();
1296 
1297     eglDestroyContext(mDisplay, mContexts[1]);
1298     mContexts[1] = EGL_NO_CONTEXT;
1299     ASSERT_EGL_ERROR(EGL_NOT_INITIALIZED);
1300     eglTerminate(mDisplay);
1301     EXPECT_EGL_SUCCESS();
1302     mDisplay = EGL_NO_DISPLAY;
1303 }
1304 
1305 // Test that we can eglSwapBuffers in one thread while another thread renders to a texture.
TEST_P(EGLContextSharingTestNoFixture,SwapBuffersShared)1306 TEST_P(EGLContextSharingTestNoFixture, SwapBuffersShared)
1307 {
1308     EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
1309     mDisplay           = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
1310                                                   reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1311     EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1312     EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1313 
1314     EGLConfig config = EGL_NO_CONFIG_KHR;
1315     EXPECT_TRUE(chooseConfig(&config));
1316 
1317     mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
1318     EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
1319     ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
1320 
1321     EGLSurface pbufferSurface;
1322     EXPECT_TRUE(createPbufferSurface(mDisplay, config, kWidth, kHeight, &pbufferSurface));
1323 
1324     // Create the two contextss
1325     EXPECT_TRUE(createContext(config, &mContexts[0]));
1326     EXPECT_TRUE(createContext(config, &mContexts[1], mContexts[0]));
1327 
1328     eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]);
1329     ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1330     eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1331 
1332     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
1333     std::mutex mutex;
1334     std::condition_variable condVar;
1335 
1336     enum class Step
1337     {
1338         Start,
1339         TextureInitialized,
1340         Finish,
1341         Abort,
1342     };
1343 
1344     Step currentStep = Step::Start;
1345 
1346     // Sample a texture in the swap thread.
1347     std::thread swapThread = std::thread([&]() {
1348         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1349         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1350         eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]);
1351 
1352         glGenTextures(1, &mTexture);
1353         glBindTexture(GL_TEXTURE_2D, mTexture);
1354         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1355                      nullptr);
1356 
1357         threadSynchronization.nextStep(Step::TextureInitialized);
1358 
1359         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
1360         glUseProgram(program);
1361 
1362         for (int i = 0; i < 100; ++i)
1363         {
1364             glClear(GL_COLOR_BUFFER_BIT);
1365             drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
1366             EXPECT_GL_NO_ERROR();
1367             eglSwapBuffers(mDisplay, mSurface);
1368         }
1369         eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1370         eglReleaseThread();
1371 
1372         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1373     });
1374 
1375     // Render to the texture in the render thread.
1376     std::thread renderThread = std::thread([&]() {
1377         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1378         ASSERT_EGL_TRUE(eglMakeCurrent(mDisplay, pbufferSurface, pbufferSurface, mContexts[1]));
1379 
1380         GLFramebuffer fbo;
1381         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1382 
1383         ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1384         ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1385         ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
1386 
1387         // The render thread will draw to the texture.
1388         ASSERT_TRUE(threadSynchronization.waitForStep(Step::TextureInitialized));
1389         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
1390         glDisable(GL_DEPTH_TEST);
1391 
1392         for (int i = 0; i < 400; ++i)
1393         {
1394             glClear(GL_COLOR_BUFFER_BIT);
1395             glUseProgram(redProgram);
1396             drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
1397 
1398             glClear(GL_COLOR_BUFFER_BIT);
1399             glUseProgram(greenProgram);
1400             drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
1401 
1402             glClear(GL_COLOR_BUFFER_BIT);
1403             glUseProgram(blueProgram);
1404             drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
1405         }
1406         eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
1407         eglReleaseThread();
1408 
1409         threadSynchronization.nextStep(Step::Finish);
1410     });
1411 
1412     swapThread.join();
1413     renderThread.join();
1414 
1415     eglDestroySurface(mDisplay, pbufferSurface);
1416     ASSERT_EGL_SUCCESS();
1417 }
1418 
1419 class EGLContextSharingTestNoSyncTextureUploads : public EGLContextSharingTest
1420 {};
1421 
1422 // Test that an application that does not synchronize when using textures across shared contexts can
1423 // still see texture updates. This behavior is not required by the GLES specification, but is
1424 // exhibited by some applications. That application will malfunction if our implementation does not
1425 // handle this in the way it expects. Only the vulkan backend has the workaround needed for this
1426 // usecase.
TEST_P(EGLContextSharingTestNoSyncTextureUploads,NoSync)1427 TEST_P(EGLContextSharingTestNoSyncTextureUploads, NoSync)
1428 {
1429     EGLDisplay display = getEGLWindow()->getDisplay();
1430     EGLConfig config   = getEGLWindow()->getConfig();
1431 
1432     const EGLint inShareGroupContextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
1433     const EGLint pbufferAttributes[]          = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE};
1434 
1435     constexpr size_t kThreadCount    = 2;
1436     EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
1437 
1438     for (size_t t = 0; t < kThreadCount; ++t)
1439     {
1440         mContexts[t] = eglCreateContext(display, config, t == 0 ? EGL_NO_CONTEXT : mContexts[0],
1441                                         inShareGroupContextAttribs);
1442         ASSERT_EGL_SUCCESS();
1443         ASSERT_NE(EGL_NO_CONTEXT, mContexts[t]);
1444 
1445         surface[t] = eglCreatePbufferSurface(display, config, pbufferAttributes);
1446         EXPECT_EGL_SUCCESS();
1447         ASSERT_NE(EGL_NO_SURFACE, surface[t]);
1448     }
1449 
1450     GLTexture textureFromCtx0;
1451     constexpr size_t kTextureCount = 10;
1452     GLTexture textures[kTextureCount];
1453 
1454     // Synchronization tools to ensure the two threads are interleaved as designed by this test.
1455     std::mutex mutex;
1456     std::condition_variable condVar;
1457     enum class Step
1458     {
1459         Start,
1460         Ctx0Current,
1461         Ctx1Current,
1462         TexturesDone,
1463         Finish,
1464         Abort,
1465     };
1466     Step currentStep = Step::Start;
1467 
1468     std::thread creatingThread = std::thread([&]() {
1469         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1470 
1471         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1472         ASSERT_EGL_TRUE(eglMakeCurrent(display, surface[0], surface[0], mContexts[0]));
1473         ASSERT_EGL_SUCCESS();
1474         threadSynchronization.nextStep(Step::Ctx0Current);
1475 
1476         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Ctx1Current));
1477 
1478         // Create the shared textures that will be accessed by the other context
1479         glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
1480         glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1481         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1482 
1483         ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
1484 
1485         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::red);
1486         glFinish();
1487 
1488         glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::blue);
1489         // Do not glFinish
1490 
1491         // We set 6 to be the threshold to flush texture updates.
1492         // We create redundant textures here to ensure that we trigger that threshold.
1493         for (size_t i = 0; i < kTextureCount; i++)
1494         {
1495             glBindTexture(GL_TEXTURE_2D, textures[i]);
1496             glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1497             glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
1498 
1499             ASSERT_GL_TRUE(glIsTexture(textures[i]));
1500 
1501             glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1502                             &GLColor::blue);
1503         }
1504 
1505         ASSERT_GL_NO_ERROR();
1506 
1507         threadSynchronization.nextStep(Step::TexturesDone);
1508         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1509         ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1510         ASSERT_EGL_SUCCESS();
1511     });
1512 
1513     std::thread samplingThread = std::thread([&]() {
1514         ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
1515 
1516         ASSERT_TRUE(threadSynchronization.waitForStep(Step::Ctx0Current));
1517         ASSERT_EGL_TRUE(eglMakeCurrent(display, surface[1], surface[1], mContexts[1]));
1518         ASSERT_EGL_SUCCESS();
1519         threadSynchronization.nextStep(Step::Ctx1Current);
1520 
1521         ASSERT_TRUE(threadSynchronization.waitForStep(Step::TexturesDone));
1522 
1523         ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
1524         ASSERT_GL_NO_ERROR();
1525 
1526         // Draw using ctx0 texture as sampler
1527         GLTexture ctx1tex;
1528         glBindTexture(GL_TEXTURE_2D, ctx1tex);
1529         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1530                      GLColor::black.data());
1531         ASSERT_GL_NO_ERROR();
1532 
1533         GLFramebuffer fbo;
1534         glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1535         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, ctx1tex, 0);
1536         ASSERT_GL_NO_ERROR();
1537 
1538         GLuint sampler;
1539         glGenSamplers(1, &sampler);
1540 
1541         ASSERT_GL_NO_ERROR();
1542 
1543         ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
1544         glUseProgram(program);
1545         glActiveTexture(GL_TEXTURE0);
1546         glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
1547         glBindSampler(0, sampler);
1548         glUniform1i(glGetUniformLocation(program, essl1_shaders::PositionAttrib()), 0);
1549         ASSERT_GL_NO_ERROR();
1550 
1551         drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
1552         ASSERT_GL_NO_ERROR();
1553 
1554         EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1555 
1556         threadSynchronization.nextStep(Step::Finish);
1557         ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1558         ASSERT_EGL_SUCCESS();
1559     });
1560 
1561     creatingThread.join();
1562     samplingThread.join();
1563 
1564     ASSERT_NE(currentStep, Step::Abort);
1565     ASSERT_EGL_SUCCESS();
1566 
1567     for (size_t t = 0; t < kThreadCount; ++t)
1568     {
1569         ASSERT_EGL_TRUE(eglDestroySurface(display, surface[t]));
1570         ASSERT_EGL_SUCCESS();
1571     }
1572 }
1573 
1574 // Tests that creating a context and immediately destroying it works when no surface has been
1575 // created.
TEST_P(EGLContextSharingTestNoFixture,ImmediateContextDestroyAfterCreation)1576 TEST_P(EGLContextSharingTestNoFixture, ImmediateContextDestroyAfterCreation)
1577 {
1578     EGLAttrib dispattrs[3] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
1579     mDisplay               = eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
1580                                                    reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
1581     EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
1582     EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
1583 
1584     EGLConfig config = EGL_NO_CONFIG_KHR;
1585     EXPECT_TRUE(chooseConfig(&config));
1586 
1587     // Create a context and immediately destroy it.  Note that no window surface should be created
1588     // for this test.  Regression test for platforms that expose multiple queue families in Vulkan,
1589     // and ANGLE defers creation of the device until a surface is created.  In this case, the
1590     // context is being destroyed before a queue is ever created.
1591     EXPECT_TRUE(createContext(config, &mContexts[0]));
1592     EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[0]));
1593     ASSERT_EGL_SUCCESS();
1594 }
1595 }  // anonymous namespace
1596 
1597 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextSharingTest);
1598 ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,
1599                        ES2_D3D9(),
1600                        ES2_D3D11(),
1601                        ES3_D3D11(),
1602                        ES2_METAL(),
1603                        ES3_METAL(),
1604                        ES2_OPENGL(),
1605                        ES3_OPENGL(),
1606                        ES2_VULKAN(),
1607                        ES3_VULKAN());
1608 
1609 ANGLE_INSTANTIATE_TEST(EGLContextSharingTestNoFixture,
1610                        WithNoFixture(ES2_METAL()),
1611                        WithNoFixture(ES3_METAL()),
1612                        WithNoFixture(ES2_OPENGLES()),
1613                        WithNoFixture(ES3_OPENGLES()),
1614                        WithNoFixture(ES2_OPENGL()),
1615                        WithNoFixture(ES3_OPENGL()),
1616                        WithNoFixture(ES2_VULKAN()),
1617                        WithNoFixture(ES3_VULKAN()));
1618 
1619 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextSharingTestNoSyncTextureUploads);
1620 ANGLE_INSTANTIATE_TEST(EGLContextSharingTestNoSyncTextureUploads,
1621                        ES2_VULKAN().enable(Feature::ForceSubmitImmutableTextureUpdates),
1622                        ES3_VULKAN().enable(Feature::ForceSubmitImmutableTextureUpdates));
1623