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