Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/angle
Path: blob/main_old/src/tests/egl_tests/EGLContextSharingTest.cpp
1693 views
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
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:
37
EGLContextSharingTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}
38
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:
64
EGLContextSharingTestNoFixture() : EGLContextSharingTest() {}
65
66
void testSetUp() override
67
{
68
mOsWindow = OSWindow::New();
69
mMajorVersion = GetParam().majorVersion;
70
}
71
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
102
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
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
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.
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
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
// Test creating two contexts in the global share group
192
mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
193
mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);
194
195
if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
196
{
197
// Make sure an error is generated and early-exit
198
ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
199
ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);
200
return;
201
}
202
203
ASSERT_EGL_SUCCESS();
204
205
ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
206
ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
207
eglDestroyContext(display, mContexts[0]);
208
mContexts[0] = EGL_NO_CONTEXT;
209
210
// Try creating a context that is not in the global share group but tries to share with a
211
// context that is
212
const EGLint notInShareGroupContextAttribs[] = {
213
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};
214
mContexts[0] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);
215
ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);
216
ASSERT_TRUE(mContexts[0] == EGL_NO_CONTEXT);
217
}
218
219
// Tests the sharing of textures using EGL_ANGLE_display_texture_share_group
220
TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)
221
{
222
EGLDisplay display = getEGLWindow()->getDisplay();
223
if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
224
{
225
std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
226
<< std::endl;
227
return;
228
}
229
230
EGLConfig config = getEGLWindow()->getConfig();
231
EGLSurface surface = getEGLWindow()->getSurface();
232
233
const EGLint inShareGroupContextAttribs[] = {
234
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
235
236
// Create two contexts in the global share group but not in the same context share group
237
mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
238
mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
239
240
ASSERT_EGL_SUCCESS();
241
242
ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);
243
ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);
244
245
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
246
ASSERT_EGL_SUCCESS();
247
248
// Create a texture and buffer in ctx 0
249
GLTexture textureFromCtx0;
250
glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
251
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
252
glBindTexture(GL_TEXTURE_2D, 0);
253
ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
254
255
GLBuffer bufferFromCtx0;
256
glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);
257
glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);
258
glBindBuffer(GL_ARRAY_BUFFER, 0);
259
ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));
260
261
ASSERT_GL_NO_ERROR();
262
263
// Switch to context 1 and verify that the texture is accessible but the buffer is not
264
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
265
ASSERT_EGL_SUCCESS();
266
267
ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
268
269
ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));
270
ASSERT_GL_NO_ERROR();
271
272
// Call readpixels on the texture to verify that the backend has proper support
273
GLFramebuffer fbo;
274
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
275
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);
276
277
GLubyte pixel[4];
278
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
279
ASSERT_GL_NO_ERROR();
280
281
// Switch back to context 0 and delete the buffer
282
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
283
ASSERT_EGL_SUCCESS();
284
}
285
286
// Tests that shared textures using EGL_ANGLE_display_texture_share_group are released when the last
287
// context is destroyed
288
TEST_P(EGLContextSharingTest, DisplayShareGroupReleasedWithLastContext)
289
{
290
EGLDisplay display = getEGLWindow()->getDisplay();
291
if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))
292
{
293
std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."
294
<< std::endl;
295
return;
296
}
297
298
EGLConfig config = getEGLWindow()->getConfig();
299
EGLSurface surface = getEGLWindow()->getSurface();
300
301
const EGLint inShareGroupContextAttribs[] = {
302
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};
303
304
// Create two contexts in the global share group but not in the same context share group
305
mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
306
mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
307
308
// Create a texture and buffer in ctx 0
309
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
310
GLTexture textureFromCtx0;
311
glBindTexture(GL_TEXTURE_2D, textureFromCtx0);
312
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
313
glBindTexture(GL_TEXTURE_2D, 0);
314
ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
315
316
// Switch to context 1 and verify that the texture is accessible
317
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));
318
ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));
319
320
// Destroy both contexts, the texture should be cleaned up automatically
321
ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[0]));
322
mContexts[0] = EGL_NO_CONTEXT;
323
ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[1]));
324
mContexts[1] = EGL_NO_CONTEXT;
325
326
// Unmake current, so the context can be released.
327
ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
328
329
// Create a new context and verify it cannot access the texture previously created
330
mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);
331
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
332
333
ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));
334
}
335
336
// Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
337
// on the Vulkan back-end where we manage object lifetime manually.
338
TEST_P(EGLContextSharingTest, TextureLifetime)
339
{
340
EGLWindow *eglWindow = getEGLWindow();
341
EGLConfig config = getEGLWindow()->getConfig();
342
EGLDisplay display = getEGLWindow()->getDisplay();
343
344
// Create a pbuffer surface for use with a shared context.
345
EGLSurface surface = eglWindow->getSurface();
346
EGLContext mainContext = eglWindow->getContext();
347
348
// Initialize a shared context.
349
mContexts[0] = eglCreateContext(display, config, mainContext, nullptr);
350
ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
351
352
// Create a Texture on the shared context.
353
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
354
355
constexpr GLsizei kTexSize = 2;
356
const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
357
GLColor::yellow};
358
GLTexture tex;
359
glBindTexture(GL_TEXTURE_2D, tex);
360
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
361
kTexData);
362
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
363
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
364
365
// Make the main Context current and draw with the texture.
366
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
367
368
glBindTexture(GL_TEXTURE_2D, tex);
369
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
370
glUseProgram(program);
371
372
// No uniform update because the update seems to hide the error on Vulkan.
373
374
// Enqueue the draw call.
375
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
376
EXPECT_GL_NO_ERROR();
377
378
// Delete the texture in the main context to orphan it.
379
// Do not read back the data to keep the commands in the graph.
380
tex.reset();
381
382
// Bind and delete the test context. This should trigger texture garbage collection.
383
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
384
SafeDestroyContext(display, mContexts[0]);
385
386
// Bind the main context to clean up the test.
387
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
388
}
389
390
// Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused
391
// on the Vulkan back-end where we manage object lifetime manually.
392
TEST_P(EGLContextSharingTest, SamplerLifetime)
393
{
394
EGLWindow *eglWindow = getEGLWindow();
395
EGLConfig config = getEGLWindow()->getConfig();
396
EGLDisplay display = getEGLWindow()->getDisplay();
397
398
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
399
ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_KHR_create_context"));
400
401
// Create a pbuffer surface for use with a shared context.
402
EGLSurface surface = eglWindow->getSurface();
403
EGLContext mainContext = eglWindow->getContext();
404
405
std::vector<EGLint> contextAttributes;
406
contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);
407
contextAttributes.push_back(getClientMajorVersion());
408
contextAttributes.push_back(EGL_NONE);
409
410
// Initialize a shared context.
411
mContexts[0] = eglCreateContext(display, config, mainContext, contextAttributes.data());
412
ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);
413
414
// Create a Texture on the shared context. Also create a Sampler object.
415
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
416
417
constexpr GLsizei kTexSize = 2;
418
const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,
419
GLColor::yellow};
420
GLTexture tex;
421
glBindTexture(GL_TEXTURE_2D, tex);
422
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
423
kTexData);
424
425
GLSampler sampler;
426
glBindSampler(0, sampler);
427
glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
428
glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
429
430
// Make the main Context current and draw with the texture and sampler.
431
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
432
433
glBindTexture(GL_TEXTURE_2D, tex);
434
glBindSampler(0, sampler);
435
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
436
glUseProgram(program);
437
438
// No uniform update because the update seems to hide the error on Vulkan.
439
440
// Enqueue the draw call.
441
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
442
EXPECT_GL_NO_ERROR();
443
444
// Delete the texture and sampler in the main context to orphan them.
445
// Do not read back the data to keep the commands in the graph.
446
tex.reset();
447
sampler.reset();
448
449
// Bind and delete the test context. This should trigger texture garbage collection.
450
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));
451
SafeDestroyContext(display, mContexts[0]);
452
453
// Bind the main context to clean up the test.
454
ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));
455
}
456
457
// Test that deleting an object reading from a shared object in one context doesn't cause the other
458
// context to crash. Mostly focused on the Vulkan back-end where we track resource dependencies in
459
// a graph.
460
TEST_P(EGLContextSharingTest, DeleteReaderOfSharedTexture)
461
{
462
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
463
// GL Fences require GLES 3.0+
464
ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);
465
466
// Initialize contexts
467
EGLWindow *window = getEGLWindow();
468
EGLDisplay dpy = window->getDisplay();
469
EGLConfig config = window->getConfig();
470
471
constexpr size_t kThreadCount = 2;
472
EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
473
EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
474
475
EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};
476
477
for (size_t t = 0; t < kThreadCount; ++t)
478
{
479
surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
480
EXPECT_EGL_SUCCESS();
481
482
ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0]);
483
EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
484
}
485
486
// Initialize test resources. They are done outside the threads to reduce the sources of
487
// errors and thus deadlock-free teardown.
488
ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
489
490
// Shared texture to read from.
491
constexpr GLsizei kTexSize = 1;
492
const GLColor kTexData = GLColor::red;
493
494
GLTexture sharedTex;
495
glBindTexture(GL_TEXTURE_2D, sharedTex);
496
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
497
&kTexData);
498
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
499
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
500
501
// Resources for each context.
502
GLRenderbuffer renderbuffer[kThreadCount];
503
GLFramebuffer fbo[kThreadCount];
504
GLProgram program[kThreadCount];
505
506
for (size_t t = 0; t < kThreadCount; ++t)
507
{
508
ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[t], surface[t], ctx[t]));
509
510
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[t]);
511
constexpr int kRenderbufferSize = 4;
512
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);
513
514
glBindFramebuffer(GL_FRAMEBUFFER, fbo[t]);
515
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
516
renderbuffer[t]);
517
518
glBindTexture(GL_TEXTURE_2D, sharedTex);
519
program[t].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
520
ASSERT_TRUE(program[t].valid());
521
}
522
523
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
524
525
// Synchronization tools to ensure the two threads are interleaved as designed by this test.
526
std::mutex mutex;
527
std::condition_variable condVar;
528
std::atomic<GLsync> deletingThreadSyncObj;
529
std::atomic<GLsync> continuingThreadSyncObj;
530
531
enum class Step
532
{
533
Start,
534
Thread0Draw,
535
Thread1Draw,
536
Thread0Delete,
537
Finish,
538
Abort,
539
};
540
Step currentStep = Step::Start;
541
542
std::thread deletingThread = std::thread([&]() {
543
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
544
545
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
546
EXPECT_EGL_SUCCESS();
547
548
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
549
550
// Draw using the shared texture.
551
drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
552
553
deletingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
554
ASSERT_GL_NO_ERROR();
555
// Force the fence to be created
556
glFlush();
557
558
// Wait for the other thread to also draw using the shared texture.
559
threadSynchronization.nextStep(Step::Thread0Draw);
560
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
561
562
ASSERT_TRUE(continuingThreadSyncObj != nullptr);
563
glWaitSync(continuingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
564
ASSERT_GL_NO_ERROR();
565
glDeleteSync(continuingThreadSyncObj);
566
ASSERT_GL_NO_ERROR();
567
continuingThreadSyncObj = nullptr;
568
569
// Delete this thread's framebuffer (reader of the shared texture).
570
fbo[0].reset();
571
572
// Wait for the other thread to use the shared texture again before unbinding the
573
// context (so no implicit flush happens).
574
threadSynchronization.nextStep(Step::Thread0Delete);
575
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
576
577
EXPECT_GL_NO_ERROR();
578
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
579
EXPECT_EGL_SUCCESS();
580
});
581
582
std::thread continuingThread = std::thread([&]() {
583
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
584
585
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
586
EXPECT_EGL_SUCCESS();
587
588
// Wait for first thread to draw using the shared texture.
589
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
590
591
ASSERT_TRUE(deletingThreadSyncObj != nullptr);
592
glWaitSync(deletingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
593
ASSERT_GL_NO_ERROR();
594
glDeleteSync(deletingThreadSyncObj);
595
ASSERT_GL_NO_ERROR();
596
deletingThreadSyncObj = nullptr;
597
598
// Draw using the shared texture.
599
drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
600
601
continuingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
602
ASSERT_GL_NO_ERROR();
603
// Force the fence to be created
604
glFlush();
605
606
// Wait for the other thread to delete its framebuffer.
607
threadSynchronization.nextStep(Step::Thread1Draw);
608
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Delete));
609
610
// Write to the shared texture differently, so a dependency is created from the previous
611
// readers of the shared texture (the two framebuffers of the two threads) to the new
612
// commands being recorded for the shared texture.
613
//
614
// If the backend attempts to create a dependency from nodes associated with the
615
// previous readers of the texture to the new node that will contain the following
616
// commands, there will be a use-after-free error.
617
const GLColor kTexData2 = GLColor::green;
618
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
619
&kTexData2);
620
drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);
621
622
threadSynchronization.nextStep(Step::Finish);
623
624
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
625
EXPECT_EGL_SUCCESS();
626
});
627
628
deletingThread.join();
629
continuingThread.join();
630
631
ASSERT_NE(currentStep, Step::Abort);
632
633
// Clean up
634
for (size_t t = 0; t < kThreadCount; ++t)
635
{
636
eglDestroySurface(dpy, surface[t]);
637
eglDestroyContext(dpy, ctx[t]);
638
}
639
}
640
641
// Test that eglTerminate() with a thread doesn't cause other threads to crash.
642
TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultiThreaded)
643
{
644
// http://anglebug.com/6208
645
// The following EGL calls led to a crash in eglMakeCurrent():
646
//
647
// Thread A: eglMakeCurrent(context A)
648
// Thread B: eglDestroyContext(context A)
649
// B: eglTerminate() <<--- this release context A
650
// Thread A: eglMakeCurrent(context B)
651
652
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
653
mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
654
reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
655
EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
656
EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
657
658
EGLConfig config = EGL_NO_CONFIG_KHR;
659
EXPECT_TRUE(chooseConfig(&config));
660
661
mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
662
EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
663
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
664
665
EXPECT_TRUE(createContext(config, &mContexts[0]));
666
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
667
668
// Must be after the eglMakeCurrent() so renderer string is initialized.
669
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
670
// TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.
671
ANGLE_SKIP_TEST_IF(IsOpenGLES());
672
673
// Synchronization tools to ensure the two threads are interleaved as designed by this test.
674
std::mutex mutex;
675
std::condition_variable condVar;
676
677
enum class Step
678
{
679
Start,
680
Thread0Clear,
681
Thread1Terminate,
682
Thread0MakeCurrentContext1,
683
Finish,
684
Abort,
685
};
686
Step currentStep = Step::Start;
687
688
std::thread thread0 = std::thread([&]() {
689
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
690
691
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
692
693
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
694
695
// Clear and read back to make sure thread 0 uses context 0.
696
glClearColor(1.0, 0.0, 0.0, 1.0);
697
glClear(GL_COLOR_BUFFER_BIT);
698
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
699
700
// Wait for thread 1 to clear.
701
threadSynchronization.nextStep(Step::Thread0Clear);
702
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
703
704
// First Display was terminated, so we need to create a new one to create a new Context.
705
mDisplay = eglGetPlatformDisplayEXT(
706
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
707
EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
708
EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
709
config = EGL_NO_CONFIG_KHR;
710
EXPECT_TRUE(chooseConfig(&config));
711
EXPECT_TRUE(createContext(config, &mContexts[1]));
712
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
713
714
// Clear and read back to make sure thread 0 uses context 1.
715
glClearColor(1.0, 1.0, 0.0, 1.0);
716
glClear(GL_COLOR_BUFFER_BIT);
717
EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
718
719
// Cleanup
720
EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
721
EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
722
eglDestroySurface(mDisplay, mSurface);
723
mSurface = EGL_NO_SURFACE;
724
eglTerminate(mDisplay);
725
mDisplay = EGL_NO_DISPLAY;
726
EXPECT_EGL_SUCCESS();
727
728
threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
729
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
730
});
731
732
std::thread thread1 = std::thread([&]() {
733
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
734
735
// Wait for thread 0 to clear.
736
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
737
738
// Destroy context 0 while thread1 has it current.
739
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
740
EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[0]));
741
EXPECT_EGL_SUCCESS();
742
eglTerminate(mDisplay);
743
mDisplay = EGL_NO_DISPLAY;
744
EXPECT_EGL_SUCCESS();
745
746
// Wait for the thread 0 to make a new context and glClear().
747
threadSynchronization.nextStep(Step::Thread1Terminate);
748
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
749
750
threadSynchronization.nextStep(Step::Finish);
751
});
752
753
thread0.join();
754
thread1.join();
755
756
ASSERT_NE(currentStep, Step::Abort);
757
}
758
759
// Test that eglDestoryContext() can be called multiple times on the same Context without causing
760
// errors.
761
TEST_P(EGLContextSharingTestNoFixture, EglDestoryContextManyTimesSameContext)
762
{
763
EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
764
mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,
765
reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
766
EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
767
EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
768
769
EGLConfig config = EGL_NO_CONFIG_KHR;
770
EXPECT_TRUE(chooseConfig(&config));
771
772
mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);
773
EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));
774
ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
775
776
EXPECT_TRUE(createContext(config, &mContexts[0]));
777
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
778
779
// Must be after the eglMakeCurrent() so renderer string is initialized.
780
ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
781
// TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.
782
ANGLE_SKIP_TEST_IF(IsOpenGLES());
783
784
// Synchronization tools to ensure the two threads are interleaved as designed by this test.
785
std::mutex mutex;
786
std::condition_variable condVar;
787
788
enum class Step
789
{
790
Start,
791
Thread0Clear,
792
Thread1Terminate,
793
Thread0MakeCurrentContext1,
794
Finish,
795
Abort,
796
};
797
Step currentStep = Step::Start;
798
799
std::thread thread0 = std::thread([&]() {
800
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
801
802
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
803
804
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));
805
806
// Clear and read back to make sure thread 0 uses context 0.
807
glClearColor(1.0, 0.0, 0.0, 1.0);
808
glClear(GL_COLOR_BUFFER_BIT);
809
EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
810
811
// Wait for thread 1 to clear.
812
threadSynchronization.nextStep(Step::Thread0Clear);
813
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));
814
815
// First Display was terminated, so we need to create a new one to create a new Context.
816
mDisplay = eglGetPlatformDisplayEXT(
817
EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
818
EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);
819
EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));
820
config = EGL_NO_CONFIG_KHR;
821
EXPECT_TRUE(chooseConfig(&config));
822
EXPECT_TRUE(createContext(config, &mContexts[1]));
823
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));
824
825
// Clear and read back to make sure thread 0 uses context 1.
826
glClearColor(1.0, 1.0, 0.0, 1.0);
827
glClear(GL_COLOR_BUFFER_BIT);
828
EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);
829
830
// Cleanup
831
EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
832
EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));
833
eglDestroySurface(mDisplay, mSurface);
834
mSurface = EGL_NO_SURFACE;
835
eglTerminate(mDisplay);
836
mDisplay = EGL_NO_DISPLAY;
837
EXPECT_EGL_SUCCESS();
838
839
threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);
840
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
841
});
842
843
std::thread thread1 = std::thread([&]() {
844
ThreadSynchronization<Step> threadSynchronization(&currentStep, &mutex, &condVar);
845
846
// Wait for thread 0 to clear.
847
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
848
849
EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
850
851
// Destroy context 0 5 times while thread1 has it current.
852
EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
853
EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
854
EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
855
EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
856
EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));
857
mContexts[0] = EGL_NO_CONTEXT;
858
859
eglTerminate(mDisplay);
860
mDisplay = EGL_NO_DISPLAY;
861
EXPECT_EGL_SUCCESS();
862
863
// Wait for the thread 0 to make a new context and glClear().
864
threadSynchronization.nextStep(Step::Thread1Terminate);
865
ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));
866
867
threadSynchronization.nextStep(Step::Finish);
868
});
869
870
thread0.join();
871
thread1.join();
872
873
ASSERT_NE(currentStep, Step::Abort);
874
}
875
} // anonymous namespace
876
877
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextSharingTest);
878
ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,
879
ES2_D3D9(),
880
ES2_D3D11(),
881
ES3_D3D11(),
882
ES2_METAL(),
883
ES2_OPENGL(),
884
ES3_OPENGL(),
885
ES2_VULKAN(),
886
ES3_VULKAN());
887
888
ANGLE_INSTANTIATE_TEST(EGLContextSharingTestNoFixture,
889
WithNoFixture(ES2_OPENGLES()),
890
WithNoFixture(ES3_OPENGLES()),
891
WithNoFixture(ES2_OPENGL()),
892
WithNoFixture(ES3_OPENGL()),
893
WithNoFixture(ES2_VULKAN()),
894
WithNoFixture(ES3_VULKAN()));
895