1 //
2 // Copyright 2016 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // EGLContextSharingTest.cpp:
7 // Tests relating to shared Contexts.
8
9 #include <gtest/gtest.h>
10
11 #include "test_utils/ANGLETest.h"
12 #include "test_utils/MultiThreadSteps.h"
13 #include "test_utils/angle_test_configs.h"
14 #include "test_utils/gl_raii.h"
15 #include "util/EGLWindow.h"
16 #include "util/OSWindow.h"
17
18 using namespace angle;
19
20 namespace
21 {
22
SafeDestroyContext(EGLDisplay display,EGLContext & context)23 EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)
24 {
25 EGLBoolean result = EGL_TRUE;
26 if (context != EGL_NO_CONTEXT)
27 {
28 result = eglDestroyContext(display, context);
29 context = EGL_NO_CONTEXT;
30 }
31 return result;
32 }
33
34 class EGLContextSharingTest : public ANGLETest
35 {
36 public:
EGLContextSharingTest()37 EGLContextSharingTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}
38
testTearDown()39 void testTearDown() override
40 {
41 glDeleteTextures(1, &mTexture);
42
43 EGLDisplay display = getEGLWindow()->getDisplay();
44
45 if (display != EGL_NO_DISPLAY)
46 {
47 for (auto &context : mContexts)
48 {
49 SafeDestroyContext(display, context);
50 }
51 }
52
53 // Set default test state to not give an error on shutdown.
54 getEGLWindow()->makeCurrent();
55 }
56
57 EGLContext mContexts[2] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
58 GLuint mTexture;
59 };
60
61 class EGLContextSharingTestNoFixture : public EGLContextSharingTest
62 {
63 public:
EGLContextSharingTestNoFixture()64 EGLContextSharingTestNoFixture() : EGLContextSharingTest() {}
65
testSetUp()66 void testSetUp() override
67 {
68 mOsWindow = OSWindow::New();
69 mMajorVersion = GetParam().majorVersion;
70 }
71
testTearDown()72 void testTearDown() override
73 {
74 if (mDisplay != EGL_NO_DISPLAY)
75 {
76 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
77
78 if (mSurface != EGL_NO_SURFACE)
79 {
80 eglDestroySurface(mDisplay, mSurface);
81 ASSERT_EGL_SUCCESS();
82 mSurface = EGL_NO_SURFACE;
83 }
84
85 for (auto &context : mContexts)
86 {
87 SafeDestroyContext(mDisplay, context);
88 }
89
90 eglTerminate(mDisplay);
91 mDisplay = EGL_NO_DISPLAY;
92 ASSERT_EGL_SUCCESS();
93 eglReleaseThread();
94 ASSERT_EGL_SUCCESS();
95 }
96
97 mOsWindow->destroy();
98 OSWindow::Delete(&mOsWindow);
99 ASSERT_EGL_SUCCESS() << "Error during test TearDown";
100 }
101
chooseConfig(EGLConfig * config) const102 bool chooseConfig(EGLConfig *config) const
103 {
104 bool result = false;
105 EGLint count = 0;
106 EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;
107 EGLint attribs[] = {EGL_RED_SIZE,
108 8,
109 EGL_GREEN_SIZE,
110 8,
111 EGL_BLUE_SIZE,
112 8,
113 EGL_ALPHA_SIZE,
114 0,
115 EGL_RENDERABLE_TYPE,
116 clientVersion,
117 EGL_SURFACE_TYPE,
118 EGL_WINDOW_BIT,
119 EGL_NONE};
120
121 result = eglChooseConfig(mDisplay, attribs, config, 1, &count);
122 EXPECT_EGL_TRUE(result && (count > 0));
123 return result;
124 }
125
createContext(EGLConfig config,EGLContext * context)126 bool createContext(EGLConfig config, EGLContext *context)
127 {
128 bool result = false;
129 EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};
130
131 *context = eglCreateContext(mDisplay, config, nullptr, attribs);
132 result = (*context != EGL_NO_CONTEXT);
133 EXPECT_TRUE(result);
134 return result;
135 }
136
createWindowSurface(EGLConfig config,EGLNativeWindowType win,EGLSurface * surface)137 bool createWindowSurface(EGLConfig config, EGLNativeWindowType win, EGLSurface *surface)
138 {
139 bool result = false;
140 EGLint attribs[] = {EGL_NONE};
141
142 *surface = eglCreateWindowSurface(mDisplay, config, win, attribs);
143 result = (*surface != EGL_NO_SURFACE);
144 EXPECT_TRUE(result);
145 return result;
146 }
147
148 OSWindow *mOsWindow;
149 EGLDisplay mDisplay = EGL_NO_DISPLAY;
150 EGLSurface mSurface = EGL_NO_SURFACE;
151 const EGLint kWidth = 64;
152 const EGLint kHeight = 64;
153 EGLint mMajorVersion = 0;
154 };
155
156 // Tests that creating resources works after freeing the share context.
TEST_P(EGLContextSharingTest,BindTextureAfterShareContextFree)157 TEST_P(EGLContextSharingTest, BindTextureAfterShareContextFree)
158 {
159 EGLDisplay display = getEGLWindow()->getDisplay();
160 EGLConfig config = getEGLWindow()->getConfig();
161 EGLSurface surface = getEGLWindow()->getSurface();
162
163 const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,
164 getEGLWindow()->getClientMajorVersion(), EGL_NONE};
165
166 mContexts[0] = eglCreateContext(display, config, nullptr, contextAttribs);
167 ASSERT_EGL_SUCCESS();
168 ASSERT_TRUE(mContexts[0] != EGL_NO_CONTEXT);
169 mContexts[1] = eglCreateContext(display, config, mContexts[1], contextAttribs);
170 ASSERT_EGL_SUCCESS();
171 ASSERT_TRUE(mContexts[1] != EGL_NO_CONTEXT);
172
173 ASSERT_EGL_TRUE(SafeDestroyContext(display, mContexts[0]));
174 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
175 ASSERT_EGL_SUCCESS();
176
177 glGenTextures(1, &mTexture);
178 glBindTexture(GL_TEXTURE_2D, mTexture);
179 ASSERT_GL_NO_ERROR();
180 }
181
182 // Tests the creation of contexts using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupContextCreation)183 TEST_P(EGLContextSharingTest, DisplayShareGroupContextCreation)
184 {
185 EGLDisplay display = getEGLWindow()->getDisplay();
186 EGLConfig config = getEGLWindow()->getConfig();
187
188 const EGLint inShareGroupContextAttribs[] = {
189 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
190
191 // Check whether extension's supported to avoid clearing the EGL error state
192 // after failed context creation.
193 bool extensionEnabled =
194 IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group");
195
196 // Test creating two contexts in the global share group
197 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
198 mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);
199
200 if (!extensionEnabled)
201 {
202 // Make sure an error is generated and early-exit
203 ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
204 ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);
205 return;
206 }
207
208 ASSERT_EGL_SUCCESS();
209
210 ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
211 ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
212 eglDestroyContext(display, mContexts[0]);
213 mContexts[0] = EGL_NO_CONTEXT;
214
215 // Try creating a context that is not in the global share group but tries to share with a
216 // context that is
217 const EGLint notInShareGroupContextAttribs[] = {
218 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};
219 mContexts[0] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);
220 ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
221 ASSERT_TRUE(mContexts[0] == EGL_NO_CONTEXT);
222 }
223
224 // Tests the sharing of textures using EGL_ANGLE_display_texture_share_group
TEST_P(EGLContextSharingTest,DisplayShareGroupObjectSharing)225 TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)
226 {
227 EGLDisplay display = getEGLWindow()->getDisplay();
228 if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
229 {
230 std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
231 << std::endl;
232 return;
233 }
234
235 EGLConfig config = getEGLWindow()->getConfig();
236 EGLSurface surface = getEGLWindow()->getSurface();
237
238 const EGLint inShareGroupContextAttribs[] = {
239 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
240
241 // Create two contexts in the global share group but not in the same context share group
242 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
243 mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
244
245 ASSERT_EGL_SUCCESS();
246
247 ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
248 ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
249
250 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
251 ASSERT_EGL_SUCCESS();
252
253 // Create a texture and buffer in ctx 0
254 GLTexture textureFromCtx0;
255 glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
256 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
257 glBindTexture(GL_TEXTURE_2D, 0);
258 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
259
260 GLBuffer bufferFromCtx0;
261 glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);
262 glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);
263 glBindBuffer(GL_ARRAY_BUFFER, 0);
264 ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
265
266 ASSERT_GL_NO_ERROR();
267
268 // Switch to context 1 and verify that the texture is accessible but the buffer is not
269 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
270 ASSERT_EGL_SUCCESS();
271
272 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
273
274 ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));
275 ASSERT_GL_NO_ERROR();
276
277 // Call readpixels on the texture to verify that the backend has proper support
278 GLFramebuffer fbo;
279 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
280 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);
281
282 GLubyte pixel[4];
283 glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
284 ASSERT_GL_NO_ERROR();
285
286 // Switch back to context 0 and delete the buffer
287 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
288 ASSERT_EGL_SUCCESS();
289 }
290
291 // Tests that shared textures using EGL_ANGLE_display_texture_share_group are released when the last
292 // context is destroyed
TEST_P(EGLContextSharingTest,DisplayShareGroupReleasedWithLastContext)293 TEST_P(EGLContextSharingTest, DisplayShareGroupReleasedWithLastContext)
294 {
295 EGLDisplay display = getEGLWindow()->getDisplay();
296 if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
297 {
298 std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
299 << std::endl;
300 return;
301 }
302
303 EGLConfig config = getEGLWindow()->getConfig();
304 EGLSurface surface = getEGLWindow()->getSurface();
305
306 const EGLint inShareGroupContextAttribs[] = {
307 EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
308
309 // Create two contexts in the global share group but not in the same context share group
310 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
311 mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
312
313 // Create a texture and buffer in ctx 0
314 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
315 GLTexture textureFromCtx0;
316 glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
317 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
318 glBindTexture(GL_TEXTURE_2D, 0);
319 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
320
321 // Switch to context 1 and verify that the texture is accessible
322 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
323 ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
324
325 // Destroy both contexts, the texture should be cleaned up automatically
326 ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[0]));
327 mContexts[0] = EGL_NO_CONTEXT;
328 ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[1]));
329 mContexts[1] = EGL_NO_CONTEXT;
330
331 // Unmake current, so the context can be released.
332 ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
333
334 // Create a new context and verify it cannot access the texture previously created
335 mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
336 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
337
338 ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
339 }
340
341 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
342 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,TextureLifetime)343 TEST_P(EGLContextSharingTest, TextureLifetime)
344 {
345 EGLWindow *eglWindow = getEGLWindow();
346 EGLConfig config = getEGLWindow()->getConfig();
347 EGLDisplay display = getEGLWindow()->getDisplay();
348
349 // Create a pbuffer surface for use with a shared context.
350 EGLSurface surface = eglWindow->getSurface();
351 EGLContext mainContext = eglWindow->getContext();
352
353 // Initialize a shared context.
354 mContexts[0] = eglCreateContext(display, config, mainContext, nullptr);
355 ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
356
357 // Create a Texture on the shared context.
358 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
359
360 constexpr GLsizei kTexSize = 2;
361 const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
362 GLColor::yellow};
363 GLTexture tex;
364 glBindTexture(GL_TEXTURE_2D, tex);
365 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
366 kTexData);
367 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
368 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
369
370 // Make the main Context current and draw with the texture.
371 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
372
373 glBindTexture(GL_TEXTURE_2D, tex);
374 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
375 glUseProgram(program);
376
377 // No uniform update because the update seems to hide the error on Vulkan.
378
379 // Enqueue the draw call.
380 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
381 EXPECT_GL_NO_ERROR();
382
383 // Delete the texture in the main context to orphan it.
384 // Do not read back the data to keep the commands in the graph.
385 tex.reset();
386
387 // Bind and delete the test context. This should trigger texture garbage collection.
388 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
389 SafeDestroyContext(display, mContexts[0]);
390
391 // Bind the main context to clean up the test.
392 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
393 }
394
395 // Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
396 // on the Vulkan back-end where we manage object lifetime manually.
TEST_P(EGLContextSharingTest,SamplerLifetime)397 TEST_P(EGLContextSharingTest, SamplerLifetime)
398 {
399 EGLWindow *eglWindow = getEGLWindow();
400 EGLConfig config = getEGLWindow()->getConfig();
401 EGLDisplay display = getEGLWindow()->getDisplay();
402
403 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
404 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_KHR_create_context"));
405
406 // Create a pbuffer surface for use with a shared context.
407 EGLSurface surface = eglWindow->getSurface();
408 EGLContext mainContext = eglWindow->getContext();
409
410 std::vector<EGLint> contextAttributes;
411 contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
412 contextAttributes.push_back(getClientMajorVersion());
413 contextAttributes.push_back(EGL_NONE);
414
415 // Initialize a shared context.
416 mContexts[0] = eglCreateContext(display, config, mainContext, contextAttributes.data());
417 ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
418
419 // Create a Texture on the shared context. Also create a Sampler object.
420 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
421
422 constexpr GLsizei kTexSize = 2;
423 const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
424 GLColor::yellow};
425 GLTexture tex;
426 glBindTexture(GL_TEXTURE_2D, tex);
427 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
428 kTexData);
429
430 GLSampler sampler;
431 glBindSampler(0, sampler);
432 glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
433 glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
434
435 // Make the main Context current and draw with the texture and sampler.
436 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
437
438 glBindTexture(GL_TEXTURE_2D, tex);
439 glBindSampler(0, sampler);
440 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
441 glUseProgram(program);
442
443 // No uniform update because the update seems to hide the error on Vulkan.
444
445 // Enqueue the draw call.
446 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
447 EXPECT_GL_NO_ERROR();
448
449 // Delete the texture and sampler in the main context to orphan them.
450 // Do not read back the data to keep the commands in the graph.
451 tex.reset();
452 sampler.reset();
453
454 // Bind and delete the test context. This should trigger texture garbage collection.
455 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
456 SafeDestroyContext(display, mContexts[0]);
457
458 // Bind the main context to clean up the test.
459 ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
460 }
461
462 // Test that deleting an object reading from a shared object in one context doesn't cause the other
463 // context to crash. Mostly focused on the Vulkan back-end where we track resource dependencies in
464 // a graph.
TEST_P(EGLContextSharingTest,DeleteReaderOfSharedTexture)465 TEST_P(EGLContextSharingTest, DeleteReaderOfSharedTexture)
466 {
467 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
468 // GL Fences require GLES 3.0+
469 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
470
471 // Initialize contexts
472 EGLWindow *window = getEGLWindow();
473 EGLDisplay dpy = window->getDisplay();
474 EGLConfig config = window->getConfig();
475
476 constexpr size_t kThreadCount = 2;
477 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
478 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
479
480 EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
481
482 for (size_t t = 0; t < kThreadCount; ++t)
483 {
484 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
485 EXPECT_EGL_SUCCESS();
486
487 ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], nullptr);
488 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
489 }
490
491 // Initialize test resources. They are done outside the threads to reduce the sources of
492 // errors and thus deadlock-free teardown.
493 ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
494
495 // Shared texture to read from.
496 constexpr GLsizei kTexSize = 1;
497 const GLColor kTexData = GLColor::red;
498
499 GLTexture sharedTex;
500 glBindTexture(GL_TEXTURE_2D, sharedTex);
501 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
502 &kTexData);
503 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
504 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
505
506 // Resources for each context.
507 GLRenderbuffer renderbuffer[kThreadCount];
508 GLFramebuffer fbo[kThreadCount];
509 GLProgram program[kThreadCount];
510
511 for (size_t t = 0; t < kThreadCount; ++t)
512 {
513 ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[t], surface[t], ctx[t]));
514
515 glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[t]);
516 constexpr int kRenderbufferSize = 4;
517 glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
518
519 glBindFramebuffer(GL_FRAMEBUFFER, fbo[t]);
520 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
521 renderbuffer[t]);
522
523 glBindTexture(GL_TEXTURE_2D, sharedTex);
524 program[t].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
525 ASSERT_TRUE(program[t].valid());
526 }
527
528 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
529
530 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
531 std::mutex mutex;
532 std::condition_variable condVar;
533 std::atomic<GLsync> deletingThreadSyncObj;
534 std::atomic<GLsync> continuingThreadSyncObj;
535
536 enum class Step
537 {
538 Start,
539 Thread0Draw,
540 Thread1Draw,
541 Thread0Delete,
542 Finish,
543 Abort,
544 };
545 Step currentStep = Step::Start;
546
547 std::thread deletingThread = std::thread([&]() {
548 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
549
550 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
551 EXPECT_EGL_SUCCESS();
552
553 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
554
555 // Draw using the shared texture.
556 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
557
558 deletingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
559 ASSERT_GL_NO_ERROR();
560 // Force the fence to be created
561 glFlush();
562
563 // Wait for the other thread to also draw using the shared texture.
564 threadSynchronization.nextStep(Step::Thread0Draw);
565 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
566
567 ASSERT_TRUE(continuingThreadSyncObj != nullptr);
568 glWaitSync(continuingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
569 ASSERT_GL_NO_ERROR();
570 glDeleteSync(continuingThreadSyncObj);
571 ASSERT_GL_NO_ERROR();
572 continuingThreadSyncObj = nullptr;
573
574 // Delete this thread's framebuffer (reader of the shared texture).
575 fbo[0].reset();
576
577 // Wait for the other thread to use the shared texture again before unbinding the
578 // context (so no implicit flush happens).
579 threadSynchronization.nextStep(Step::Thread0Delete);
580 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
581
582 EXPECT_GL_NO_ERROR();
583 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
584 EXPECT_EGL_SUCCESS();
585 });
586
587 std::thread continuingThread = std::thread([&]() {
588 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
589
590 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
591 EXPECT_EGL_SUCCESS();
592
593 // Wait for first thread to draw using the shared texture.
594 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
595
596 ASSERT_TRUE(deletingThreadSyncObj != nullptr);
597 glWaitSync(deletingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
598 ASSERT_GL_NO_ERROR();
599 glDeleteSync(deletingThreadSyncObj);
600 ASSERT_GL_NO_ERROR();
601 deletingThreadSyncObj = nullptr;
602
603 // Draw using the shared texture.
604 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
605
606 continuingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
607 ASSERT_GL_NO_ERROR();
608 // Force the fence to be created
609 glFlush();
610
611 // Wait for the other thread to delete its framebuffer.
612 threadSynchronization.nextStep(Step::Thread1Draw);
613 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Delete));
614
615 // Write to the shared texture differently, so a dependency is created from the previous
616 // readers of the shared texture (the two framebuffers of the two threads) to the new
617 // commands being recorded for the shared texture.
618 //
619 // If the backend attempts to create a dependency from nodes associated with the
620 // previous readers of the texture to the new node that will contain the following
621 // commands, there will be a use-after-free error.
622 const GLColor kTexData2 = GLColor::green;
623 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
624 &kTexData2);
625 drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
626
627 threadSynchronization.nextStep(Step::Finish);
628
629 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
630 EXPECT_EGL_SUCCESS();
631 });
632
633 deletingThread.join();
634 continuingThread.join();
635
636 ASSERT_NE(currentStep, Step::Abort);
637
638 // Clean up
639 for (size_t t = 0; t < kThreadCount; ++t)
640 {
641 eglDestroySurface(dpy, surface[t]);
642 eglDestroyContext(dpy, ctx[t]);
643 }
644 }
645
646 // Test that eglTerminate() with a thread doesn't cause other threads to crash.
TEST_P(EGLContextSharingTestNoFixture,EglTerminateMultiThreaded)647 TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultiThreaded)
648 {
649 // http://anglebug.com/6208
650 // The following EGL calls led to a crash in eglMakeCurrent():
651 //
652 // Thread A: eglMakeCurrent(context A)
653 // Thread B: eglDestroyContext(context A)
654 // B: eglTerminate() <<--- this release context A
655 // Thread A: eglMakeCurrent(context B)
656
657 EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
658 mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
659 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
660 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
661 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
662
663 EGLConfig config = EGL_NO_CONFIG_KHR;
664 EXPECT_TRUE(chooseConfig(&config));
665
666 mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
667 EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
668 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
669
670 EXPECT_TRUE(createContext(config, &mContexts[0]));
671 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
672
673 // Must be after the eglMakeCurrent() so renderer string is initialized.
674 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
675 // TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.
676 ANGLE_SKIP_TEST_IF(IsOpenGLES());
677
678 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
679 std::mutex mutex;
680 std::condition_variable condVar;
681
682 enum class Step
683 {
684 Start,
685 Thread0Clear,
686 Thread1Terminate,
687 Thread0MakeCurrentContext1,
688 Finish,
689 Abort,
690 };
691 Step currentStep = Step::Start;
692
693 std::thread thread0 = std::thread([&]() {
694 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
695
696 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
697
698 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
699
700 // Clear and read back to make sure thread 0 uses context 0.
701 glClearColor(1.0, 0.0, 0.0, 1.0);
702 glClear(GL_COLOR_BUFFER_BIT);
703 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
704
705 // Wait for thread 1 to clear.
706 threadSynchronization.nextStep(Step::Thread0Clear);
707 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
708
709 // First Display was terminated, so we need to create a new one to create a new Context.
710 mDisplay = eglGetPlatformDisplayEXT(
711 EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
712 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
713 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
714 config = EGL_NO_CONFIG_KHR;
715 EXPECT_TRUE(chooseConfig(&config));
716 EXPECT_TRUE(createContext(config, &mContexts[1]));
717 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
718
719 // Clear and read back to make sure thread 0 uses context 1.
720 glClearColor(1.0, 1.0, 0.0, 1.0);
721 glClear(GL_COLOR_BUFFER_BIT);
722 EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
723
724 // Cleanup
725 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
726 EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
727 eglDestroySurface(mDisplay, mSurface);
728 mSurface = EGL_NO_SURFACE;
729 eglTerminate(mDisplay);
730 mDisplay = EGL_NO_DISPLAY;
731 EXPECT_EGL_SUCCESS();
732
733 threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
734 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
735 });
736
737 std::thread thread1 = std::thread([&]() {
738 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
739
740 // Wait for thread 0 to clear.
741 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
742
743 // Destroy context 0 while thread1 has it current.
744 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
745 EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[0]));
746 EXPECT_EGL_SUCCESS();
747 eglTerminate(mDisplay);
748 mDisplay = EGL_NO_DISPLAY;
749 EXPECT_EGL_SUCCESS();
750
751 // Wait for the thread 0 to make a new context and glClear().
752 threadSynchronization.nextStep(Step::Thread1Terminate);
753 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
754
755 threadSynchronization.nextStep(Step::Finish);
756 });
757
758 thread0.join();
759 thread1.join();
760
761 ASSERT_NE(currentStep, Step::Abort);
762 }
763
764 // Test that eglDestoryContext() can be called multiple times on the same Context without causing
765 // errors.
TEST_P(EGLContextSharingTestNoFixture,EglDestoryContextManyTimesSameContext)766 TEST_P(EGLContextSharingTestNoFixture, EglDestoryContextManyTimesSameContext)
767 {
768 EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
769 mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
770 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
771 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
772 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
773
774 EGLConfig config = EGL_NO_CONFIG_KHR;
775 EXPECT_TRUE(chooseConfig(&config));
776
777 mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
778 EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
779 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
780
781 EXPECT_TRUE(createContext(config, &mContexts[0]));
782 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
783
784 // Must be after the eglMakeCurrent() so renderer string is initialized.
785 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
786 // TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.
787 ANGLE_SKIP_TEST_IF(IsOpenGLES());
788
789 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
790 std::mutex mutex;
791 std::condition_variable condVar;
792
793 enum class Step
794 {
795 Start,
796 Thread0Clear,
797 Thread1Terminate,
798 Thread0MakeCurrentContext1,
799 Finish,
800 Abort,
801 };
802 Step currentStep = Step::Start;
803
804 std::thread thread0 = std::thread([&]() {
805 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
806
807 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
808
809 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
810
811 // Clear and read back to make sure thread 0 uses context 0.
812 glClearColor(1.0, 0.0, 0.0, 1.0);
813 glClear(GL_COLOR_BUFFER_BIT);
814 EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
815
816 // Wait for thread 1 to clear.
817 threadSynchronization.nextStep(Step::Thread0Clear);
818 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
819
820 // First Display was terminated, so we need to create a new one to create a new Context.
821 mDisplay = eglGetPlatformDisplayEXT(
822 EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
823 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
824 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
825 config = EGL_NO_CONFIG_KHR;
826 EXPECT_TRUE(chooseConfig(&config));
827 EXPECT_TRUE(createContext(config, &mContexts[1]));
828 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
829
830 // Clear and read back to make sure thread 0 uses context 1.
831 glClearColor(1.0, 1.0, 0.0, 1.0);
832 glClear(GL_COLOR_BUFFER_BIT);
833 EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
834
835 // Cleanup
836 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
837 EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
838 eglDestroySurface(mDisplay, mSurface);
839 mSurface = EGL_NO_SURFACE;
840 eglTerminate(mDisplay);
841 mDisplay = EGL_NO_DISPLAY;
842 EXPECT_EGL_SUCCESS();
843
844 threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
845 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
846 });
847
848 std::thread thread1 = std::thread([&]() {
849 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
850
851 // Wait for thread 0 to clear.
852 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
853
854 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
855
856 // Destroy context 0 5 times while thread1 has it current.
857 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
858 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
859 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
860 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
861 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
862 mContexts[0] = EGL_NO_CONTEXT;
863
864 eglTerminate(mDisplay);
865 mDisplay = EGL_NO_DISPLAY;
866 EXPECT_EGL_SUCCESS();
867
868 // Wait for the thread 0 to make a new context and glClear().
869 threadSynchronization.nextStep(Step::Thread1Terminate);
870 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
871
872 threadSynchronization.nextStep(Step::Finish);
873 });
874
875 thread0.join();
876 thread1.join();
877
878 ASSERT_NE(currentStep, Step::Abort);
879 }
880
881 // Test that eglTerminate() can be called multiple times on the same Display while Contexts are
882 // still current without causing errors.
TEST_P(EGLContextSharingTestNoFixture,EglTerminateMultipleTimes)883 TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultipleTimes)
884 {
885 // https://bugs.chromium.org/p/skia/issues/detail?id=12413#c4
886 // The following sequence caused a crash with the D3D backend in the Skia infra:
887 // eglDestroyContext(ctx0)
888 // eglDestroySurface(srf0)
889 // eglTerminate(shared-display)
890 // eglDestroyContext(ctx1) // completes the cleanup from the above terminate
891 // eglDestroySurface(srf1)
892 // eglTerminate(shared-display)
893
894 EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
895 mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
896 reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
897 EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
898 EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
899
900 EGLConfig config = EGL_NO_CONFIG_KHR;
901 EXPECT_TRUE(chooseConfig(&config));
902
903 mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
904 EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
905 EXPECT_TRUE(mSurface != EGL_NO_SURFACE);
906 ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
907
908 EXPECT_TRUE(createContext(config, &mContexts[0]));
909 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
910 EXPECT_TRUE(createContext(config, &mContexts[1]));
911 EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
912
913 // Must be after the eglMakeCurrent() so renderer string is initialized.
914 // TODO(http://www.anglebug.com/6304): Fails with Mac + OpenGL backend.
915 ANGLE_SKIP_TEST_IF(IsOSX() && IsOpenGL());
916
917 EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
918
919 eglDestroySurface(mDisplay, mSurface);
920 mSurface = EGL_NO_SURFACE;
921 EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
922 mContexts[0] = EGL_NO_CONTEXT;
923 eglTerminate(mDisplay);
924 EXPECT_EGL_SUCCESS();
925
926 eglDestroyContext(mDisplay, mContexts[1]);
927 mContexts[1] = EGL_NO_CONTEXT;
928 ASSERT_EGL_ERROR(EGL_NOT_INITIALIZED);
929 eglTerminate(mDisplay);
930 EXPECT_EGL_SUCCESS();
931 mDisplay = EGL_NO_DISPLAY;
932 }
933 } // anonymous namespace
934
935 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextSharingTest);
936 ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,
937 ES2_D3D9(),
938 ES2_D3D11(),
939 ES3_D3D11(),
940 ES2_METAL(),
941 ES2_OPENGL(),
942 ES3_OPENGL(),
943 ES2_VULKAN(),
944 ES3_VULKAN());
945
946 ANGLE_INSTANTIATE_TEST(EGLContextSharingTestNoFixture,
947 WithNoFixture(ES2_OPENGLES()),
948 WithNoFixture(ES3_OPENGLES()),
949 WithNoFixture(ES2_OPENGL()),
950 WithNoFixture(ES3_OPENGL()),
951 WithNoFixture(ES2_VULKAN()),
952 WithNoFixture(ES3_VULKAN()));
953