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