1 //
2 // Copyright 2018 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 // MulithreadingTest.cpp : Tests of multithreaded rendering
7
8 #include "test_utils/ANGLETest.h"
9 #include "test_utils/MultiThreadSteps.h"
10 #include "test_utils/gl_raii.h"
11 #include "util/EGLWindow.h"
12 #include "util/test_utils.h"
13
14 #include <atomic>
15 #include <mutex>
16 #include <thread>
17
18 namespace angle
19 {
20
21 class MultithreadingTest : public ANGLETest<>
22 {
23 public:
24 static constexpr uint32_t kSize = 512;
25
26 protected:
MultithreadingTest()27 MultithreadingTest()
28 {
29 setWindowWidth(kSize);
30 setWindowHeight(kSize);
31 setConfigRedBits(8);
32 setConfigGreenBits(8);
33 setConfigBlueBits(8);
34 setConfigAlphaBits(8);
35 }
36
hasFenceSyncExtension() const37 bool hasFenceSyncExtension() const
38 {
39 return IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_fence_sync");
40 }
hasWaitSyncExtension() const41 bool hasWaitSyncExtension() const
42 {
43 return hasFenceSyncExtension() &&
44 IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(), "EGL_KHR_wait_sync");
45 }
hasGLSyncExtension() const46 bool hasGLSyncExtension() const { return IsGLExtensionEnabled("GL_OES_EGL_sync"); }
47
createMultithreadedContext(EGLWindow * window,EGLContext shareCtx)48 EGLContext createMultithreadedContext(EGLWindow *window, EGLContext shareCtx)
49 {
50 EGLint attribs[] = {EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, mVirtualizationGroup++,
51 EGL_NONE};
52 if (!IsEGLDisplayExtensionEnabled(getEGLWindow()->getDisplay(),
53 "EGL_ANGLE_context_virtualization"))
54 {
55 attribs[0] = EGL_NONE;
56 }
57
58 return window->createContext(shareCtx, attribs);
59 }
60
runMultithreadedGLTest(std::function<void (EGLSurface surface,size_t threadIndex)> testBody,size_t threadCount)61 void runMultithreadedGLTest(
62 std::function<void(EGLSurface surface, size_t threadIndex)> testBody,
63 size_t threadCount)
64 {
65 std::mutex mutex;
66
67 EGLWindow *window = getEGLWindow();
68 EGLDisplay dpy = window->getDisplay();
69 EGLConfig config = window->getConfig();
70
71 constexpr EGLint kPBufferSize = 256;
72
73 std::vector<std::thread> threads(threadCount);
74 for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
75 {
76 threads[threadIdx] = std::thread([&, threadIdx]() {
77 EGLSurface surface = EGL_NO_SURFACE;
78 EGLContext ctx = EGL_NO_CONTEXT;
79
80 {
81 std::lock_guard<decltype(mutex)> lock(mutex);
82
83 // Initialize the pbuffer and context
84 EGLint pbufferAttributes[] = {
85 EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
86 };
87 surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
88 EXPECT_EGL_SUCCESS();
89
90 ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
91 EXPECT_NE(EGL_NO_CONTEXT, ctx);
92
93 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
94 EXPECT_EGL_SUCCESS();
95 }
96
97 testBody(surface, threadIdx);
98
99 {
100 std::lock_guard<decltype(mutex)> lock(mutex);
101
102 // Clean up
103 EXPECT_EGL_TRUE(
104 eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
105 EXPECT_EGL_SUCCESS();
106
107 eglDestroySurface(dpy, surface);
108 eglDestroyContext(dpy, ctx);
109 }
110 });
111 }
112
113 for (std::thread &thread : threads)
114 {
115 thread.join();
116 }
117 }
118
119 std::atomic<EGLint> mVirtualizationGroup;
120 };
121
122 class MultithreadingTestES3 : public MultithreadingTest
123 {
124 public:
125 void textureThreadFunction(bool useDraw);
126 void mainThreadDraw(bool useDraw);
127
128 protected:
MultithreadingTestES3()129 MultithreadingTestES3()
130 : mTexture2D(0), mExitThread(false), mMainThreadSyncObj(NULL), mSecondThreadSyncObj(NULL)
131 {
132 setWindowWidth(kSize);
133 setWindowHeight(kSize);
134 setConfigRedBits(8);
135 setConfigGreenBits(8);
136 setConfigBlueBits(8);
137 setConfigAlphaBits(8);
138 }
139
create2DTexture()140 GLuint create2DTexture()
141 {
142 GLuint texture2D;
143 glGenTextures(1, &texture2D);
144 glActiveTexture(GL_TEXTURE0);
145 glBindTexture(GL_TEXTURE_2D, texture2D);
146 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
147 nullptr);
148 EXPECT_GL_NO_ERROR();
149 return texture2D;
150 }
151
testSetUp()152 void testSetUp() override { mTexture2D = create2DTexture(); }
153
testTearDown()154 void testTearDown() override
155 {
156 if (mTexture2D)
157 {
158 glDeleteTextures(1, &mTexture2D);
159 }
160 }
161
162 enum class FenceTest
163 {
164 ClientWait,
165 ServerWait,
166 GetStatus,
167 };
168 enum class FlushMethod
169 {
170 Flush,
171 Finish,
172 };
173 void testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod);
174
175 enum class DrawOrder
176 {
177 Before,
178 After,
179 };
180 void testFramebufferFetch(DrawOrder drawOrder);
181
182 std::mutex mMutex;
183 GLuint mTexture2D;
184 std::atomic<bool> mExitThread;
185 std::atomic<bool> mDrawGreen; // Toggle drawing green or red
186 std::atomic<GLsync> mMainThreadSyncObj;
187 std::atomic<GLsync> mSecondThreadSyncObj;
188 };
189
190 // Test that it's possible to make one context current on different threads
TEST_P(MultithreadingTest,MakeCurrentSingleContext)191 TEST_P(MultithreadingTest, MakeCurrentSingleContext)
192 {
193 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
194
195 std::mutex mutex;
196
197 EGLWindow *window = getEGLWindow();
198 EGLDisplay dpy = window->getDisplay();
199 EGLContext ctx = window->getContext();
200 EGLSurface surface = window->getSurface();
201
202 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
203 EXPECT_EGL_SUCCESS();
204
205 constexpr size_t kThreadCount = 16;
206 std::array<std::thread, kThreadCount> threads;
207 for (std::thread &thread : threads)
208 {
209 thread = std::thread([&]() {
210 std::lock_guard<decltype(mutex)> lock(mutex);
211
212 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
213 EXPECT_EGL_SUCCESS();
214
215 EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
216 EXPECT_EGL_SUCCESS();
217
218 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
219 EXPECT_EGL_SUCCESS();
220 });
221 }
222
223 for (std::thread &thread : threads)
224 {
225 thread.join();
226 }
227
228 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
229 EXPECT_EGL_SUCCESS();
230 }
231
232 // Test that multiple threads can clear and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextClear)233 TEST_P(MultithreadingTest, MultiContextClear)
234 {
235 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
236
237 auto testBody = [](EGLSurface surface, size_t thread) {
238 constexpr size_t kIterationsPerThread = 32;
239 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
240 {
241 // Base the clear color on the thread and iteration indexes so every clear color is
242 // unique
243 const GLColor color(static_cast<GLubyte>(thread % 255),
244 static_cast<GLubyte>(iteration % 255), 0, 255);
245 const angle::Vector4 floatColor = color.toNormalizedVector();
246
247 glClearColor(floatColor[0], floatColor[1], floatColor[2], floatColor[3]);
248 EXPECT_GL_NO_ERROR();
249
250 glClear(GL_COLOR_BUFFER_BIT);
251 EXPECT_GL_NO_ERROR();
252
253 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
254 }
255 };
256 runMultithreadedGLTest(
257 testBody,
258 getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 72);
259 }
260
261 // Verify that threads can interleave eglDestroyContext and draw calls without
262 // any crashes.
TEST_P(MultithreadingTest,MultiContextDeleteDraw)263 TEST_P(MultithreadingTest, MultiContextDeleteDraw)
264 {
265 // Skip this test on non-D3D11 backends, as it has the potential to time-out
266 // and this test was originally intended to catch a crash on the D3D11 backend.
267 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
268 ANGLE_SKIP_TEST_IF(!IsD3D11());
269
270 EGLWindow *window = getEGLWindow();
271 EGLDisplay dpy = window->getDisplay();
272 EGLConfig config = window->getConfig();
273
274 std::thread t1 = std::thread([&]() {
275 // 5000 is chosen here as it reliably reproduces the former crash.
276 for (int i = 0; i < 5000; i++)
277 {
278 EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
279 EGLContext ctx2 = createMultithreadedContext(window, EGL_NO_CONTEXT);
280
281 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
282 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
283
284 EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
285 EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
286 }
287 });
288
289 std::thread t2 = std::thread([&]() {
290 EGLint pbufferAttributes[] = {
291 EGL_WIDTH, 256, EGL_HEIGHT, 256, EGL_NONE, EGL_NONE,
292 };
293
294 EGLSurface surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
295 EXPECT_EGL_SUCCESS();
296
297 auto ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
298 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
299
300 constexpr size_t kIterationsPerThread = 512;
301 constexpr size_t kDrawsPerIteration = 512;
302
303 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
304 glUseProgram(program);
305
306 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
307
308 auto quadVertices = GetQuadVertices();
309
310 GLBuffer vertexBuffer;
311 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
312 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
313
314 GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
315 glEnableVertexAttribArray(positionLocation);
316 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
317 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
318 {
319 const GLColor color(static_cast<GLubyte>(15151 % 255),
320 static_cast<GLubyte>(iteration % 255), 0, 255);
321 const angle::Vector4 floatColor = color.toNormalizedVector();
322 glUniform4fv(colorLocation, 1, floatColor.data());
323 for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
324 {
325 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
326 glDrawArrays(GL_TRIANGLES, 0, 6);
327 }
328 }
329 });
330
331 t1.join();
332 t2.join();
333 }
334
335 // Test that multiple threads can draw and readback pixels successfully at the same time
TEST_P(MultithreadingTest,MultiContextDraw)336 TEST_P(MultithreadingTest, MultiContextDraw)
337 {
338 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
339
340 ANGLE_SKIP_TEST_IF(isSwiftshader());
341
342 auto testBody = [](EGLSurface surface, size_t thread) {
343 constexpr size_t kIterationsPerThread = 32;
344 constexpr size_t kDrawsPerIteration = 500;
345
346 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
347 glUseProgram(program);
348
349 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
350
351 auto quadVertices = GetQuadVertices();
352
353 GLBuffer vertexBuffer;
354 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
355 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
356
357 GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
358 glEnableVertexAttribArray(positionLocation);
359 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
360
361 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
362 {
363 // Base the clear color on the thread and iteration indexes so every clear color is
364 // unique
365 const GLColor color(static_cast<GLubyte>(thread % 255),
366 static_cast<GLubyte>(iteration % 255), 0, 255);
367 const angle::Vector4 floatColor = color.toNormalizedVector();
368 glUniform4fv(colorLocation, 1, floatColor.data());
369
370 for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
371 {
372 glDrawArrays(GL_TRIANGLES, 0, 6);
373 }
374
375 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
376 }
377 };
378 runMultithreadedGLTest(testBody, 4);
379 }
380
381 // Test that multiple threads can draw and read back pixels correctly.
382 // Using eglSwapBuffers stresses race conditions around use of QueueSerials.
TEST_P(MultithreadingTest,MultiContextDrawWithSwapBuffers)383 TEST_P(MultithreadingTest, MultiContextDrawWithSwapBuffers)
384 {
385 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
386
387 // http://anglebug.com/42263666
388 ANGLE_SKIP_TEST_IF(IsAndroid() && IsOpenGLES());
389
390 EGLWindow *window = getEGLWindow();
391 EGLDisplay dpy = window->getDisplay();
392
393 auto testBody = [dpy](EGLSurface surface, size_t thread) {
394 constexpr size_t kIterationsPerThread = 100;
395 constexpr size_t kDrawsPerIteration = 10;
396
397 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
398 glUseProgram(program);
399
400 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
401
402 auto quadVertices = GetQuadVertices();
403
404 GLBuffer vertexBuffer;
405 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
406 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
407
408 GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
409 glEnableVertexAttribArray(positionLocation);
410 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
411
412 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
413 {
414 // Base the clear color on the thread and iteration indexes so every clear color is
415 // unique
416 const GLColor color(static_cast<GLubyte>(thread % 255),
417 static_cast<GLubyte>(iteration % 255), 0, 255);
418 const angle::Vector4 floatColor = color.toNormalizedVector();
419 glUniform4fv(colorLocation, 1, floatColor.data());
420
421 for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
422 {
423 glDrawArrays(GL_TRIANGLES, 0, 6);
424 }
425
426 EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
427 EXPECT_EGL_SUCCESS();
428
429 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
430 }
431 };
432 runMultithreadedGLTest(
433 testBody,
434 getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 32);
435 }
436
437 // Test that ANGLE handles multiple threads creating and destroying resources (vertex buffer in this
438 // case).
TEST_P(MultithreadingTest,MultiContextCreateAndDeleteResources)439 TEST_P(MultithreadingTest, MultiContextCreateAndDeleteResources)
440 {
441 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
442
443 EGLWindow *window = getEGLWindow();
444 EGLDisplay dpy = window->getDisplay();
445
446 auto testBody = [dpy](EGLSurface surface, size_t thread) {
447 constexpr size_t kIterationsPerThread = 32;
448 constexpr size_t kDrawsPerIteration = 1;
449
450 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
451 glUseProgram(program);
452
453 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
454
455 auto quadVertices = GetQuadVertices();
456
457 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
458 {
459 GLBuffer vertexBuffer;
460 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
461 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(),
462 GL_STATIC_DRAW);
463
464 GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
465 glEnableVertexAttribArray(positionLocation);
466 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
467
468 // Base the clear color on the thread and iteration indexes so every clear color is
469 // unique
470 const GLColor color(static_cast<GLubyte>(thread % 255),
471 static_cast<GLubyte>(iteration % 255), 0, 255);
472 const angle::Vector4 floatColor = color.toNormalizedVector();
473 glUniform4fv(colorLocation, 1, floatColor.data());
474
475 for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
476 {
477 glDrawArrays(GL_TRIANGLES, 0, 6);
478 }
479
480 EXPECT_EGL_TRUE(eglSwapBuffers(dpy, surface));
481 EXPECT_EGL_SUCCESS();
482
483 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
484 }
485 glFinish();
486 };
487 runMultithreadedGLTest(
488 testBody,
489 getEGLWindow()->isFeatureEnabled(Feature::SlowAsyncCommandQueueForTesting) ? 4 : 32);
490 }
491
TEST_P(MultithreadingTest,MultiCreateContext)492 TEST_P(MultithreadingTest, MultiCreateContext)
493 {
494 // Supported by CGL, GLX, and WGL (https://anglebug.com/42263324)
495 // Not supported on Ozone (https://crbug.com/1103009)
496 ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsMac()) || IsOzone());
497
498 EGLWindow *window = getEGLWindow();
499 EGLDisplay dpy = window->getDisplay();
500 EGLContext ctx = window->getContext();
501 EGLSurface surface = window->getSurface();
502
503 // Un-makeCurrent the test window's context
504 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
505 EXPECT_EGL_SUCCESS();
506
507 constexpr size_t kThreadCount = 16;
508 std::atomic<uint32_t> barrier(0);
509 std::vector<std::thread> threads(kThreadCount);
510 std::vector<EGLContext> contexts(kThreadCount);
511 for (size_t threadIdx = 0; threadIdx < kThreadCount; threadIdx++)
512 {
513 threads[threadIdx] = std::thread([&, threadIdx]() {
514 contexts[threadIdx] = EGL_NO_CONTEXT;
515 {
516 contexts[threadIdx] = createMultithreadedContext(window, EGL_NO_CONTEXT);
517 EXPECT_NE(EGL_NO_CONTEXT, contexts[threadIdx]);
518
519 barrier++;
520 }
521
522 while (barrier < kThreadCount)
523 {
524 }
525
526 {
527 EXPECT_TRUE(eglDestroyContext(dpy, contexts[threadIdx]));
528 }
529 });
530 }
531
532 for (std::thread &thread : threads)
533 {
534 thread.join();
535 }
536
537 // Re-make current the test window's context for teardown.
538 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
539 EXPECT_EGL_SUCCESS();
540 }
541
542 // Create multiple shared context and draw with shared vertex buffer simutanously
TEST_P(MultithreadingTest,CreateMultiSharedContextAndDraw)543 TEST_P(MultithreadingTest, CreateMultiSharedContextAndDraw)
544 {
545 // Supported by CGL, GLX, and WGL (https://anglebug.com/42263324)
546 // Not supported on Ozone (https://crbug.com/1103009)
547 ANGLE_SKIP_TEST_IF(!(IsWindows() || IsLinux() || IsMac()) || IsOzone());
548 EGLWindow *window = getEGLWindow();
549 EGLDisplay dpy = window->getDisplay();
550 EGLConfig config = window->getConfig();
551 constexpr EGLint kPBufferSize = 256;
552
553 // Initialize the pbuffer and context
554 EGLint pbufferAttributes[] = {
555 EGL_WIDTH, kPBufferSize, EGL_HEIGHT, kPBufferSize, EGL_NONE, EGL_NONE,
556 };
557 EGLSurface sharedSurface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
558 EXPECT_EGL_SUCCESS();
559 EGLContext sharedCtx = createMultithreadedContext(window, EGL_NO_CONTEXT);
560 EXPECT_NE(EGL_NO_CONTEXT, sharedCtx);
561 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, sharedSurface, sharedSurface, sharedCtx));
562 EXPECT_EGL_SUCCESS();
563
564 // Create a shared vertextBuffer
565 auto quadVertices = GetQuadVertices();
566 GLBuffer sharedVertexBuffer;
567 glBindBuffer(GL_ARRAY_BUFFER, sharedVertexBuffer);
568 glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * 6, quadVertices.data(), GL_STATIC_DRAW);
569 ASSERT_GL_NO_ERROR();
570
571 // Now draw with the buffer and verify
572 {
573 ANGLE_GL_PROGRAM(sharedProgram, essl1_shaders::vs::Simple(),
574 essl1_shaders::fs::UniformColor());
575 glUseProgram(sharedProgram);
576 GLint colorLocation = glGetUniformLocation(sharedProgram, essl1_shaders::ColorUniform());
577 GLint positionLocation =
578 glGetAttribLocation(sharedProgram, essl1_shaders::PositionAttrib());
579 glEnableVertexAttribArray(positionLocation);
580 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
581 const GLColor color(0, 0, 0, 255);
582 const angle::Vector4 floatColor = color.toNormalizedVector();
583 glUniform4fv(colorLocation, 1, floatColor.data());
584 glDrawArrays(GL_TRIANGLES, 0, 6);
585 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
586 }
587
588 // Create shared context in their own threads and draw with the shared vertex buffer at the same
589 // time.
590 size_t threadCount = 16;
591 constexpr size_t kIterationsPerThread = 3;
592 constexpr size_t kDrawsPerIteration = 50;
593 std::vector<std::thread> threads(threadCount);
594 std::atomic<uint32_t> numOfContextsCreated(0);
595 std::mutex mutex;
596 for (size_t threadIdx = 0; threadIdx < threadCount; threadIdx++)
597 {
598 threads[threadIdx] = std::thread([&, threadIdx]() {
599 EGLSurface surface = EGL_NO_SURFACE;
600 EGLContext ctx = EGL_NO_CONTEXT;
601
602 {
603 std::lock_guard<decltype(mutex)> lock(mutex);
604 // Initialize the pbuffer and context
605 surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
606 EXPECT_EGL_SUCCESS();
607 ctx = createMultithreadedContext(window, /*EGL_NO_CONTEXT*/ sharedCtx);
608 EXPECT_NE(EGL_NO_CONTEXT, ctx);
609 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
610 EXPECT_EGL_SUCCESS();
611 numOfContextsCreated++;
612 }
613
614 // Wait for all contexts created.
615 while (numOfContextsCreated < threadCount)
616 {
617 }
618
619 // Now draw with shared vertex buffer
620 {
621 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(),
622 essl1_shaders::fs::UniformColor());
623 glUseProgram(program);
624
625 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
626 GLint positionLocation =
627 glGetAttribLocation(program, essl1_shaders::PositionAttrib());
628
629 // Use sharedVertexBuffer
630 glBindBuffer(GL_ARRAY_BUFFER, sharedVertexBuffer);
631 glEnableVertexAttribArray(positionLocation);
632 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, 0);
633
634 for (size_t iteration = 0; iteration < kIterationsPerThread; iteration++)
635 {
636 // Base the clear color on the thread and iteration indexes so every clear color
637 // is unique
638 const GLColor color(static_cast<GLubyte>(threadIdx % 255),
639 static_cast<GLubyte>(iteration % 255), 0, 255);
640 const angle::Vector4 floatColor = color.toNormalizedVector();
641 glUniform4fv(colorLocation, 1, floatColor.data());
642
643 for (size_t draw = 0; draw < kDrawsPerIteration; draw++)
644 {
645 glDrawArrays(GL_TRIANGLES, 0, 6);
646 }
647
648 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
649 }
650 }
651
652 // tear down shared context
653 {
654 std::lock_guard<decltype(mutex)> lock(mutex);
655 EXPECT_EGL_TRUE(
656 eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
657 EXPECT_EGL_SUCCESS();
658 eglDestroySurface(dpy, surface);
659 eglDestroyContext(dpy, ctx);
660 }
661 });
662 }
663
664 for (std::thread &thread : threads)
665 {
666 thread.join();
667 }
668
669 eglDestroySurface(dpy, sharedSurface);
670 eglDestroyContext(dpy, sharedCtx);
671
672 // Re-make current the test window's context for teardown.
673 EXPECT_EGL_TRUE(
674 eglMakeCurrent(dpy, window->getSurface(), window->getSurface(), window->getContext()));
675 EXPECT_EGL_SUCCESS();
676 }
677
678 // Producer/Consumer test using EGLImages and EGLSyncs
TEST_P(MultithreadingTest,EGLImageProduceConsume)679 TEST_P(MultithreadingTest, EGLImageProduceConsume)
680 {
681 EGLWindow *window = getEGLWindow();
682 EGLDisplay dpy = window->getDisplay();
683 EGLContext rootCtx = window->getContext();
684
685 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image"));
686 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
687 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_fence_sync"));
688
689 struct sharedImage
690 {
691 EGLImage image;
692 EGLSync sync;
693 GLuint rootTexture;
694 };
695
696 std::mutex mutex;
697 std::vector<sharedImage> waitingForProduce;
698 std::vector<sharedImage> waitingForConsume;
699
700 constexpr size_t kNumImages = 10;
701 for (size_t i = 0; i < kNumImages; i++)
702 {
703 GLuint texture;
704 glGenTextures(1, &texture);
705 glBindTexture(GL_TEXTURE_2D, texture);
706 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
707
708 sharedImage img;
709 img.image = eglCreateImageKHR(dpy, rootCtx, EGL_GL_TEXTURE_2D_KHR,
710 reinterpret_cast<EGLClientBuffer>(texture), nullptr);
711 img.sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
712 img.rootTexture = texture;
713
714 waitingForProduce.push_back(std::move(img));
715 }
716
717 constexpr size_t kIterations = 10000;
718
719 std::thread producerThread([&]() {
720 EGLContext ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
721 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx));
722
723 {
724 ANGLE_GL_PROGRAM(drawColor, essl1_shaders::vs::Simple(),
725 essl1_shaders::fs::UniformColor());
726 glUseProgram(drawColor);
727 GLint colorUniformLocation =
728 glGetUniformLocation(drawColor, angle::essl1_shaders::ColorUniform());
729 ASSERT_NE(colorUniformLocation, -1);
730
731 size_t iteration = 0;
732 while (iteration < kIterations)
733 {
734 sharedImage img;
735 {
736 std::lock_guard<decltype(mutex)> lock(mutex);
737 if (waitingForProduce.empty())
738 {
739 continue;
740 }
741 img = std::move(waitingForProduce.back());
742 waitingForProduce.pop_back();
743 }
744
745 eglWaitSync(dpy, img.sync, 0);
746 EXPECT_EGL_SUCCESS();
747
748 eglDestroySync(dpy, img.sync);
749
750 GLTexture texture;
751 glBindTexture(GL_TEXTURE_2D, texture);
752 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img.image);
753 EXPECT_GL_NO_ERROR();
754
755 GLFramebuffer fbo;
756 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
757 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture,
758 0);
759
760 glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
761 glClear(GL_COLOR_BUFFER_BIT);
762
763 glUniform4f(colorUniformLocation, float(iteration) / kIterations, 0.0f, 0.0f, 1.0f);
764 drawQuad(drawColor, essl1_shaders::PositionAttrib(), 0);
765
766 glBindTexture(GL_TEXTURE_2D, 0);
767 glBindFramebuffer(GL_FRAMEBUFFER, 0);
768
769 img.sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
770 EXPECT_EGL_SUCCESS();
771 glFlush();
772
773 {
774 std::lock_guard<decltype(mutex)> lock(mutex);
775 waitingForConsume.insert(waitingForConsume.begin(), std::move(img));
776 }
777
778 iteration++;
779 }
780 }
781
782 eglDestroyContext(dpy, ctx);
783 });
784
785 std::thread consumerThread([&]() {
786 EGLContext ctx = createMultithreadedContext(window, EGL_NO_CONTEXT);
787 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx));
788
789 {
790 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
791 essl1_shaders::fs::Texture2D());
792 glUseProgram(drawTexture);
793 GLint textureUniformLocation =
794 glGetUniformLocation(drawTexture, angle::essl1_shaders::Texture2DUniform());
795 ASSERT_NE(textureUniformLocation, -1);
796 glUniform1i(textureUniformLocation, 0);
797 glActiveTexture(GL_TEXTURE0);
798
799 GLTexture backbufferTexture;
800 glBindTexture(GL_TEXTURE_2D, backbufferTexture);
801 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE,
802 nullptr);
803
804 GLFramebuffer fbo;
805 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
806 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
807 backbufferTexture, 0);
808
809 size_t iteration = 0;
810 while (iteration < kIterations)
811 {
812 sharedImage img;
813 {
814 std::lock_guard<decltype(mutex)> lock(mutex);
815 if (waitingForConsume.empty())
816 {
817 continue;
818 }
819 img = std::move(waitingForConsume.back());
820 waitingForConsume.pop_back();
821 }
822
823 eglWaitSync(dpy, img.sync, 0);
824 EXPECT_EGL_SUCCESS();
825 eglDestroySync(dpy, img.sync);
826
827 GLTexture texture;
828 glBindTexture(GL_TEXTURE_2D, texture);
829 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, img.image);
830 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
831 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
832
833 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0);
834 EXPECT_GL_NO_ERROR();
835
836 glBindTexture(GL_TEXTURE_2D, 0);
837
838 img.sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
839 EXPECT_EGL_SUCCESS();
840 glFlush();
841
842 {
843 std::lock_guard<decltype(mutex)> lock(mutex);
844 waitingForProduce.insert(waitingForProduce.begin(), std::move(img));
845 }
846
847 iteration++;
848 }
849 }
850 eglDestroyContext(dpy, ctx);
851 });
852
853 producerThread.join();
854 consumerThread.join();
855
856 // Clean up
857 {
858 for (auto &img : waitingForProduce)
859 {
860 eglDestroyImageKHR(dpy, img.image);
861 eglDestroySync(dpy, img.sync);
862 glDeleteTextures(1, &img.rootTexture);
863 }
864 for (auto &img : waitingForConsume)
865 {
866 eglDestroyImageKHR(dpy, img.image);
867 eglDestroySync(dpy, img.sync);
868 glDeleteTextures(1, &img.rootTexture);
869 }
870 }
871 }
872
textureThreadFunction(bool useDraw)873 void MultithreadingTestES3::textureThreadFunction(bool useDraw)
874 {
875 EGLWindow *window = getEGLWindow();
876 EGLDisplay dpy = window->getDisplay();
877 EGLConfig config = window->getConfig();
878 EGLSurface surface = EGL_NO_SURFACE;
879 EGLContext ctx = EGL_NO_CONTEXT;
880
881 // Initialize the pbuffer and context
882 EGLint pbufferAttributes[] = {
883 EGL_WIDTH, kSize, EGL_HEIGHT, kSize, EGL_NONE, EGL_NONE,
884 };
885 surface = eglCreatePbufferSurface(dpy, config, pbufferAttributes);
886 EXPECT_EGL_SUCCESS();
887 EXPECT_NE(EGL_NO_SURFACE, surface);
888
889 ctx = createMultithreadedContext(window, window->getContext());
890 EXPECT_NE(EGL_NO_CONTEXT, ctx);
891
892 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
893 EXPECT_EGL_SUCCESS();
894
895 std::vector<GLColor> greenColor(kSize * kSize, GLColor::green);
896 std::vector<GLColor> redColor(kSize * kSize, GLColor::red);
897 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
898 ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
899
900 glBindTexture(GL_TEXTURE_2D, mTexture2D);
901 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
902 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
903 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
904 ASSERT_GL_NO_ERROR();
905
906 GLFramebuffer fbo;
907 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
908 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture2D, 0);
909 ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
910
911 mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
912 ASSERT_GL_NO_ERROR();
913
914 // Draw something
915 while (!mExitThread)
916 {
917 if (mMainThreadSyncObj == nullptr)
918 {
919 angle::Sleep(0);
920 }
921
922 std::lock_guard<decltype(mMutex)> lock(mMutex);
923
924 if (mMainThreadSyncObj != nullptr)
925 {
926 glWaitSync(mMainThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
927 ASSERT_GL_NO_ERROR();
928 glDeleteSync(mMainThreadSyncObj);
929 ASSERT_GL_NO_ERROR();
930 mMainThreadSyncObj = nullptr;
931 }
932 else
933 {
934 continue;
935 }
936
937 mDrawGreen = !mDrawGreen;
938
939 glBindTexture(GL_TEXTURE_2D, mTexture2D);
940 ASSERT_GL_NO_ERROR();
941
942 if (mDrawGreen)
943 {
944 if (useDraw)
945 {
946 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
947 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.0f);
948 }
949 else
950 {
951 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
952 greenColor.data());
953 }
954 ASSERT_GL_NO_ERROR();
955 }
956 else
957 {
958 if (useDraw)
959 {
960 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
961 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.0f);
962 }
963 else
964 {
965 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSize, kSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
966 redColor.data());
967 }
968 ASSERT_GL_NO_ERROR();
969 }
970
971 ASSERT_EQ(mSecondThreadSyncObj.load(), nullptr);
972 mSecondThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
973 ASSERT_GL_NO_ERROR();
974 }
975
976 // Clean up
977 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
978 EXPECT_EGL_SUCCESS();
979
980 eglDestroySurface(dpy, surface);
981 eglDestroyContext(dpy, ctx);
982 }
983
984 // Test fence sync with multiple threads drawing
mainThreadDraw(bool useDraw)985 void MultithreadingTestES3::mainThreadDraw(bool useDraw)
986 {
987 EGLWindow *window = getEGLWindow();
988 EGLDisplay dpy = window->getDisplay();
989 EGLContext ctx = window->getContext();
990 EGLSurface surface = window->getSurface();
991 // Use odd numbers so we bounce between red and green in the final image
992 constexpr int kNumIterations = 5;
993 constexpr int kNumDraws = 5;
994
995 mDrawGreen = false;
996
997 std::thread textureThread(&MultithreadingTestES3::textureThreadFunction, this, true);
998
999 ANGLE_GL_PROGRAM(texProgram, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
1000
1001 for (int iterations = 0; iterations < kNumIterations; ++iterations)
1002 {
1003 GLColor expectedDrawColor;
1004
1005 for (int draws = 0; draws < kNumDraws;)
1006 {
1007 if (mSecondThreadSyncObj == nullptr)
1008 {
1009 angle::Sleep(0);
1010 }
1011
1012 std::lock_guard<decltype(mMutex)> lock(mMutex);
1013
1014 if (mSecondThreadSyncObj != nullptr)
1015 {
1016 glWaitSync(mSecondThreadSyncObj, 0, GL_TIMEOUT_IGNORED);
1017 ASSERT_GL_NO_ERROR();
1018 glDeleteSync(mSecondThreadSyncObj);
1019 ASSERT_GL_NO_ERROR();
1020 mSecondThreadSyncObj = nullptr;
1021 }
1022 else
1023 {
1024 continue;
1025 }
1026
1027 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1028 glBindTexture(GL_TEXTURE_2D, mTexture2D);
1029 glUseProgram(texProgram);
1030 drawQuad(texProgram, essl1_shaders::PositionAttrib(), 0.0f);
1031
1032 // mDrawGreen will be changed by the background thread past mMainThreadSyncObj
1033 // as it will start drawing the next color to fbo. This shouldn't affect
1034 // pixels of the current frame so save the expected color before unblocking the thread
1035 expectedDrawColor = mDrawGreen ? GLColor::green : GLColor::red;
1036
1037 ASSERT_EQ(mMainThreadSyncObj.load(), nullptr);
1038 mMainThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1039 ASSERT_GL_NO_ERROR();
1040
1041 ++draws;
1042 }
1043
1044 ASSERT_GL_NO_ERROR();
1045 EXPECT_PIXEL_RECT_EQ(0, 0, kSize, kSize, expectedDrawColor);
1046
1047 swapBuffers();
1048 }
1049
1050 mExitThread = true;
1051 textureThread.join();
1052
1053 // Re-make current the test window's context for teardown.
1054 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, ctx));
1055 EXPECT_EGL_SUCCESS();
1056 }
1057
1058 // Test that glFenceSync/glWaitSync works correctly with multithreading.
1059 // Main thread: Samples from the shared texture to draw to the default FBO.
1060 // Secondary (Texture) thread: Draws to the shared texture, which the Main thread samples from.
1061 // The overall execution flow is:
1062 // Main Thread:
1063 // 1. Wait for the mSecondThreadSyncObj fence object to be created.
1064 // - This fence object is used by synchronize access to the shared texture by indicating that the
1065 // Secondary thread's draws to the texture have all completed and it's now safe to sample from
1066 // it.
1067 // 2. Once the fence is created, add a glWaitSync(mSecondThreadSyncObj) to the command stream and
1068 // then delete it.
1069 // 3. Draw, sampling from the shared texture.
1070 // 4. Create a new mMainThreadSyncObj.
1071 // - This fence object is used to synchronize access to the shared texture by indicating that the
1072 // Main thread's draws are no longer sampling from the texture, so it's now safe for the
1073 // Secondary thread to draw to it again with a new color.
1074 // Secondary (Texture) Thread:
1075 // 1. Wait for the mMainThreadSyncObj fence object to be created.
1076 // 2. Once the fence is created, add a glWaitSync(mMainThreadSyncObj) to the command stream and then
1077 // delete it.
1078 // 3. Draw/Fill the texture.
1079 // 4. Create a new mSecondThreadSyncObj.
1080 //
1081 // These threads loop for the specified number of iterations, drawing/sampling the shared texture
1082 // with the necessary glFlush()s and occasional eglSwapBuffers() to mimic a real multithreaded GLES
1083 // application.
TEST_P(MultithreadingTestES3,MultithreadFenceDraw)1084 TEST_P(MultithreadingTestES3, MultithreadFenceDraw)
1085 {
1086 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1087
1088 // Have the secondary thread use glDrawArrays()
1089 mainThreadDraw(true);
1090 }
1091
1092 // Same as MultithreadFenceDraw, but with the secondary thread using glTexImage2D rather than
1093 // glDrawArrays.
TEST_P(MultithreadingTestES3,MultithreadFenceTexImage)1094 TEST_P(MultithreadingTestES3, MultithreadFenceTexImage)
1095 {
1096 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1097
1098 // Have the secondary thread use glTexImage2D()
1099 mainThreadDraw(false);
1100 }
1101
1102 // Test that waiting on a sync object that hasn't been flushed and without a current context returns
1103 // TIMEOUT_EXPIRED or CONDITION_SATISFIED, but doesn't generate an error or crash.
TEST_P(MultithreadingTest,NoFlushNoContextReturnsTimeout)1104 TEST_P(MultithreadingTest, NoFlushNoContextReturnsTimeout)
1105 {
1106 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1107 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1108
1109 std::mutex mutex;
1110
1111 EGLWindow *window = getEGLWindow();
1112 EGLDisplay dpy = window->getDisplay();
1113
1114 glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
1115 glClear(GL_COLOR_BUFFER_BIT);
1116
1117 EGLSyncKHR sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1118 EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1119
1120 std::thread thread = std::thread([&]() {
1121 std::lock_guard<decltype(mutex)> lock(mutex);
1122 // Make sure there is no active context on this thread.
1123 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1124 EXPECT_EGL_SUCCESS();
1125 // Don't wait forever to make sure the test terminates
1126 constexpr GLuint64 kTimeout = 1'000'000'000; // 1 second
1127 int result = eglClientWaitSyncKHR(dpy, sync, 0, kTimeout);
1128 // We typically expect to get back TIMEOUT_EXPIRED since the sync object was never flushed.
1129 // However, the OpenGL ES backend returns CONDITION_SATISFIED, which is also a passing
1130 // result.
1131 ASSERT_TRUE(result == EGL_TIMEOUT_EXPIRED_KHR || result == EGL_CONDITION_SATISFIED_KHR);
1132 });
1133
1134 thread.join();
1135
1136 EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
1137 }
1138
1139 // Test that waiting on sync object that hasn't been flushed yet, but is later flushed by another
1140 // thread, correctly returns when the fence is signalled without a timeout.
TEST_P(MultithreadingTest,CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)1141 TEST_P(MultithreadingTest, CreateFenceThreadAClientWaitSyncThreadBDelayedFlush)
1142 {
1143 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1144 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1145
1146 EGLSyncKHR sync = EGL_NO_SYNC_KHR;
1147
1148 std::mutex mutex;
1149 std::condition_variable condVar;
1150
1151 enum class Step
1152 {
1153 Start,
1154 Thread0Clear,
1155 Thread1CreateFence,
1156 Thread0ClientWaitSync,
1157 Thread1Flush,
1158 Finish,
1159 Abort,
1160 };
1161 Step currentStep = Step::Start;
1162
1163 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1164 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1165
1166 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1167
1168 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1169
1170 // Do work.
1171 glClearColor(1.0, 0.0, 0.0, 1.0);
1172 glClear(GL_COLOR_BUFFER_BIT);
1173
1174 // Wait for thread 1 to clear.
1175 threadSynchronization.nextStep(Step::Thread0Clear);
1176 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1177
1178 // Wait on the sync object, but do *not* flush it, since the other thread will flush.
1179 constexpr GLuint64 kTimeout = 2'000'000'000; // 2 seconds
1180 threadSynchronization.nextStep(Step::Thread0ClientWaitSync);
1181 ASSERT_EQ(EGL_CONDITION_SATISFIED_KHR, eglClientWaitSyncKHR(dpy, sync, 0, kTimeout));
1182
1183 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1184 };
1185
1186 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1187 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1188
1189 // Wait for thread 0 to clear.
1190 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));
1191
1192 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1193
1194 // Do work.
1195 glClearColor(0.0, 1.0, 0.0, 1.0);
1196 glClear(GL_COLOR_BUFFER_BIT);
1197
1198 sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
1199 EXPECT_NE(sync, EGL_NO_SYNC_KHR);
1200
1201 // Wait for the thread 0 to eglClientWaitSyncKHR().
1202 threadSynchronization.nextStep(Step::Thread1CreateFence);
1203 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ClientWaitSync));
1204
1205 // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1206 angle::Sleep(500);
1207 glFlush();
1208
1209 // Clean up
1210 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1211
1212 threadSynchronization.nextStep(Step::Finish);
1213 };
1214
1215 std::array<LockStepThreadFunc, 2> threadFuncs = {
1216 std::move(thread0),
1217 std::move(thread1),
1218 };
1219
1220 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1221
1222 ASSERT_NE(currentStep, Step::Abort);
1223 }
1224
1225 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1226 // that.
testFenceWithOpenRenderPass(FenceTest test,FlushMethod flushMethod)1227 void MultithreadingTestES3::testFenceWithOpenRenderPass(FenceTest test, FlushMethod flushMethod)
1228 {
1229 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1230 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1231
1232 constexpr uint32_t kWidth = 100;
1233 constexpr uint32_t kHeight = 200;
1234
1235 GLsync sync = 0;
1236 GLuint texture = 0;
1237
1238 std::mutex mutex;
1239 std::condition_variable condVar;
1240
1241 enum class Step
1242 {
1243 Start,
1244 Thread0CreateFence,
1245 Thread1WaitFence,
1246 Thread0Finish,
1247 Finish,
1248 Abort,
1249 };
1250 Step currentStep = Step::Start;
1251
1252 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1253 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1254
1255 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1256
1257 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1258
1259 // Create a shared texture to test synchronization
1260 GLTexture color;
1261 texture = color;
1262
1263 glBindTexture(GL_TEXTURE_2D, texture);
1264 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1265
1266 GLFramebuffer fbo;
1267 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1268 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1269
1270 // Draw to shared texture.
1271 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1272 drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1273 ASSERT_GL_NO_ERROR();
1274
1275 // Issue a fence. A render pass is currently open, so the fence is not actually submitted
1276 // in the Vulkan backend.
1277 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1278 ASSERT_NE(sync, nullptr);
1279
1280 // Wait for thread 1 to wait on it.
1281 threadSynchronization.nextStep(Step::Thread0CreateFence);
1282 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1WaitFence));
1283
1284 // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1285 angle::Sleep(500);
1286 switch (flushMethod)
1287 {
1288 case FlushMethod::Flush:
1289 glFlush();
1290 break;
1291 case FlushMethod::Finish:
1292 glFinish();
1293 break;
1294 }
1295
1296 // Clean up
1297 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1298
1299 threadSynchronization.nextStep(Step::Thread0Finish);
1300 threadSynchronization.waitForStep(Step::Finish);
1301 };
1302
1303 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1304 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1305
1306 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1307
1308 // Wait for thread 0 to create the fence object.
1309 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateFence));
1310
1311 // Test access to the fence object
1312 threadSynchronization.nextStep(Step::Thread1WaitFence);
1313
1314 constexpr GLuint64 kTimeout = 2'000'000'000; // 2 seconds
1315 GLenum result = GL_CONDITION_SATISFIED;
1316 switch (test)
1317 {
1318 case FenceTest::ClientWait:
1319 result = glClientWaitSync(sync, 0, kTimeout);
1320 break;
1321 case FenceTest::ServerWait:
1322 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1323 break;
1324 case FenceTest::GetStatus:
1325 {
1326 GLint value;
1327 glGetSynciv(sync, GL_SYNC_STATUS, 1, nullptr, &value);
1328 if (value != GL_SIGNALED)
1329 {
1330 result = glClientWaitSync(sync, 0, kTimeout);
1331 }
1332 break;
1333 }
1334 }
1335 ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1336
1337 // Verify the shared texture is drawn to.
1338 glBindTexture(GL_TEXTURE_2D, texture);
1339
1340 GLFramebuffer fbo;
1341 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1342 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1343
1344 EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1345
1346 // Clean up
1347 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1348
1349 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Finish));
1350 threadSynchronization.nextStep(Step::Finish);
1351 };
1352
1353 std::array<LockStepThreadFunc, 2> threadFuncs = {
1354 std::move(thread0),
1355 std::move(thread1),
1356 };
1357
1358 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1359
1360 ASSERT_NE(currentStep, Step::Abort);
1361 }
1362
1363 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1364 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFlush)1365 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFlush)
1366 {
1367 testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Flush);
1368 }
1369
1370 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1371 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFlush)1372 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFlush)
1373 {
1374 testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Flush);
1375 }
1376
1377 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1378 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFlush)1379 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFlush)
1380 {
1381 testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Flush);
1382 }
1383
1384 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1385 // that.
TEST_P(MultithreadingTestES3,ThreadBClientWaitBeforeThreadASyncFinish)1386 TEST_P(MultithreadingTestES3, ThreadBClientWaitBeforeThreadASyncFinish)
1387 {
1388 testFenceWithOpenRenderPass(FenceTest::ClientWait, FlushMethod::Finish);
1389 }
1390
1391 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1392 // that.
TEST_P(MultithreadingTestES3,ThreadBServerWaitBeforeThreadASyncFinish)1393 TEST_P(MultithreadingTestES3, ThreadBServerWaitBeforeThreadASyncFinish)
1394 {
1395 testFenceWithOpenRenderPass(FenceTest::ServerWait, FlushMethod::Finish);
1396 }
1397
1398 // Test that thread B can wait on thread A's sync before thread A flushes it, and wakes up after
1399 // that.
TEST_P(MultithreadingTestES3,ThreadBGetStatusBeforeThreadASyncFinish)1400 TEST_P(MultithreadingTestES3, ThreadBGetStatusBeforeThreadASyncFinish)
1401 {
1402 testFenceWithOpenRenderPass(FenceTest::GetStatus, FlushMethod::Finish);
1403 }
1404
1405 // Test the following scenario:
1406 //
1407 // - Thread A opens a render pass, and flushes it. In the Vulkan backend, this may make the flush
1408 // deferred.
1409 // - Thread B opens a render pass and creates a fence. In the Vulkan backend, this also defers the
1410 // flush.
1411 // - Thread C waits on fence
1412 //
1413 // In the Vulkan backend, submission of the fence is implied by thread C's wait, and thread A may
1414 // also be flushed as collateral. If the fence's serial is updated based on thread A's submission,
1415 // synchronization between B and C would be broken.
TEST_P(MultithreadingTestES3,ThreadCWaitBeforeThreadBSyncFinish)1416 TEST_P(MultithreadingTestES3, ThreadCWaitBeforeThreadBSyncFinish)
1417 {
1418 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1419 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1420
1421 constexpr uint32_t kWidth = 100;
1422 constexpr uint32_t kHeight = 200;
1423
1424 GLsync sync = 0;
1425 GLuint texture = 0;
1426
1427 std::mutex mutex;
1428 std::condition_variable condVar;
1429
1430 enum class Step
1431 {
1432 Start,
1433 Thread0DrawAndFlush,
1434 Thread1CreateFence,
1435 Thread2WaitFence,
1436 Thread2Finished,
1437 Finish,
1438 Abort,
1439 };
1440 Step currentStep = Step::Start;
1441
1442 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1443 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1444
1445 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1446
1447 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1448
1449 // Open a render pass and flush it.
1450 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1451 drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1452 glFlush();
1453 ASSERT_GL_NO_ERROR();
1454
1455 threadSynchronization.nextStep(Step::Thread0DrawAndFlush);
1456 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1457
1458 // Clean up
1459 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1460 };
1461
1462 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1463 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1464
1465 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1466
1467 // Wait for thread 0 to set up
1468 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0DrawAndFlush));
1469
1470 // Create a shared texture to test synchronization
1471 GLTexture color;
1472 texture = color;
1473
1474 glBindTexture(GL_TEXTURE_2D, texture);
1475 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
1476
1477 GLFramebuffer fbo;
1478 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1479 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1480
1481 // Draw to shared texture.
1482 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
1483 drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1484 ASSERT_GL_NO_ERROR();
1485
1486 // Issue a fence. A render pass is currently open, so the fence is not actually submitted
1487 // in the Vulkan backend.
1488 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1489 ASSERT_NE(sync, nullptr);
1490
1491 // Wait for thread 1 to wait on it.
1492 threadSynchronization.nextStep(Step::Thread1CreateFence);
1493 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2WaitFence));
1494
1495 // Wait a little to give thread 1 time to wait on the sync object before flushing it.
1496 angle::Sleep(500);
1497 glFlush();
1498
1499 // Clean up
1500 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1501
1502 threadSynchronization.nextStep(Step::Thread2Finished);
1503 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1504 };
1505
1506 auto thread2 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1507 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1508
1509 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1510
1511 // Wait for thread 0 to create the fence object.
1512 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateFence));
1513
1514 // Test access to the fence object
1515 threadSynchronization.nextStep(Step::Thread2WaitFence);
1516
1517 constexpr GLuint64 kTimeout = 2'000'000'000; // 2 seconds
1518 GLenum result = glClientWaitSync(sync, 0, kTimeout);
1519 ASSERT_TRUE(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
1520
1521 // Verify the shared texture is drawn to.
1522 glBindTexture(GL_TEXTURE_2D, texture);
1523
1524 GLFramebuffer fbo;
1525 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1526 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1527
1528 EXPECT_PIXEL_RECT_EQ(0, 0, kWidth, kHeight, GLColor::red);
1529
1530 // Clean up
1531 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1532
1533 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread2Finished));
1534 threadSynchronization.nextStep(Step::Finish);
1535 };
1536
1537 std::array<LockStepThreadFunc, 3> threadFuncs = {
1538 std::move(thread0),
1539 std::move(thread1),
1540 std::move(thread2),
1541 };
1542
1543 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1544
1545 ASSERT_NE(currentStep, Step::Abort);
1546 }
1547
1548 // Test that having commands recorded but not submitted on one thread using a texture, does not
1549 // interfere with similar commands on another thread using the same texture. Regression test for a
1550 // bug in the Vulkan backend where the first thread would batch updates to a descriptor set not
1551 // visible to the other thread, while the other thread picks up the (unupdated) descriptor set from
1552 // a shared cache.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads)1553 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads)
1554 {
1555 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1556 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1557
1558 GLsync sync = 0;
1559 GLuint texture = 0;
1560
1561 constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1562
1563 std::mutex mutex;
1564 std::condition_variable condVar;
1565
1566 enum class Step
1567 {
1568 Start,
1569 Thread0CreateTextureAndDraw,
1570 Thread1DrawAndFlush,
1571 Finish,
1572 Abort,
1573 };
1574 Step currentStep = Step::Start;
1575
1576 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1577 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1578
1579 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1580
1581 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1582
1583 // Create a texture, and record a command that draws into it.
1584 GLTexture color;
1585 texture = color;
1586
1587 glBindTexture(GL_TEXTURE_2D, texture);
1588 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1589 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1590 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1591 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1592
1593 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1594 ASSERT_NE(sync, nullptr);
1595
1596 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1597 essl1_shaders::fs::Texture2D());
1598 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1599
1600 // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1601 // Vulkan backend.
1602 threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1603 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1604
1605 // Flush after thread 1
1606 EXPECT_PIXEL_COLOR_NEAR(
1607 0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1608
1609 // Clean up
1610 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1611
1612 threadSynchronization.nextStep(Step::Finish);
1613 };
1614
1615 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1616 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1617
1618 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1619
1620 // Wait for thread 0 to set up
1621 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1622
1623 // Synchronize with the texture upload (but not the concurrent read)
1624 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1625
1626 // Draw with the same texture, in the same way as thread 0. This ensures that the
1627 // descriptor sets used in the Vulkan backend are identical.
1628 glBindTexture(GL_TEXTURE_2D, texture);
1629 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1630 essl1_shaders::fs::Texture2D());
1631 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1632
1633 // Flush
1634 EXPECT_PIXEL_COLOR_NEAR(
1635 0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1636
1637 threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1638
1639 // Clean up
1640 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1641
1642 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1643 };
1644
1645 std::array<LockStepThreadFunc, 2> threadFuncs = {
1646 std::move(thread0),
1647 std::move(thread1),
1648 };
1649
1650 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1651
1652 ASSERT_NE(currentStep, Step::Abort);
1653 }
1654
1655 // Similar to UnsynchronizedTextureReads, but the texture update is done through framebuffer write.
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads2)1656 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads2)
1657 {
1658 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1659 ANGLE_SKIP_TEST_IF(!hasFenceSyncExtension() || !hasGLSyncExtension());
1660
1661 GLsync sync = 0;
1662 GLuint texture = 0;
1663
1664 std::mutex mutex;
1665 std::condition_variable condVar;
1666
1667 enum class Step
1668 {
1669 Start,
1670 Thread0CreateTextureAndDraw,
1671 Thread1DrawAndFlush,
1672 Finish,
1673 Abort,
1674 };
1675 Step currentStep = Step::Start;
1676
1677 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1678 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1679
1680 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1681
1682 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1683
1684 // Create a texture, and record a command that draws into it.
1685 GLTexture color;
1686 texture = color;
1687
1688 glBindTexture(GL_TEXTURE_2D, texture);
1689 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1690 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1691 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1692
1693 GLFramebuffer fbo;
1694 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
1695 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
1696
1697 glClearColor(1, 0, 0, 1);
1698 glClear(GL_COLOR_BUFFER_BIT);
1699 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1700 glBindFramebuffer(GL_FRAMEBUFFER, 0);
1701
1702 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
1703 ASSERT_NE(sync, nullptr);
1704
1705 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1706 essl1_shaders::fs::Texture2D());
1707 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1708
1709 // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1710 // Vulkan backend.
1711 threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1712 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1713
1714 // Flush after thread 1
1715 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1716
1717 // Clean up
1718 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1719
1720 threadSynchronization.nextStep(Step::Finish);
1721 };
1722
1723 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1724 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1725
1726 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1727
1728 // Wait for thread 0 to set up
1729 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1730
1731 // Synchronize with the texture update (but not the concurrent read)
1732 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
1733
1734 // Draw with the same texture, in the same way as thread 0. This ensures that the
1735 // descriptor sets used in the Vulkan backend are identical.
1736 glBindTexture(GL_TEXTURE_2D, texture);
1737 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1738 essl1_shaders::fs::Texture2D());
1739 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1740
1741 // Flush
1742 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1743
1744 threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1745
1746 // Clean up
1747 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1748
1749 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1750 };
1751
1752 std::array<LockStepThreadFunc, 2> threadFuncs = {
1753 std::move(thread0),
1754 std::move(thread1),
1755 };
1756
1757 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1758
1759 ASSERT_NE(currentStep, Step::Abort);
1760 }
1761
1762 // Similar to UnsynchronizedTextureReads, but the texture is used once. This is because
1763 // UnsynchronizedTextureRead hits a different bug than it intends to test. This test makes sure the
1764 // image is put in the right layout, by using it together with another texture (i.e. a different
1765 // descriptor set).
TEST_P(MultithreadingTestES3,UnsynchronizedTextureReads3)1766 TEST_P(MultithreadingTestES3, UnsynchronizedTextureReads3)
1767 {
1768 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1769
1770 constexpr GLubyte kInitialData[4] = {127, 63, 191, 255};
1771
1772 GLuint texture = 0;
1773
1774 std::mutex mutex;
1775 std::condition_variable condVar;
1776
1777 enum class Step
1778 {
1779 Start,
1780 Thread0CreateTextureAndDraw,
1781 Thread1DrawAndFlush,
1782 Finish,
1783 Abort,
1784 };
1785 Step currentStep = Step::Start;
1786
1787 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1788 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1789
1790 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1791
1792 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1793
1794 // Create a texture, and record a command that draws into it.
1795 GLTexture color;
1796 texture = color;
1797
1798 glActiveTexture(GL_TEXTURE0);
1799 glBindTexture(GL_TEXTURE_2D, texture);
1800 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1801 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1802 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1803 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1804
1805 glActiveTexture(GL_TEXTURE1);
1806 GLTexture color2;
1807 glBindTexture(GL_TEXTURE_2D, color2);
1808 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1809 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
1810 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1811 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, kInitialData);
1812
1813 ANGLE_GL_PROGRAM(setupTexture, essl1_shaders::vs::Texture2D(),
1814 R"(precision mediump float;
1815 uniform sampler2D tex2D;
1816 uniform sampler2D tex2D2;
1817 varying vec2 v_texCoord;
1818
1819 void main()
1820 {
1821 gl_FragColor = texture2D(tex2D, v_texCoord) + texture2D(tex2D2, v_texCoord);
1822 })");
1823 drawQuad(setupTexture, essl1_shaders::PositionAttrib(), 0.0f);
1824 ASSERT_GL_NO_ERROR();
1825
1826 glFinish();
1827
1828 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1829 essl1_shaders::fs::Texture2D());
1830 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1831 ASSERT_GL_NO_ERROR();
1832
1833 // Don't flush yet; this leaves the descriptor set updates to the texture pending in the
1834 // Vulkan backend.
1835 threadSynchronization.nextStep(Step::Thread0CreateTextureAndDraw);
1836 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1DrawAndFlush));
1837
1838 // Flush after thread 1
1839 EXPECT_PIXEL_COLOR_NEAR(
1840 0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1841
1842 // Clean up
1843 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1844
1845 threadSynchronization.nextStep(Step::Finish);
1846 };
1847
1848 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1849 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1850
1851 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1852
1853 // Wait for thread 0 to set up
1854 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreateTextureAndDraw));
1855
1856 // Draw with the same texture, in the same way as thread 0. This ensures that the
1857 // descriptor sets used in the Vulkan backend are identical.
1858 glBindTexture(GL_TEXTURE_2D, texture);
1859 ANGLE_GL_PROGRAM(drawTexture, essl1_shaders::vs::Texture2D(),
1860 essl1_shaders::fs::Texture2D());
1861 drawQuad(drawTexture, essl1_shaders::PositionAttrib(), 0.0f);
1862 ASSERT_GL_NO_ERROR();
1863
1864 // Flush
1865 EXPECT_PIXEL_COLOR_NEAR(
1866 0, 0, GLColor(kInitialData[0], kInitialData[1], kInitialData[2], kInitialData[3]), 1);
1867
1868 threadSynchronization.nextStep(Step::Thread1DrawAndFlush);
1869
1870 // Clean up
1871 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1872
1873 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1874 };
1875
1876 std::array<LockStepThreadFunc, 2> threadFuncs = {
1877 std::move(thread0),
1878 std::move(thread1),
1879 };
1880
1881 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1882
1883 ASSERT_NE(currentStep, Step::Abort);
1884 }
1885
1886 // Test framebuffer fetch program used between share groups.
testFramebufferFetch(DrawOrder drawOrder)1887 void MultithreadingTestES3::testFramebufferFetch(DrawOrder drawOrder)
1888 {
1889 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
1890 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
1891
1892 GLProgram framebufferFetchProgram;
1893
1894 constexpr char kFS[] = R"(#version 300 es
1895 #extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
1896 layout(noncoherent, location = 0) inout highp vec4 o_color;
1897
1898 uniform highp vec4 u_color;
1899 void main (void)
1900 {
1901 o_color += u_color;
1902 })";
1903
1904 std::mutex mutex;
1905 std::condition_variable condVar;
1906
1907 enum class Step
1908 {
1909 Start,
1910 Thread0PreCreateProgram,
1911 Thread1CreateProgram,
1912 Finish,
1913 Abort,
1914 };
1915 Step currentStep = Step::Start;
1916
1917 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1918 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1919
1920 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
1921
1922 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1923
1924 // Open a render pass, if requested.
1925 if (drawOrder == DrawOrder::Before)
1926 {
1927 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1928 drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1929 ASSERT_GL_NO_ERROR();
1930 }
1931
1932 threadSynchronization.nextStep(Step::Thread0PreCreateProgram);
1933 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1CreateProgram));
1934
1935 // Render using the framebuffer fetch program
1936 if (drawOrder == DrawOrder::After)
1937 {
1938 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
1939 drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
1940 ASSERT_GL_NO_ERROR();
1941 }
1942
1943 glFramebufferFetchBarrierEXT();
1944
1945 glUseProgram(framebufferFetchProgram);
1946 GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1947 glUniform4f(colorLocation, 1, 0, 0, 0);
1948 drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1949 ASSERT_GL_NO_ERROR();
1950
1951 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
1952
1953 threadSynchronization.nextStep(Step::Finish);
1954
1955 // Clean up
1956 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1957 };
1958
1959 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
1960 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
1961
1962 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
1963
1964 // Wait for thread 0 to set up
1965 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0PreCreateProgram));
1966
1967 // Create the framebuffer fetch program
1968 framebufferFetchProgram.makeRaster(essl3_shaders::vs::Simple(), kFS);
1969 glUseProgram(framebufferFetchProgram);
1970
1971 // Notify the other thread to use it
1972 threadSynchronization.nextStep(Step::Thread1CreateProgram);
1973 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
1974
1975 glClearColor(0, 0, 0, 1);
1976 glClear(GL_COLOR_BUFFER_BIT);
1977
1978 glFramebufferFetchBarrierEXT();
1979
1980 glUseProgram(framebufferFetchProgram);
1981 GLint colorLocation = glGetUniformLocation(framebufferFetchProgram, "u_color");
1982 glUniform4f(colorLocation, 0, 0, 1, 0);
1983 drawQuad(framebufferFetchProgram, essl1_shaders::PositionAttrib(), 0.0f);
1984 ASSERT_GL_NO_ERROR();
1985
1986 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1987
1988 // Clean up
1989 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
1990 };
1991
1992 std::array<LockStepThreadFunc, 2> threadFuncs = {
1993 std::move(thread0),
1994 std::move(thread1),
1995 };
1996
1997 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
1998
1999 ASSERT_NE(currentStep, Step::Abort);
2000 }
2001
2002 // Thread 1 creates the framebuffer fetch program. Thread 0 proceeds to use it.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchBeforeRenderPass)2003 TEST_P(MultithreadingTestES3, CreateFramebufferFetchBeforeRenderPass)
2004 {
2005 testFramebufferFetch(DrawOrder::After);
2006 }
2007
2008 // Thread 1 creates the framebuffer fetch program while thread 0 is mid render pass. Thread 0
2009 // proceeds to use the framebuffer fetch program in the rest of its render pass.
TEST_P(MultithreadingTestES3,CreateFramebufferFetchMidRenderPass)2010 TEST_P(MultithreadingTestES3, CreateFramebufferFetchMidRenderPass)
2011 {
2012 testFramebufferFetch(DrawOrder::Before);
2013 }
2014
2015 // Test async monolithic pipeline creation in the Vulkan backend vs shared programs. This test
2016 // makes one context/thread create a set of programs, then has another context/thread use them a few
2017 // times, and then the original context destroys them.
TEST_P(MultithreadingTestES3,ProgramUseAndDestroyInTwoContexts)2018 TEST_P(MultithreadingTestES3, ProgramUseAndDestroyInTwoContexts)
2019 {
2020 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2021
2022 GLProgram programs[6];
2023
2024 GLsync sync = 0;
2025
2026 std::mutex mutex;
2027 std::condition_variable condVar;
2028
2029 enum class Step
2030 {
2031 Start,
2032 Thread0CreatePrograms,
2033 Thread1UsePrograms,
2034 Finish,
2035 Abort,
2036 };
2037 Step currentStep = Step::Start;
2038
2039 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2040 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2041
2042 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2043
2044 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2045
2046 // Create the programs
2047 programs[0].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2048 programs[1].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2049 programs[2].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2050 programs[3].makeRaster(essl1_shaders::vs::Passthrough(), essl1_shaders::fs::Checkered());
2051 programs[4].makeRaster(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
2052 programs[5].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
2053
2054 EXPECT_TRUE(programs[0].valid());
2055 EXPECT_TRUE(programs[1].valid());
2056 EXPECT_TRUE(programs[2].valid());
2057 EXPECT_TRUE(programs[3].valid());
2058 EXPECT_TRUE(programs[4].valid());
2059 EXPECT_TRUE(programs[5].valid());
2060
2061 threadSynchronization.nextStep(Step::Thread0CreatePrograms);
2062 // Wait for the other thread to use the programs
2063 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1UsePrograms));
2064
2065 // Destroy them
2066 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
2067 programs[0].reset();
2068 programs[1].reset();
2069 programs[2].reset();
2070 programs[3].reset();
2071 programs[4].reset();
2072 programs[5].reset();
2073
2074 threadSynchronization.nextStep(Step::Finish);
2075
2076 // Clean up
2077 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2078 };
2079
2080 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2081 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2082
2083 // Wait for thread 0 to create the programs
2084 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0CreatePrograms));
2085
2086 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2087
2088 // Use them a few times.
2089 drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2090 drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2091 drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2092 drawQuad(programs[3], essl1_shaders::PositionAttrib(), 0.0f);
2093 drawQuad(programs[4], essl1_shaders::PositionAttrib(), 0.0f);
2094
2095 drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2096 drawQuad(programs[1], essl1_shaders::PositionAttrib(), 0.0f);
2097 drawQuad(programs[2], essl1_shaders::PositionAttrib(), 0.0f);
2098
2099 drawQuad(programs[0], essl1_shaders::PositionAttrib(), 0.0f);
2100
2101 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2102 ASSERT_NE(sync, nullptr);
2103
2104 // Notify the other thread to destroy the programs.
2105 threadSynchronization.nextStep(Step::Thread1UsePrograms);
2106 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2107
2108 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
2109
2110 // Clean up
2111 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2112 };
2113
2114 std::array<LockStepThreadFunc, 2> threadFuncs = {
2115 std::move(thread0),
2116 std::move(thread1),
2117 };
2118
2119 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
2120
2121 ASSERT_NE(currentStep, Step::Abort);
2122 }
2123
2124 // Tests that Context with High Priority will correctly sample Texture rendered by Share Context
2125 // with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriority)2126 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriority)
2127 {
2128 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2129
2130 constexpr size_t kIterationCountMax = 10;
2131
2132 const bool reduceLoad = isSwiftshader();
2133 const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2134 const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2135
2136 // Initialize contexts
2137 EGLWindow *window = getEGLWindow();
2138 EGLDisplay dpy = window->getDisplay();
2139 EGLConfig config = window->getConfig();
2140
2141 // Large enough texture to catch timing problems.
2142 constexpr GLsizei kTexSize = 1024;
2143 constexpr size_t kThreadCount = 2;
2144 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2145 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2146
2147 EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2148
2149 EGLint pbufferAttributes[kThreadCount][6] = {
2150 {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2151 {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2152
2153 EGLint attributes[] = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2154 EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2155 EGLint *extraAttributes = attributes;
2156 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2157 {
2158 attributes[2] = EGL_NONE;
2159 }
2160 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2161 {
2162 // Run tests with single priority anyway.
2163 extraAttributes += 2;
2164 }
2165
2166 for (size_t t = 0; t < kThreadCount; ++t)
2167 {
2168 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2169 EXPECT_EGL_SUCCESS();
2170
2171 attributes[1] = priorities[t];
2172 attributes[3] = mVirtualizationGroup++;
2173
2174 ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2175 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2176 }
2177
2178 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2179 std::mutex mutex;
2180 std::condition_variable condVar;
2181
2182 enum class Step
2183 {
2184 Start,
2185 Thread0Init,
2186 Thread1Init,
2187 Thread0Draw,
2188 Thread1Draw,
2189 Finish = Thread0Draw + kIterationCountMax * 2,
2190 Abort,
2191 };
2192 Step currentStep = Step::Start;
2193
2194 GLTexture texture;
2195 GLsync thread0DrawSyncObj;
2196
2197 auto calculateTestColor = [](size_t i) {
2198 return GLColor(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2199 };
2200 auto makeStep = [](Step base, size_t i) {
2201 return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2202 };
2203
2204 // Render to the texture.
2205 std::thread thread0 = std::thread([&]() {
2206 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2207
2208 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2209 EXPECT_EGL_SUCCESS();
2210
2211 glBindTexture(GL_TEXTURE_2D, texture);
2212 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2213 nullptr);
2214 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2215 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2216
2217 GLFramebuffer fbo;
2218 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2219 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2220 glViewport(0, 0, kTexSize, kTexSize);
2221
2222 ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2223 essl1_shaders::fs::UniformColor());
2224 GLint colorLocation =
2225 glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2226 ASSERT_NE(-1, colorLocation);
2227 glUseProgram(colorProgram);
2228
2229 // Notify second thread that initialization is finished.
2230 threadSynchronization.nextStep(Step::Thread0Init);
2231 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2232
2233 for (size_t i = 0; i < iterationCount; ++i)
2234 {
2235 // Simulate heavy work...
2236 glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2237 for (size_t j = 0; j < heavyDrawCount; ++j)
2238 {
2239 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2240 }
2241
2242 // Draw with test color.
2243 Vector4 color = calculateTestColor(i).toNormalizedVector();
2244 glUniform4f(colorLocation, color.x(), color.y(), color.z(), color.w());
2245 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2246
2247 thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2248 ASSERT_GL_NO_ERROR();
2249
2250 // Notify second thread that draw is finished.
2251 threadSynchronization.nextStep(makeStep(Step::Thread0Draw, i));
2252 ASSERT_TRUE(threadSynchronization.waitForStep(
2253 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i)));
2254 }
2255
2256 EXPECT_GL_NO_ERROR();
2257 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2258 EXPECT_EGL_SUCCESS();
2259 });
2260
2261 // Sample texture
2262 std::thread thread1 = std::thread([&]() {
2263 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2264
2265 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2266 EXPECT_EGL_SUCCESS();
2267
2268 glViewport(0, 0, kTexSize, kTexSize);
2269
2270 ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2271 essl1_shaders::fs::Texture2D());
2272 glUseProgram(textureProgram);
2273
2274 // Wait for first thread to finish initializing.
2275 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Init));
2276
2277 glBindTexture(GL_TEXTURE_2D, texture);
2278
2279 // Wait for first thread to draw using the shared texture.
2280 threadSynchronization.nextStep(Step::Thread1Init);
2281
2282 for (size_t i = 0; i < iterationCount; ++i)
2283 {
2284 ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Draw, i)));
2285
2286 ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2287 glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2288 ASSERT_GL_NO_ERROR();
2289
2290 // Should draw test color.
2291 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2292
2293 // Check test color in four corners.
2294 GLColor color = calculateTestColor(i);
2295 EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2296 EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2297 EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2298 EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2299
2300 glDeleteSync(thread0DrawSyncObj);
2301 ASSERT_GL_NO_ERROR();
2302 thread0DrawSyncObj = nullptr;
2303
2304 threadSynchronization.nextStep(
2305 (i == iterationCount - 1) ? Step::Finish : makeStep(Step::Thread1Draw, i));
2306 }
2307
2308 EXPECT_GL_NO_ERROR();
2309 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2310 EXPECT_EGL_SUCCESS();
2311 });
2312
2313 thread0.join();
2314 thread1.join();
2315
2316 ASSERT_NE(currentStep, Step::Abort);
2317
2318 // Clean up
2319 for (size_t t = 0; t < kThreadCount; ++t)
2320 {
2321 eglDestroySurface(dpy, surface[t]);
2322 eglDestroyContext(dpy, ctx[t]);
2323 }
2324 }
2325
2326 // Tests that newly created Context with High Priority will correctly sample Texture already
2327 // rendered by Share Context with Low Priority.
TEST_P(MultithreadingTestES3,RenderThenSampleInNewContextWithDifferentPriority)2328 TEST_P(MultithreadingTestES3, RenderThenSampleInNewContextWithDifferentPriority)
2329 {
2330 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2331 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2332
2333 const bool reduceLoad = isSwiftshader();
2334 const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2335
2336 // Initialize contexts
2337 EGLWindow *window = getEGLWindow();
2338 EGLDisplay dpy = window->getDisplay();
2339 EGLConfig config = window->getConfig();
2340
2341 // Large enough texture to catch timing problems.
2342 constexpr GLsizei kTexSize = 1024;
2343 constexpr size_t kThreadCount = 2;
2344 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2345 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2346
2347 EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2348
2349 EGLint pbufferAttributes[kThreadCount][6] = {
2350 {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2351 {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2352
2353 EGLint attributes[] = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2354 EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2355 EGLint *extraAttributes = attributes;
2356 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2357 {
2358 attributes[2] = EGL_NONE;
2359 }
2360 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2361 {
2362 // Run tests with single priority anyway.
2363 extraAttributes += 2;
2364 }
2365
2366 for (size_t t = 0; t < kThreadCount; ++t)
2367 {
2368 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2369 EXPECT_EGL_SUCCESS();
2370
2371 attributes[1] = priorities[t];
2372 attributes[3] = mVirtualizationGroup++;
2373
2374 // Second context will be created in a thread 1
2375 if (t == 0)
2376 {
2377 ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0], extraAttributes);
2378 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2379 }
2380 }
2381
2382 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2383 std::mutex mutex;
2384 std::condition_variable condVar;
2385
2386 enum class Step
2387 {
2388 Start,
2389 Thread0Draw,
2390 Thread1Draw,
2391 Finish,
2392 Abort,
2393 };
2394 Step currentStep = Step::Start;
2395
2396 // Create shared resources before threads to minimize timing delays.
2397 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2398 EXPECT_EGL_SUCCESS();
2399
2400 ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2401 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2402 ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2403 ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2404 essl1_shaders::fs::Texture2D());
2405
2406 GLTexture texture;
2407 glBindTexture(GL_TEXTURE_2D, texture);
2408 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2409 nullptr);
2410 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2411 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2412
2413 GLFramebuffer fbo;
2414 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2415 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2416 glViewport(0, 0, kTexSize, kTexSize);
2417
2418 EXPECT_GL_NO_ERROR();
2419 EXPECT_EGL_TRUE(window->makeCurrent());
2420 EXPECT_EGL_SUCCESS();
2421
2422 GLsync thread0DrawSyncObj;
2423
2424 // Render to the texture.
2425 std::thread thread0 = std::thread([&]() {
2426 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2427
2428 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2429 EXPECT_EGL_SUCCESS();
2430
2431 // Enable additive blend
2432 glEnable(GL_BLEND);
2433 glBlendFunc(GL_ONE, GL_ONE);
2434
2435 GLuint query;
2436 glGenQueries(1, &query);
2437 glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2438 ASSERT_GL_NO_ERROR();
2439
2440 // Simulate heavy work...
2441 glUseProgram(redProgram);
2442 for (size_t j = 0; j < heavyDrawCount; ++j)
2443 {
2444 // Draw with Red color.
2445 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2446 }
2447
2448 // Draw with Green color.
2449 glUseProgram(greenProgram);
2450 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2451
2452 // This should force "flushToPrimary()"
2453 glEndQuery(GL_TIME_ELAPSED_EXT);
2454 glDeleteQueries(1, &query);
2455 ASSERT_GL_NO_ERROR();
2456
2457 // Continue draw with Blue color after flush...
2458 glUseProgram(blueProgram);
2459 drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2460
2461 thread0DrawSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
2462 ASSERT_GL_NO_ERROR();
2463
2464 // Notify second thread that draw is finished.
2465 threadSynchronization.nextStep(Step::Thread0Draw);
2466 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2467
2468 EXPECT_GL_NO_ERROR();
2469 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2470 EXPECT_EGL_SUCCESS();
2471 });
2472
2473 // Sample texture
2474 std::thread thread1 = std::thread([&]() {
2475 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2476
2477 // Wait for first thread to finish draw.
2478 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2479
2480 // Create High priority Context when Low priority Context already rendered to the texture.
2481 ctx[1] = window->createContext(ctx[0], extraAttributes);
2482 EXPECT_NE(EGL_NO_CONTEXT, ctx[1]);
2483
2484 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2485 EXPECT_EGL_SUCCESS();
2486
2487 glViewport(0, 0, kTexSize, kTexSize);
2488
2489 ASSERT_TRUE(thread0DrawSyncObj != nullptr);
2490 glWaitSync(thread0DrawSyncObj, 0, GL_TIMEOUT_IGNORED);
2491 ASSERT_GL_NO_ERROR();
2492
2493 // Should draw test color.
2494 glUseProgram(textureProgram);
2495 glBindTexture(GL_TEXTURE_2D, texture);
2496 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2497
2498 // Check test color in four corners.
2499 GLColor color = GLColor::white;
2500 EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2501 EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2502 EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2503 EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2504
2505 glDeleteSync(thread0DrawSyncObj);
2506 ASSERT_GL_NO_ERROR();
2507 thread0DrawSyncObj = nullptr;
2508
2509 threadSynchronization.nextStep(Step::Finish);
2510
2511 EXPECT_GL_NO_ERROR();
2512 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2513 EXPECT_EGL_SUCCESS();
2514 });
2515
2516 thread0.join();
2517 thread1.join();
2518
2519 ASSERT_NE(currentStep, Step::Abort);
2520
2521 // Clean up
2522 for (size_t t = 0; t < kThreadCount; ++t)
2523 {
2524 eglDestroySurface(dpy, surface[t]);
2525 eglDestroyContext(dpy, ctx[t]);
2526 }
2527 }
2528
2529 // Tests that Context with High Priority will correctly sample EGLImage target Texture rendered by
2530 // other Context with Low Priority into EGLImage source texture.
TEST_P(MultithreadingTestES3,RenderThenSampleDifferentContextPriorityUsingEGLImage)2531 TEST_P(MultithreadingTestES3, RenderThenSampleDifferentContextPriorityUsingEGLImage)
2532 {
2533 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2534 ANGLE_SKIP_TEST_IF(!hasWaitSyncExtension() || !hasGLSyncExtension());
2535 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_EGL_image"));
2536 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2537
2538 const bool reduceLoad = isSwiftshader();
2539 const size_t heavyDrawCount = reduceLoad ? 75 : 1000;
2540
2541 // Initialize contexts
2542 EGLWindow *window = getEGLWindow();
2543 EGLDisplay dpy = window->getDisplay();
2544 EGLConfig config = window->getConfig();
2545
2546 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_image_base"));
2547 ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(dpy, "EGL_KHR_gl_texture_2D_image"));
2548
2549 // Large enough texture to catch timing problems.
2550 constexpr GLsizei kTexSize = 1024;
2551 constexpr size_t kThreadCount = 2;
2552 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2553 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2554
2555 EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2556
2557 EGLint pbufferAttributes[kThreadCount][6] = {
2558 {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2559 {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2560
2561 EGLint attributes[] = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2562 EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2563 EGLint *extraAttributes = attributes;
2564 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2565 {
2566 attributes[2] = EGL_NONE;
2567 }
2568 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2569 {
2570 // Run tests with single priority anyway.
2571 extraAttributes += 2;
2572 }
2573
2574 for (size_t t = 0; t < kThreadCount; ++t)
2575 {
2576 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2577 EXPECT_EGL_SUCCESS();
2578
2579 attributes[1] = priorities[t];
2580 attributes[3] = mVirtualizationGroup++;
2581
2582 // Contexts not shared
2583 ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2584 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2585 }
2586
2587 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2588 std::mutex mutex;
2589 std::condition_variable condVar;
2590
2591 enum class Step
2592 {
2593 Start,
2594 Thread1Init,
2595 Thread0Draw,
2596 Thread1Draw,
2597 Finish,
2598 Abort,
2599 };
2600 Step currentStep = Step::Start;
2601
2602 EGLImage image = EGL_NO_IMAGE_KHR;
2603 EGLSyncKHR sync = EGL_NO_SYNC_KHR;
2604
2605 // Render to the EGLImage source texture.
2606 std::thread thread0 = std::thread([&]() {
2607 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2608
2609 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2610 EXPECT_EGL_SUCCESS();
2611
2612 // Create source texture.
2613 GLTexture texture;
2614 glBindTexture(GL_TEXTURE_2D, texture);
2615 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2616 nullptr);
2617 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2618 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2619
2620 GLFramebuffer fbo;
2621 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2622 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2623 glViewport(0, 0, kTexSize, kTexSize);
2624
2625 ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2626 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
2627 ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
2628
2629 // Wait for second thread to finish initializing.
2630 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Init));
2631
2632 // Enable additive blend
2633 glEnable(GL_BLEND);
2634 glBlendFunc(GL_ONE, GL_ONE);
2635
2636 GLuint query;
2637 glGenQueries(1, &query);
2638 glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2639 ASSERT_GL_NO_ERROR();
2640
2641 // Simulate heavy work...
2642 glUseProgram(redProgram);
2643 for (size_t j = 0; j < heavyDrawCount; ++j)
2644 {
2645 // Draw with Red color.
2646 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2647 }
2648
2649 // Draw with Green color.
2650 glUseProgram(greenProgram);
2651 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
2652
2653 // This should force "flushToPrimary()"
2654 glEndQuery(GL_TIME_ELAPSED_EXT);
2655 glDeleteQueries(1, &query);
2656 ASSERT_GL_NO_ERROR();
2657
2658 // Continue draw with Blue color after flush...
2659 glUseProgram(blueProgram);
2660 drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
2661
2662 sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, nullptr);
2663 EXPECT_NE(sync, EGL_NO_SYNC_KHR);
2664
2665 // Create EGLImage.
2666 image = eglCreateImageKHR(
2667 dpy, ctx[0], EGL_GL_TEXTURE_2D_KHR,
2668 reinterpret_cast<EGLClientBuffer>(static_cast<uintptr_t>(texture)), nullptr);
2669 ASSERT_EGL_SUCCESS();
2670
2671 // Notify second thread that draw is finished.
2672 threadSynchronization.nextStep(Step::Thread0Draw);
2673 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2674
2675 EXPECT_GL_NO_ERROR();
2676 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2677 EXPECT_EGL_SUCCESS();
2678 });
2679
2680 // Sample texture
2681 std::thread thread1 = std::thread([&]() {
2682 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2683
2684 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2685 EXPECT_EGL_SUCCESS();
2686
2687 glViewport(0, 0, kTexSize, kTexSize);
2688
2689 ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2690 essl1_shaders::fs::Texture2D());
2691 glUseProgram(textureProgram);
2692
2693 // Create target texture.
2694 GLTexture texture;
2695 glBindTexture(GL_TEXTURE_2D, texture);
2696 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2697 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2698
2699 // Wait for first thread to draw into the source texture.
2700 threadSynchronization.nextStep(Step::Thread1Init);
2701 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
2702
2703 // Wait for draw to complete
2704 ASSERT_TRUE(eglWaitSyncKHR(dpy, sync, 0));
2705
2706 // Specify target texture from EGLImage.
2707 glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
2708 ASSERT_GL_NO_ERROR();
2709
2710 // Should draw test color.
2711 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2712 ASSERT_GL_NO_ERROR();
2713
2714 // Check test color in four corners.
2715 GLColor color = GLColor::white;
2716 EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2717 EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2718 EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2719 EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2720
2721 EXPECT_EGL_TRUE(eglDestroyImageKHR(dpy, image));
2722 image = EGL_NO_IMAGE_KHR;
2723
2724 EXPECT_EGL_TRUE(eglDestroySyncKHR(dpy, sync));
2725 sync = EGL_NO_SYNC_KHR;
2726
2727 threadSynchronization.nextStep(Step::Finish);
2728
2729 EXPECT_GL_NO_ERROR();
2730 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2731 EXPECT_EGL_SUCCESS();
2732 });
2733
2734 thread0.join();
2735 thread1.join();
2736
2737 ASSERT_NE(currentStep, Step::Abort);
2738
2739 // Clean up
2740 for (size_t t = 0; t < kThreadCount; ++t)
2741 {
2742 eglDestroySurface(dpy, surface[t]);
2743 eglDestroyContext(dpy, ctx[t]);
2744 }
2745 }
2746
2747 // Tests mixing commands of Contexts with different Priorities in a single Command Buffers (Vulkan).
TEST_P(MultithreadingTestES3,ContextPriorityMixing)2748 TEST_P(MultithreadingTestES3, ContextPriorityMixing)
2749 {
2750 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2751 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_disjoint_timer_query"));
2752
2753 constexpr size_t kIterationCountMax = 10;
2754
2755 const bool reduceLoad = isSwiftshader();
2756 const size_t iterationCount = reduceLoad ? 3 : kIterationCountMax;
2757 const size_t heavyDrawCount = reduceLoad ? 25 : 100;
2758
2759 // Initialize contexts
2760 EGLWindow *window = getEGLWindow();
2761 EGLDisplay dpy = window->getDisplay();
2762 EGLConfig config = window->getConfig();
2763
2764 // Large enough texture to catch timing problems.
2765 constexpr GLsizei kTexSize = 1024;
2766 constexpr size_t kThreadCount = 2;
2767 EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};
2768 EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};
2769
2770 EGLint priorities[kThreadCount] = {EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG};
2771
2772 EGLint pbufferAttributes[kThreadCount][6] = {
2773 {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE},
2774 {EGL_WIDTH, kTexSize, EGL_HEIGHT, kTexSize, EGL_NONE, EGL_NONE}};
2775
2776 EGLint attributes[] = {EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_NONE,
2777 EGL_CONTEXT_VIRTUALIZATION_GROUP_ANGLE, EGL_NONE, EGL_NONE};
2778 EGLint *extraAttributes = attributes;
2779 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_ANGLE_context_virtualization"))
2780 {
2781 attributes[2] = EGL_NONE;
2782 }
2783 if (!IsEGLDisplayExtensionEnabled(dpy, "EGL_IMG_context_priority"))
2784 {
2785 // Run tests with single priority anyway.
2786 extraAttributes += 2;
2787 }
2788
2789 for (size_t t = 0; t < kThreadCount; ++t)
2790 {
2791 surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes[t]);
2792 EXPECT_EGL_SUCCESS();
2793
2794 attributes[1] = priorities[t];
2795 attributes[3] = mVirtualizationGroup++;
2796
2797 // Contexts not shared
2798 ctx[t] = window->createContext(EGL_NO_CONTEXT, extraAttributes);
2799 EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);
2800 }
2801
2802 // Synchronization tools to ensure the two threads are interleaved as designed by this test.
2803 std::mutex mutex;
2804 std::condition_variable condVar;
2805
2806 enum class Step
2807 {
2808 Start,
2809 Thread1DrawColor,
2810 Thread0Iterate,
2811 Finish = Thread1DrawColor + kIterationCountMax * 2,
2812 Abort,
2813 };
2814 Step currentStep = Step::Start;
2815
2816 auto makeStep = [](Step base, size_t i) {
2817 return static_cast<Step>(static_cast<size_t>(base) + i * 2);
2818 };
2819
2820 // Triggers commands submission.
2821 std::thread thread0 = std::thread([&]() {
2822 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2823
2824 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2825 EXPECT_EGL_SUCCESS();
2826
2827 ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
2828
2829 for (size_t i = 0; i < iterationCount; ++i)
2830 {
2831 ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread1DrawColor, i)));
2832
2833 glUseProgram(redProgram);
2834 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
2835 ASSERT_GL_NO_ERROR();
2836
2837 // This should perform commands submission.
2838 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2839 EXPECT_EGL_SUCCESS();
2840
2841 threadSynchronization.nextStep(makeStep(Step::Thread0Iterate, i));
2842
2843 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));
2844 EXPECT_EGL_SUCCESS();
2845 }
2846
2847 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
2848
2849 EXPECT_GL_NO_ERROR();
2850 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2851 EXPECT_EGL_SUCCESS();
2852 });
2853
2854 // Render and then sample texture.
2855 std::thread thread1 = std::thread([&]() {
2856 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2857
2858 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));
2859 EXPECT_EGL_SUCCESS();
2860
2861 glDisable(GL_DEPTH_TEST);
2862 glViewport(0, 0, kTexSize, kTexSize);
2863
2864 GLTexture texture;
2865 glBindTexture(GL_TEXTURE_2D, texture);
2866 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2867 nullptr);
2868 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2869 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2870
2871 GLFramebuffer fbo;
2872 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2873 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
2874
2875 ANGLE_GL_PROGRAM(colorProgram, essl1_shaders::vs::Simple(),
2876 essl1_shaders::fs::UniformColor());
2877 GLint colorLocation =
2878 glGetUniformLocation(colorProgram, angle::essl1_shaders::ColorUniform());
2879 ASSERT_NE(-1, colorLocation);
2880
2881 ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
2882 essl1_shaders::fs::Texture2D());
2883
2884 for (size_t i = 0; i < iterationCount; ++i)
2885 {
2886 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
2887 glUseProgram(colorProgram);
2888
2889 GLuint query;
2890 glGenQueries(1, &query);
2891 glBeginQuery(GL_TIME_ELAPSED_EXT, query);
2892 ASSERT_GL_NO_ERROR();
2893
2894 // Simulate heavy work...
2895 glUniform4f(colorLocation, 0.0f, 0.0f, 0.0f, 0.0f);
2896 for (size_t j = 0; j < heavyDrawCount; ++j)
2897 {
2898 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2899 }
2900
2901 // Draw with test color.
2902 GLColor color(i % 256, (i + 1) % 256, (i + 2) % 256, 255);
2903 Vector4 colorF = color.toNormalizedVector();
2904 glUniform4f(colorLocation, colorF.x(), colorF.y(), colorF.z(), colorF.w());
2905 drawQuad(colorProgram, essl1_shaders::PositionAttrib(), 0.5f);
2906 ASSERT_GL_NO_ERROR();
2907
2908 // This should force "flushToPrimary()"
2909 glEndQuery(GL_TIME_ELAPSED_EXT);
2910 glDeleteQueries(1, &query);
2911 ASSERT_GL_NO_ERROR();
2912
2913 threadSynchronization.nextStep(makeStep(Step::Thread1DrawColor, i));
2914 ASSERT_TRUE(threadSynchronization.waitForStep(makeStep(Step::Thread0Iterate, i)));
2915
2916 glBindFramebuffer(GL_FRAMEBUFFER, 0);
2917 glUseProgram(textureProgram);
2918
2919 // Should draw test color.
2920 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
2921 ASSERT_GL_NO_ERROR();
2922
2923 // Check test color in four corners.
2924 EXPECT_PIXEL_EQ(0, 0, color.R, color.G, color.B, color.A);
2925 EXPECT_PIXEL_EQ(0, kTexSize - 1, color.R, color.G, color.B, color.A);
2926 EXPECT_PIXEL_EQ(kTexSize - 1, 0, color.R, color.G, color.B, color.A);
2927 EXPECT_PIXEL_EQ(kTexSize - 1, kTexSize - 1, color.R, color.G, color.B, color.A);
2928 }
2929
2930 threadSynchronization.nextStep(Step::Finish);
2931
2932 EXPECT_GL_NO_ERROR();
2933 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
2934 EXPECT_EGL_SUCCESS();
2935 });
2936
2937 thread0.join();
2938 thread1.join();
2939
2940 ASSERT_NE(currentStep, Step::Abort);
2941
2942 // Clean up
2943 for (size_t t = 0; t < kThreadCount; ++t)
2944 {
2945 eglDestroySurface(dpy, surface[t]);
2946 eglDestroyContext(dpy, ctx[t]);
2947 }
2948 }
2949
2950 // Test that it is possible to upload textures in one thread and use them in another with
2951 // synchronization.
TEST_P(MultithreadingTestES3,MultithreadedTextureUploadAndDraw)2952 TEST_P(MultithreadingTestES3, MultithreadedTextureUploadAndDraw)
2953 {
2954 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
2955
2956 constexpr size_t kTexSize = 4;
2957 GLTexture texture1;
2958 GLTexture texture2;
2959 std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
2960 std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
2961
2962 // Sync primitives
2963 GLsync sync = nullptr;
2964 std::mutex mutex;
2965 std::condition_variable condVar;
2966
2967 enum class Step
2968 {
2969 Start,
2970 Thread0UploadFinish,
2971 Finish,
2972 Abort,
2973 };
2974 Step currentStep = Step::Start;
2975
2976 // Threads to upload and draw with textures.
2977 auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
2978 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
2979 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));
2980
2981 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
2982
2983 // Two mipmap textures are defined here. They are used for drawing in the other thread.
2984 glBindTexture(GL_TEXTURE_2D, texture1);
2985 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2986 textureColors1.data());
2987 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
2988 GL_UNSIGNED_BYTE, textureColors1.data());
2989 glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
2990 GL_UNSIGNED_BYTE, textureColors1.data());
2991 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2992 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2993 ASSERT_GL_NO_ERROR();
2994
2995 glBindTexture(GL_TEXTURE_2D, texture2);
2996 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2997 textureColors2.data());
2998 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
2999 GL_UNSIGNED_BYTE, textureColors2.data());
3000 glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, kTexSize / 4, kTexSize / 4, 0, GL_RGBA,
3001 GL_UNSIGNED_BYTE, textureColors2.data());
3002 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3003 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3004 ASSERT_GL_NO_ERROR();
3005
3006 // Create a sync object to be used for the draw thread.
3007 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3008 glFlush();
3009 ASSERT_NE(sync, nullptr);
3010
3011 threadSynchronization.nextStep(Step::Thread0UploadFinish);
3012 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3013 };
3014
3015 auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3016 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3017 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0UploadFinish));
3018
3019 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3020
3021 // Wait for the sync object to be signaled.
3022 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3023 ASSERT_GL_NO_ERROR();
3024
3025 // Draw using the textures from the texture upload thread.
3026 ANGLE_GL_PROGRAM(textureProgram, essl1_shaders::vs::Texture2D(),
3027 essl1_shaders::fs::Texture2D());
3028 glUseProgram(textureProgram);
3029
3030 glBindTexture(GL_TEXTURE_2D, texture1);
3031 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3032 glFlush();
3033 ASSERT_GL_NO_ERROR();
3034 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3035
3036 glBindTexture(GL_TEXTURE_2D, texture2);
3037 drawQuad(textureProgram, essl1_shaders::PositionAttrib(), 0.5f);
3038 glFlush();
3039 ASSERT_GL_NO_ERROR();
3040 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3041
3042 threadSynchronization.nextStep(Step::Finish);
3043 };
3044
3045 std::array<LockStepThreadFunc, 2> threadFuncs = {
3046 std::move(thread0Upload),
3047 std::move(thread1Draw),
3048 };
3049
3050 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3051
3052 ASSERT_NE(currentStep, Step::Abort);
3053 }
3054
3055 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3056 // previous context, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnNewThread)3057 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnNewThread)
3058 {
3059 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3060
3061 constexpr size_t kTexSize = 4;
3062 GLTexture texture1;
3063 GLTexture texture2;
3064 std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3065 std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3066
3067 EGLWindow *window = getEGLWindow();
3068 EGLDisplay dpy = window->getDisplay();
3069
3070 std::thread thread = std::thread([&]() {
3071 // Create a context and upload the textures.
3072 EGLContext ctx1 = createMultithreadedContext(window, EGL_NO_CONTEXT);
3073 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3074
3075 glBindTexture(GL_TEXTURE_2D, texture1);
3076 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3077 textureColors1.data());
3078 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3079 GL_UNSIGNED_BYTE, textureColors1.data());
3080 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3081 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3082 ASSERT_GL_NO_ERROR();
3083
3084 glBindTexture(GL_TEXTURE_2D, texture2);
3085 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3086 textureColors2.data());
3087 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3088 GL_UNSIGNED_BYTE, textureColors2.data());
3089 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3090 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3091 ASSERT_GL_NO_ERROR();
3092
3093 // Create a new context and use the uploaded textures.
3094 EGLContext ctx2 = createMultithreadedContext(window, ctx1);
3095 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx2));
3096
3097 GLFramebuffer fbo;
3098 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3099
3100 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3101 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3102
3103 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3104 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3105
3106 // Destroy the contexts.
3107 EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx2));
3108 EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3109 });
3110
3111 thread.join();
3112 }
3113
3114 // Test that it is possible to create a new context after uploading mutable mipmap textures in the
3115 // main thread, and use them in the new context.
TEST_P(MultithreadingTestES3,CreateNewContextAfterTextureUploadOnMainThread)3116 TEST_P(MultithreadingTestES3, CreateNewContextAfterTextureUploadOnMainThread)
3117 {
3118 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3119
3120 EGLWindow *window = getEGLWindow();
3121 EGLDisplay dpy = window->getDisplay();
3122
3123 // Upload the textures.
3124 constexpr size_t kTexSize = 4;
3125 GLTexture texture1;
3126 GLTexture texture2;
3127 std::vector<GLColor> textureColors1(kTexSize * kTexSize, GLColor::red);
3128 std::vector<GLColor> textureColors2(kTexSize * kTexSize, GLColor::green);
3129
3130 glBindTexture(GL_TEXTURE_2D, texture1);
3131 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3132 textureColors1.data());
3133 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3134 GL_UNSIGNED_BYTE, textureColors1.data());
3135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3137 ASSERT_GL_NO_ERROR();
3138
3139 glBindTexture(GL_TEXTURE_2D, texture2);
3140 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
3141 textureColors2.data());
3142 glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kTexSize / 2, kTexSize / 2, 0, GL_RGBA,
3143 GL_UNSIGNED_BYTE, textureColors2.data());
3144 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3145 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3146 ASSERT_GL_NO_ERROR();
3147
3148 GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3149
3150 std::thread thread = std::thread([&]() {
3151 // Create a context.
3152 EGLContext ctx1 = createMultithreadedContext(window, window->getContext());
3153 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, ctx1));
3154
3155 // Wait for the sync object to be signaled.
3156 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3157 ASSERT_GL_NO_ERROR();
3158
3159 // Use the uploaded textures in the main thread.
3160 GLFramebuffer fbo;
3161 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
3162
3163 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture1, 0);
3164 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::red);
3165
3166 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture2, 0);
3167 EXPECT_PIXEL_RECT_EQ(0, 0, kTexSize, kTexSize, GLColor::green);
3168
3169 // Destroy the context.
3170 EXPECT_EGL_TRUE(eglDestroyContext(dpy, ctx1));
3171 });
3172
3173 thread.join();
3174 }
3175
3176 // Test when lots of upload happens on a different thread at the same time as the main thread doing
3177 // draws.
TEST_P(MultithreadingTestES3,SimultaneousUploadAndDraw)3178 TEST_P(MultithreadingTestES3, SimultaneousUploadAndDraw)
3179 {
3180 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3181
3182 // The following shader is used to create busy work while the worker thread is doing something.
3183 // It intentionally spreads its uniforms and inputs so the main thread has to make many GL
3184 // calls.
3185 constexpr char kBusyDrawVS[] = R"(#version 300 es
3186 uniform mediump float x0;
3187 uniform mediump float y0;
3188 uniform mediump float x1;
3189 uniform mediump float y1;
3190
3191 in mediump float r;
3192 in mediump float g;
3193 in mediump float b;
3194 in mediump float a;
3195
3196 out mediump vec4 color;
3197
3198 void main()
3199 {
3200 // gl_VertexID x y
3201 // 0 -1 -1
3202 // 1 1 -1
3203 // 2 -1 1
3204 // 3 1 1
3205 int bit0 = gl_VertexID & 1;
3206 int bit1 = gl_VertexID >> 1;
3207 gl_Position.x = bit0 == 0 ? x0 : x1;
3208 gl_Position.y = bit1 == 0 ? y0 : y1;
3209 gl_Position.z = 0.;
3210 gl_Position.w = 1.;
3211
3212 color = vec4(r, g, b, a);
3213 })";
3214 constexpr char kBusyDrawFS[] = R"(#version 300 es
3215
3216 in mediump vec4 color;
3217 out mediump vec4 colorOut;
3218
3219 void main()
3220 {
3221 colorOut = color;
3222 })";
3223
3224 // The following shader is used to consume the results of texture uploads, ensuring appropriate
3225 // synchronization.
3226 constexpr char kTextureDrawVS[] = R"(#version 300 es
3227 out mediump vec2 uv;
3228
3229 void main()
3230 {
3231 // gl_VertexID x y
3232 // 0 -1 -1
3233 // 1 1 -1
3234 // 2 -1 1
3235 // 3 1 1
3236 int bit0 = gl_VertexID & 1;
3237 int bit1 = gl_VertexID >> 1;
3238 gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1);
3239 uv = vec2(bit0, bit1);
3240 })";
3241 constexpr char kTextureDrawFS[] = R"(#version 300 es
3242
3243 uniform mediump sampler2D s0;
3244 uniform mediump sampler2D s1;
3245 uniform mediump sampler2D s2;
3246 uniform mediump sampler2D s3;
3247 uniform mediump sampler2D s4;
3248 uniform mediump sampler2D s5;
3249 uniform mediump sampler2D s6;
3250 uniform mediump sampler2D s7;
3251 uniform mediump sampler2D s8;
3252 uniform mediump sampler2D s9;
3253
3254 in mediump vec2 uv;
3255 out mediump vec4 colorOut;
3256
3257 void main()
3258 {
3259 highp vec4 result = texture(s0, uv) +
3260 texture(s1, uv) +
3261 texture(s2, uv) +
3262 texture(s3, uv) +
3263 texture(s4, uv) +
3264 texture(s5, uv) +
3265 texture(s6, uv) +
3266 texture(s7, uv) +
3267 texture(s8, uv) +
3268 texture(s9, uv);
3269 result /= 10.;
3270
3271 colorOut = result;
3272 })";
3273
3274 constexpr uint32_t kTextureCount = 10;
3275 GLuint textures[kTextureCount];
3276
3277 ASSERT(IsGLExtensionEnabled("GL_KHR_texture_compression_astc_ldr") ||
3278 IsGLExtensionEnabled("GL_EXT_texture_compression_bptc"));
3279 // Note ASTC may be emulated in ANGLE, so check for BPTC first
3280 const bool hasBPTC = IsGLExtensionEnabled("GL_EXT_texture_compression_bptc");
3281 const GLenum compressedFormat =
3282 hasBPTC ? GL_COMPRESSED_RGBA_BPTC_UNORM_EXT : GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
3283
3284 std::vector<uint8_t> textureData[kTextureCount];
3285
3286 constexpr int kSurfaceWidth = 256;
3287 constexpr int kSurfaceHeight = 512;
3288 constexpr int kTexSize = 1024;
3289
3290 // Sync primitives
3291 GLsync sync = nullptr;
3292 std::mutex mutex;
3293 std::condition_variable condVar;
3294
3295 enum class Step
3296 {
3297 Start,
3298 Thread1Ready,
3299 Thread0UploadFinish,
3300 Finish,
3301 Abort,
3302 };
3303 Step currentStep = Step::Start;
3304
3305 // Threads to upload and draw with textures.
3306 auto thread0Upload = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3307 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3308 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3309
3310 // Wait for the other thread to set everything up
3311 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3312
3313 // Perform uploads while the other thread does draws
3314 for (uint32_t i = 0; i < kTextureCount; ++i)
3315 {
3316 glBindTexture(GL_TEXTURE_2D, textures[i]);
3317 glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kTexSize, kTexSize, compressedFormat,
3318 static_cast<GLsizei>(textureData[i].size()),
3319 textureData[i].data());
3320 }
3321 ASSERT_GL_NO_ERROR();
3322
3323 // Create a sync object to be used for the draw thread.
3324 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
3325 ASSERT_NE(sync, nullptr);
3326 ASSERT_GL_NO_ERROR();
3327
3328 threadSynchronization.nextStep(Step::Thread0UploadFinish);
3329 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3330 };
3331
3332 auto thread1Draw = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3333 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3334 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3335
3336 ANGLE_GL_PROGRAM(busyDrawProgram, kBusyDrawVS, kBusyDrawFS);
3337
3338 // Set up the test. Don't let the other thread work yet.
3339 glUseProgram(busyDrawProgram);
3340 GLuint busyDrawX0Loc = glGetUniformLocation(busyDrawProgram, "x0");
3341 GLuint busyDrawY0Loc = glGetUniformLocation(busyDrawProgram, "y0");
3342 GLuint busyDrawX1Loc = glGetUniformLocation(busyDrawProgram, "x1");
3343 GLuint busyDrawY1Loc = glGetUniformLocation(busyDrawProgram, "y1");
3344 GLuint busyDrawRLoc = glGetAttribLocation(busyDrawProgram, "r");
3345 GLuint busyDrawGLoc = glGetAttribLocation(busyDrawProgram, "g");
3346 GLuint busyDrawBLoc = glGetAttribLocation(busyDrawProgram, "b");
3347 GLuint busyDrawALoc = glGetAttribLocation(busyDrawProgram, "a");
3348
3349 ANGLE_GL_PROGRAM(textureDrawProgram, kTextureDrawVS, kTextureDrawFS);
3350 GLuint textureDrawSamplerLoc[kTextureCount] = {};
3351
3352 glUseProgram(textureDrawProgram);
3353 glGenTextures(kTextureCount, textures);
3354 for (uint32_t i = 0; i < kTextureCount; ++i)
3355 {
3356 std::ostringstream name;
3357 name << "s" << i;
3358
3359 textureDrawSamplerLoc[i] = glGetUniformLocation(textureDrawProgram, name.str().c_str());
3360
3361 glBindTexture(GL_TEXTURE_2D, textures[i]);
3362 glTexStorage2D(GL_TEXTURE_2D, 1, compressedFormat, kTexSize, kTexSize);
3363
3364 // Both ASTC 4x4 and BPTC have 1 byte per pixel. The textures' contents are arbitrary
3365 // but distinct.
3366 textureData[i].resize(kTexSize * kTexSize);
3367 for (int y = 0; y < kTexSize; ++y)
3368 {
3369 for (int x = 0; x < kTexSize; ++x)
3370 {
3371 textureData[i][y * kTexSize + x] = (i * 50 + y + x) % 255;
3372 }
3373 }
3374 }
3375 ASSERT_GL_NO_ERROR();
3376
3377 // Now that everything is set up, let the upload thread work while this thread does draws.
3378 threadSynchronization.nextStep(Step::Thread1Ready);
3379
3380 int w = kSurfaceWidth;
3381 int h = kSurfaceHeight;
3382
3383 glClear(GL_COLOR_BUFFER_BIT);
3384 glViewport(0, 0, w, h);
3385 glUseProgram(busyDrawProgram);
3386 for (uint32_t y = 0; y < 8; ++y)
3387 {
3388 for (uint32_t x = 0; x < 8; ++x)
3389 {
3390 float width = w / 4;
3391 float height = h / 8;
3392
3393 glUniform1f(busyDrawX0Loc, x * width / w - 1);
3394 glUniform1f(busyDrawY0Loc, y * height / h);
3395 glUniform1f(busyDrawX1Loc, (x + 1) * width / w - 1);
3396 glUniform1f(busyDrawY1Loc, (y + 1) * height / h);
3397
3398 glVertexAttrib1f(busyDrawRLoc, x / 8.0f);
3399 glVertexAttrib1f(busyDrawGLoc, y / 8.0f);
3400 glVertexAttrib1f(busyDrawBLoc, 0);
3401 glVertexAttrib1f(busyDrawALoc, 1);
3402
3403 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3404 }
3405 }
3406 ASSERT_GL_NO_ERROR();
3407
3408 // Wait for the other thread to finish with uploads.
3409 threadSynchronization.waitForStep(Step::Thread0UploadFinish);
3410
3411 // Wait for fence and use all textures in a draw.
3412 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
3413
3414 glUseProgram(textureDrawProgram);
3415 for (uint32_t i = 0; i < kTextureCount; ++i)
3416 {
3417 glActiveTexture(GL_TEXTURE0 + i);
3418 glBindTexture(GL_TEXTURE_2D, textures[i]);
3419 glUniform1i(textureDrawSamplerLoc[i], i);
3420 }
3421 glViewport(0, 0, w, h / 2);
3422 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
3423 ASSERT_GL_NO_ERROR();
3424
3425 threadSynchronization.nextStep(Step::Finish);
3426
3427 // Verify results
3428 for (uint32_t y = 0; y < 8; ++y)
3429 {
3430 for (uint32_t x = 0; x < 8; ++x)
3431 {
3432 int width = w / 8;
3433 int height = h / 16;
3434
3435 EXPECT_PIXEL_COLOR_NEAR(x * width + width / 2, h - (y * height + height / 2),
3436 GLColor(x * 255 / 8, (7 - y) * 255 / 8, 0, 255), 1);
3437 }
3438 }
3439 ASSERT_GL_NO_ERROR();
3440
3441 for (uint32_t x = 0; x < 8; ++x)
3442 {
3443 // The compressed data is gibberish, just ensure it's not all black.
3444 EXPECT_PIXEL_NE(x * w / 8, h / 4, 0, 0, 0, 0);
3445 }
3446 ASSERT_GL_NO_ERROR();
3447 };
3448
3449 std::array<LockStepThreadFunc, 2> threadFuncs = {
3450 std::move(thread0Upload),
3451 std::move(thread1Draw),
3452 };
3453
3454 RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
3455 threadFuncs.data());
3456
3457 ASSERT_NE(currentStep, Step::Abort);
3458 }
3459
3460 // Test that calling glUniformBlockBinding on one context affects all contexts.
TEST_P(MultithreadingTestES3,UniformBlockBinding)3461 TEST_P(MultithreadingTestES3, UniformBlockBinding)
3462 {
3463 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3464
3465 constexpr char kVS[] = R"(#version 300 es
3466 void main()
3467 {
3468 vec2 pos = vec2(0.0);
3469 switch (gl_VertexID) {
3470 case 0: pos = vec2(-1.0, -1.0); break;
3471 case 1: pos = vec2(3.0, -1.0); break;
3472 case 2: pos = vec2(-1.0, 3.0); break;
3473 };
3474 gl_Position = vec4(pos, 0.0, 1.0);
3475 })";
3476 constexpr char kFS[] = R"(#version 300 es
3477 out mediump vec4 colorOut;
3478
3479 layout(std140) uniform buffer { mediump vec4 color; };
3480
3481 void main()
3482 {
3483 colorOut = color;
3484 })";
3485
3486 GLProgram program;
3487 GLint uniformBufferIndex;
3488
3489 // Sync primitives
3490 std::mutex mutex;
3491 std::condition_variable condVar;
3492
3493 enum class Step
3494 {
3495 Start,
3496 Thread1Ready,
3497 Thread0BindingChanged,
3498 Thread1FinishedDrawing,
3499 Finish,
3500 Abort,
3501 };
3502 Step currentStep = Step::Start;
3503
3504 // Threads to create programs and draw with different uniform blocks.
3505 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3506 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3507 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3508
3509 // Create buffers bound to bindings 1 and 2
3510 constexpr std::array<float, 4> kRed = {1, 0, 0, 1};
3511 constexpr std::array<float, 4> kTransparentGreen = {0, 1, 0, 0};
3512 GLBuffer red, transparentGreen;
3513 glBindBuffer(GL_UNIFORM_BUFFER, red);
3514 glBufferData(GL_UNIFORM_BUFFER, sizeof(kRed), kRed.data(), GL_STATIC_DRAW);
3515 glBindBuffer(GL_UNIFORM_BUFFER, transparentGreen);
3516 glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentGreen), kTransparentGreen.data(),
3517 GL_STATIC_DRAW);
3518
3519 glBindBufferBase(GL_UNIFORM_BUFFER, 1, transparentGreen);
3520 glBindBufferBase(GL_UNIFORM_BUFFER, 2, red);
3521
3522 // Wait for the other thread to set everything up
3523 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3524
3525 // Issue a draw call. The buffer should be transparent green now
3526 glClearColor(0, 0, 0, 0);
3527 glClear(GL_COLOR_BUFFER_BIT);
3528 glEnable(GL_BLEND);
3529 glBlendFunc(GL_ONE, GL_ONE);
3530
3531 glUseProgram(program);
3532 glDrawArrays(GL_TRIANGLES, 0, 3);
3533
3534 // Change the binding
3535 glUniformBlockBinding(program, uniformBufferIndex, 1);
3536 ASSERT_GL_NO_ERROR();
3537
3538 // Let the other thread work before any deferred operations for the binding change above are
3539 // processed in this context.
3540 threadSynchronization.nextStep(Step::Thread0BindingChanged);
3541 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3542
3543 // Draw again, it should accumulate blue and the buffer should become magenta.
3544 glDrawArrays(GL_TRIANGLES, 0, 3);
3545
3546 // Verify results
3547 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::yellow);
3548 ASSERT_GL_NO_ERROR();
3549
3550 threadSynchronization.nextStep(Step::Finish);
3551 };
3552
3553 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3554 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3555 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3556
3557 // Create buffers bound to bindings 1 and 2
3558 constexpr std::array<float, 4> kBlue = {0, 0, 1, 1};
3559 constexpr std::array<float, 4> kTransparentRed = {1, 0, 0, 0};
3560 GLBuffer blue, transparentRed;
3561 glBindBuffer(GL_UNIFORM_BUFFER, blue);
3562 glBufferData(GL_UNIFORM_BUFFER, sizeof(kBlue), kBlue.data(), GL_STATIC_DRAW);
3563 glBindBuffer(GL_UNIFORM_BUFFER, transparentRed);
3564 glBufferData(GL_UNIFORM_BUFFER, sizeof(kTransparentRed), kTransparentRed.data(),
3565 GL_STATIC_DRAW);
3566
3567 glBindBufferBase(GL_UNIFORM_BUFFER, 1, blue);
3568 glBindBufferBase(GL_UNIFORM_BUFFER, 2, transparentRed);
3569
3570 // Create the program
3571 program.makeRaster(kVS, kFS);
3572 glUseProgram(program);
3573 uniformBufferIndex = glGetUniformBlockIndex(program, "buffer");
3574
3575 // Configure the buffer binding to binding 2
3576 glUniformBlockBinding(program, uniformBufferIndex, 2);
3577 ASSERT_GL_NO_ERROR();
3578
3579 // Issue a draw call. The buffer should be transparent red now
3580 glClearColor(0, 0, 0, 0);
3581 glClear(GL_COLOR_BUFFER_BIT);
3582 glEnable(GL_BLEND);
3583 glBlendFunc(GL_ONE, GL_ONE);
3584
3585 glDrawArrays(GL_TRIANGLES, 0, 3);
3586
3587 // Now that everything is set up, let the other thread continue
3588 threadSynchronization.nextStep(Step::Thread1Ready);
3589 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0BindingChanged));
3590
3591 // The other thread has changed the binding. Draw again, it should accumulate blue and the
3592 // buffer should become magenta.
3593 glDrawArrays(GL_TRIANGLES, 0, 3);
3594
3595 // Verify results
3596 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
3597 ASSERT_GL_NO_ERROR();
3598
3599 // Tell the other thread to finish up.
3600 threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3601 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3602 };
3603
3604 std::array<LockStepThreadFunc, 2> threadFuncs = {
3605 std::move(thread0),
3606 std::move(thread1),
3607 };
3608
3609 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3610
3611 ASSERT_NE(currentStep, Step::Abort);
3612 }
3613
3614 // Test that observers are notified of a change in foveation state of a texture
TEST_P(MultithreadingTestES3,SharedFoveatedTexture)3615 TEST_P(MultithreadingTestES3, SharedFoveatedTexture)
3616 {
3617 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3618 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_QCOM_texture_foveated"));
3619
3620 // Shared texture
3621 GLTexture texture;
3622
3623 // Sync primitives
3624 std::mutex mutex;
3625 std::condition_variable condVar;
3626
3627 enum class Step
3628 {
3629 Start,
3630 Thread0Draw,
3631 Thread1Draw,
3632 Thread0ConfiguredTextureFoveation,
3633 Finish,
3634 Abort,
3635 };
3636 Step currentStep = Step::Start;
3637
3638 // Thread to configure texture foveation.
3639 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3640 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3641 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3642
3643 // Create non-foveated framebuffer and attach shared texture as color attachment
3644 GLFramebuffer framebuffer;
3645 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3646 glActiveTexture(GL_TEXTURE0);
3647 glBindTexture(GL_TEXTURE_2D, texture);
3648 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
3649 GL_UNSIGNED_BYTE, nullptr);
3650 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
3651 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
3652 EXPECT_GL_NO_ERROR();
3653 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3654 ASSERT_GL_NO_ERROR();
3655 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3656
3657 // Render before configuring foveation on the texture
3658 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
3659 glUseProgram(greenProgram);
3660
3661 // Draw
3662 drawQuad(greenProgram, essl1_shaders::PositionAttrib(), 0.5f);
3663 EXPECT_GL_NO_ERROR();
3664
3665 // Verify results
3666 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
3667 ASSERT_GL_NO_ERROR();
3668
3669 threadSynchronization.nextStep(Step::Thread0Draw);
3670 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));
3671
3672 // Configure foveation for the texture
3673 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM,
3674 GL_FOVEATION_ENABLE_BIT_QCOM);
3675 EXPECT_GL_NO_ERROR();
3676 glTextureFoveationParametersQCOM(texture, 0, 0, 0.0f, 0.0f, 8.0f, 8.0f, 0.0f);
3677 EXPECT_GL_NO_ERROR();
3678
3679 threadSynchronization.nextStep(Step::Thread0ConfiguredTextureFoveation);
3680 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3681 };
3682
3683 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3684 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3685 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3686
3687 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));
3688
3689 // Create non-foveated framebuffer and attach shared texture as color attachment
3690 GLFramebuffer framebuffer;
3691 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3692 glActiveTexture(GL_TEXTURE0);
3693 glBindTexture(GL_TEXTURE_2D, texture);
3694 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3695 ASSERT_GL_NO_ERROR();
3696 EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
3697
3698 // Render before configuring foveation on the texture
3699 ANGLE_GL_PROGRAM(redProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
3700 glUseProgram(redProgram);
3701
3702 // Draw
3703 drawQuad(redProgram, essl1_shaders::PositionAttrib(), 0.5f);
3704 EXPECT_GL_NO_ERROR();
3705
3706 // Verify results
3707 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3708 ASSERT_GL_NO_ERROR();
3709
3710 threadSynchronization.nextStep(Step::Thread1Draw);
3711 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ConfiguredTextureFoveation));
3712
3713 // Render after texture foveation was configured
3714 ANGLE_GL_PROGRAM(blueProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Blue());
3715 glUseProgram(blueProgram);
3716
3717 // Draw
3718 drawQuad(blueProgram, essl1_shaders::PositionAttrib(), 0.5f);
3719 EXPECT_GL_NO_ERROR();
3720
3721 // Verify results
3722 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
3723 ASSERT_GL_NO_ERROR();
3724
3725 threadSynchronization.nextStep(Step::Finish);
3726 };
3727
3728 std::array<LockStepThreadFunc, 2> threadFuncs = {
3729 std::move(thread0),
3730 std::move(thread1),
3731 };
3732
3733 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3734
3735 ASSERT_NE(currentStep, Step::Abort);
3736 }
3737
3738 // Test GL_EXT_sRGB_write_control works as expected when multiple contexts are used
TEST_P(MultithreadingTestES3,SharedSrgbTextureMultipleContexts)3739 TEST_P(MultithreadingTestES3, SharedSrgbTextureMultipleContexts)
3740 {
3741 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3742 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_sRGB_write_control"));
3743
3744 constexpr angle::GLColor encodedToSrgbColor(64, 127, 191, 255);
3745 constexpr angle::GLColor inputColor(13, 54, 133, 255);
3746
3747 EGLWindow *window = getEGLWindow();
3748 EGLDisplay dpy = window->getDisplay();
3749 EGLContext context1 = window->createContext(EGL_NO_CONTEXT, nullptr);
3750 EGLContext context2 = window->createContext(context1, nullptr);
3751
3752 // Shared texture
3753 GLTexture texture;
3754
3755 // Shared program
3756 GLuint program;
3757
3758 // Sync primitives
3759 std::mutex mutex;
3760 std::condition_variable condVar;
3761
3762 enum class Step
3763 {
3764 Start,
3765 Thread0Draw1,
3766 Thread1Draw1,
3767 Thread0Draw2,
3768 Thread1Draw2,
3769 Finish,
3770 Abort,
3771 };
3772 Step currentStep = Step::Start;
3773
3774 // Thread0 rendering to shared texture.
3775 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3776 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3777
3778 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context1));
3779 EXPECT_EGL_SUCCESS();
3780
3781 glBindTexture(GL_TEXTURE_2D, texture);
3782 glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB_ALPHA_EXT, 1, 1, 0, GL_SRGB_ALPHA_EXT,
3783 GL_UNSIGNED_BYTE, nullptr);
3784
3785 GLFramebuffer framebuffer;
3786 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3787 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3788
3789 program = CompileProgram(essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
3790 ASSERT_NE(0u, program);
3791
3792 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
3793 ASSERT_NE(-1, colorLocation);
3794
3795 glUseProgram(program);
3796 glUniform4fv(colorLocation, 1, inputColor.toNormalizedVector().data());
3797
3798 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
3799 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3800 EXPECT_PIXEL_COLOR_NEAR(0, 0, inputColor, 1.0);
3801
3802 threadSynchronization.nextStep(Step::Thread0Draw1);
3803 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw1));
3804
3805 glEnable(GL_FRAMEBUFFER_SRGB_EXT);
3806 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3807 EXPECT_PIXEL_COLOR_NEAR(0, 0, encodedToSrgbColor, 1.0);
3808
3809 threadSynchronization.nextStep(Step::Thread0Draw2);
3810 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw2));
3811
3812 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
3813 EXPECT_EGL_SUCCESS();
3814
3815 threadSynchronization.nextStep(Step::Finish);
3816 };
3817
3818 // Thread1 rendering to shared texture.
3819 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3820 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3821 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context2));
3822 EXPECT_EGL_SUCCESS();
3823
3824 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw1));
3825
3826 glBindTexture(GL_TEXTURE_2D, texture);
3827 GLFramebuffer framebuffer;
3828 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
3829 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
3830
3831 ASSERT_NE(0u, program);
3832 GLint colorLocation = glGetUniformLocation(program, essl1_shaders::ColorUniform());
3833 ASSERT_NE(-1, colorLocation);
3834
3835 glUseProgram(program);
3836 glUniform4fv(colorLocation, 1, inputColor.toNormalizedVector().data());
3837
3838 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3839 EXPECT_PIXEL_COLOR_NEAR(0, 0, encodedToSrgbColor, 1.0);
3840
3841 threadSynchronization.nextStep(Step::Thread1Draw1);
3842 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw2));
3843
3844 glBindTexture(GL_TEXTURE_2D, texture);
3845 glDisable(GL_FRAMEBUFFER_SRGB_EXT);
3846 drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
3847 EXPECT_PIXEL_COLOR_NEAR(0, 0, inputColor, 1.0);
3848
3849 threadSynchronization.nextStep(Step::Thread1Draw2);
3850 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3851
3852 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));
3853 EXPECT_EGL_SUCCESS();
3854 };
3855
3856 std::array<LockStepThreadFunc, 2> threadFuncs = {
3857 std::move(thread0),
3858 std::move(thread1),
3859 };
3860
3861 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3862
3863 // Cleanup
3864 EXPECT_EGL_TRUE(eglDestroyContext(dpy, context1));
3865 EXPECT_EGL_TRUE(eglDestroyContext(dpy, context2));
3866 EXPECT_EGL_SUCCESS();
3867
3868 ASSERT_NE(currentStep, Step::Abort);
3869 }
3870
3871 // Test that a program linked in one context can be bound in another context while link may be
3872 // happening in parallel.
TEST_P(MultithreadingTest,ProgramLinkAndBind)3873 TEST_P(MultithreadingTest, ProgramLinkAndBind)
3874 {
3875 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3876
3877 GLuint vs;
3878 GLuint redfs;
3879 GLuint greenfs;
3880
3881 GLuint program;
3882
3883 // Sync primitives
3884 std::mutex mutex;
3885 std::condition_variable condVar;
3886
3887 enum class Step
3888 {
3889 Start,
3890 Thread1Ready,
3891 Thread0ProgramLinked,
3892 Thread1FinishedDrawing,
3893 Finish,
3894 Abort,
3895 };
3896 Step currentStep = Step::Start;
3897
3898 // Threads to create programs and draw.
3899 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3900 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3901 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3902
3903 // Wait for thread 1 to bind the program before linking it
3904 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
3905
3906 glUseProgram(program);
3907
3908 // Link a program, but don't resolve link.
3909 glDetachShader(program, greenfs);
3910 glAttachShader(program, redfs);
3911 glLinkProgram(program);
3912
3913 // Let the other thread bind and use the program.
3914 threadSynchronization.nextStep(Step::Thread0ProgramLinked);
3915 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1FinishedDrawing));
3916
3917 // Draw in this context too
3918 drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3919
3920 // Verify results
3921 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3922 ASSERT_GL_NO_ERROR();
3923
3924 threadSynchronization.nextStep(Step::Finish);
3925 };
3926
3927 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
3928 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
3929 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
3930
3931 vs = CompileShader(GL_VERTEX_SHADER, essl1_shaders::vs::Simple());
3932 redfs = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Red());
3933 greenfs = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Green());
3934 program = glCreateProgram();
3935
3936 glAttachShader(program, vs);
3937 glAttachShader(program, greenfs);
3938 glLinkProgram(program);
3939 ASSERT_NE(CheckLinkStatusAndReturnProgram(program, true), 0u);
3940
3941 // Bind the program before it's relinked. Otherwise the program is resolved before the
3942 // binding happens.
3943 glUseProgram(program);
3944
3945 threadSynchronization.nextStep(Step::Thread1Ready);
3946 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0ProgramLinked));
3947
3948 // Unbind and rebind for extra testing
3949 glUseProgram(0);
3950 glUseProgram(program);
3951
3952 // Issue a draw call
3953 drawQuad(program, essl1_shaders::PositionAttrib(), 0);
3954
3955 // Verify results
3956 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
3957 ASSERT_GL_NO_ERROR();
3958
3959 // Tell the other thread to finish up.
3960 threadSynchronization.nextStep(Step::Thread1FinishedDrawing);
3961 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
3962 };
3963
3964 std::array<LockStepThreadFunc, 2> threadFuncs = {
3965 std::move(thread0),
3966 std::move(thread1),
3967 };
3968
3969 RunLockStepThreads(getEGLWindow(), threadFuncs.size(), threadFuncs.data());
3970
3971 ASSERT_NE(currentStep, Step::Abort);
3972 }
3973
3974 // Test that two contexts in share group can generate, delete and bind buffers for themselves in
3975 // parallel.
TEST_P(MultithreadingTestES3,SimultaneousBufferBindAndGen)3976 TEST_P(MultithreadingTestES3, SimultaneousBufferBindAndGen)
3977 {
3978 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
3979
3980 constexpr char kFS[] = R"(#version 300 es
3981 precision mediump float;
3982
3983 layout(std140) uniform Block
3984 {
3985 vec4 colorIn;
3986 };
3987
3988 out vec4 color;
3989
3990 void main()
3991 {
3992 color = colorIn;
3993 })";
3994
3995 constexpr int kSurfaceWidth = 32;
3996 constexpr int kSurfaceHeight = 128;
3997
3998 // Sync primitives
3999 std::mutex mutex;
4000 std::condition_variable condVar;
4001
4002 enum class Step
4003 {
4004 Start,
4005 Thread0Ready,
4006 Thread1Ready,
4007 Finish,
4008 Abort,
4009 };
4010 Step currentStep = Step::Start;
4011
4012 auto threadFunc = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context, uint32_t index) {
4013 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
4014 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4015
4016 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4017
4018 // Make sure the two threads start work around the same time
4019 if (index == 0)
4020 {
4021 threadSynchronization.nextStep(Step::Thread0Ready);
4022 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
4023 }
4024 else
4025 {
4026 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
4027 threadSynchronization.nextStep(Step::Thread1Ready);
4028 }
4029
4030 std::vector<GLuint> buffers(kSurfaceWidth * kSurfaceHeight);
4031
4032 glEnable(GL_SCISSOR_TEST);
4033 for (int y = 0; y < kSurfaceHeight; ++y)
4034 {
4035 for (int x = 0; x < kSurfaceWidth; ++x)
4036 {
4037 GLuint &buffer = buffers[y * kSurfaceWidth + x];
4038 const float bufferData[4] = {
4039 ((y * kSurfaceWidth + x + index * 100) % 255) / 255.0f,
4040 ((y * kSurfaceWidth + x + index * 100 + 1) % 255) / 255.0f,
4041 ((y * kSurfaceWidth + x + index * 100 + 2) % 255) / 255.0f,
4042 ((y * kSurfaceWidth + x + index * 100 + 3) % 255) / 255.0f,
4043 };
4044
4045 // Generate one buffer per pixel and shade the pixel with it.
4046 glGenBuffers(1, &buffer);
4047 glBindBuffer(GL_UNIFORM_BUFFER, buffers[y * kSurfaceWidth + x]);
4048 glBufferData(GL_UNIFORM_BUFFER, sizeof(bufferData), bufferData, GL_STATIC_DRAW);
4049 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
4050
4051 glScissor(x, y, 1, 1);
4052 drawQuad(program, essl3_shaders::PositionAttrib(), 0);
4053
4054 if ((x + y) % 2 == 0)
4055 {
4056 glDeleteBuffers(1, &buffer);
4057 buffer = 0;
4058 }
4059 }
4060 }
4061
4062 // Verify the results
4063 auto verify = [&](int x, int y) {
4064 const GLColor expect((y * kSurfaceWidth + x + index * 100) % 255,
4065 (y * kSurfaceWidth + x + index * 100 + 1) % 255,
4066 (y * kSurfaceWidth + x + index * 100 + 2) % 255,
4067 (y * kSurfaceWidth + x + index * 100 + 3) % 255);
4068 EXPECT_PIXEL_COLOR_EQ(x, y, expect);
4069 };
4070
4071 verify(0, 0);
4072 verify(0, kSurfaceHeight - 1);
4073 verify(kSurfaceWidth - 1, 0);
4074 verify(kSurfaceWidth - 1, kSurfaceHeight - 1);
4075 verify(kSurfaceWidth / 2, kSurfaceHeight / 2);
4076 ASSERT_GL_NO_ERROR();
4077
4078 if (index == 0)
4079 {
4080 threadSynchronization.nextStep(Step::Finish);
4081 }
4082 else
4083 {
4084 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
4085 }
4086 };
4087
4088 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4089 threadFunc(dpy, surface, context, 0);
4090 };
4091 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4092 threadFunc(dpy, surface, context, 1);
4093 };
4094
4095 std::array<LockStepThreadFunc, 2> threadFuncs = {
4096 std::move(thread0),
4097 std::move(thread1),
4098 };
4099
4100 RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
4101 threadFuncs.data());
4102
4103 ASSERT_NE(currentStep, Step::Abort);
4104 }
4105
4106 // Test that ref counting is thread-safe when the same buffer is used in multiple threads.
TEST_P(MultithreadingTestES3,SimultaneousBufferBind)4107 TEST_P(MultithreadingTestES3, SimultaneousBufferBind)
4108 {
4109 ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());
4110
4111 constexpr char kFS[] = R"(#version 300 es
4112 precision mediump float;
4113
4114 layout(std140) uniform Block
4115 {
4116 vec4 colorIn;
4117 };
4118
4119 out vec4 color;
4120
4121 void main()
4122 {
4123 color = colorIn;
4124 })";
4125
4126 constexpr int kSurfaceWidth = 32;
4127 constexpr int kSurfaceHeight = 128;
4128
4129 GLuint buffer;
4130 GLsync sync = nullptr;
4131
4132 // Sync primitives
4133 std::mutex mutex;
4134 std::condition_variable condVar;
4135
4136 enum class Step
4137 {
4138 Start,
4139 Thread0Ready,
4140 Thread1Ready,
4141 Finish,
4142 Abort,
4143 };
4144 Step currentStep = Step::Start;
4145
4146 auto thread0 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4147 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
4148 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4149
4150 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4151
4152 // Create the buffer in this context
4153 glGenBuffers(1, &buffer);
4154
4155 constexpr float kBufferData[4] = {
4156 10.0f / 255.0f,
4157 50.0f / 255.0f,
4158 130.0f / 255.0f,
4159 220.0f / 255.0f,
4160 };
4161 glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4162 glBufferData(GL_UNIFORM_BUFFER, sizeof(kBufferData), kBufferData, GL_STATIC_DRAW);
4163
4164 sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
4165
4166 // Make sure the two threads start work around the same time
4167 threadSynchronization.nextStep(Step::Thread0Ready);
4168 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Ready));
4169
4170 // Bind and unbind the buffer many times. If ref counting is not thread safe, chances are
4171 // the ref count would be incorrect in the end. This can result in the buffer prematurely
4172 // getting deleted.
4173 for (uint32_t i = 0; i < 8000; ++i)
4174 {
4175 glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? 0 : buffer);
4176 }
4177 ASSERT_GL_NO_ERROR();
4178
4179 threadSynchronization.nextStep(Step::Finish);
4180 };
4181 auto thread1 = [&](EGLDisplay dpy, EGLSurface surface, EGLContext context) {
4182 ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);
4183 EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface, surface, context));
4184
4185 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
4186
4187 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Ready));
4188 threadSynchronization.nextStep(Step::Thread1Ready);
4189
4190 glWaitSync(sync, 0, GL_TIMEOUT_IGNORED);
4191
4192 // Bind and unbind the buffer many times.
4193 for (uint32_t i = 0; i < 4000; ++i)
4194 {
4195 glBindBuffer(GL_UNIFORM_BUFFER, i % 2 == 0 ? buffer : 0);
4196 }
4197
4198 // Draw with it to make sure buffer is still valid and not accidentally deleted due to bad
4199 // ref counting.
4200 glBindBuffer(GL_UNIFORM_BUFFER, buffer);
4201 glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffer);
4202 drawQuad(program, essl3_shaders::PositionAttrib(), 0);
4203
4204 // Verify the results
4205 const GLColor expect(10, 50, 130, 220);
4206 EXPECT_PIXEL_RECT_EQ(0, 0, kSurfaceWidth, kSurfaceHeight, expect);
4207 ASSERT_GL_NO_ERROR();
4208
4209 ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));
4210 };
4211
4212 std::array<LockStepThreadFunc, 2> threadFuncs = {
4213 std::move(thread0),
4214 std::move(thread1),
4215 };
4216
4217 RunLockStepThreadsWithSize(getEGLWindow(), kSurfaceWidth, kSurfaceHeight, threadFuncs.size(),
4218 threadFuncs.data());
4219
4220 ASSERT_NE(currentStep, Step::Abort);
4221 }
4222 ANGLE_INSTANTIATE_TEST(
4223 MultithreadingTest,
4224 ES2_METAL(),
4225 ES3_METAL(),
4226 ES2_OPENGL(),
4227 ES3_OPENGL(),
4228 ES2_OPENGLES(),
4229 ES3_OPENGLES(),
4230 ES3_VULKAN(),
4231 ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4232 ES3_VULKAN_SWIFTSHADER()
4233 .enable(Feature::AsyncCommandQueue)
4234 .enable(Feature::SlowAsyncCommandQueueForTesting),
4235 ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4236 ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4237 ES3_VULKAN_SWIFTSHADER()
4238 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4239 .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4240 ES3_VULKAN_SWIFTSHADER()
4241 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4242 .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4243 ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4244 ES3_VULKAN_SWIFTSHADER()
4245 .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4246 .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4247 ES3_VULKAN_SWIFTSHADER()
4248 .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4249 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4250 .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4251 ES2_D3D11(),
4252 ES3_D3D11());
4253
4254 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MultithreadingTestES3);
4255 ANGLE_INSTANTIATE_TEST(
4256 MultithreadingTestES3,
4257 ES3_OPENGL(),
4258 ES3_OPENGLES(),
4259 ES3_VULKAN(),
4260 ES3_VULKAN_SWIFTSHADER().enable(Feature::AsyncCommandQueue),
4261 ES3_VULKAN_SWIFTSHADER()
4262 .enable(Feature::AsyncCommandQueue)
4263 .enable(Feature::SlowAsyncCommandQueueForTesting),
4264 ES3_VULKAN_SWIFTSHADER().disable(Feature::PreferMonolithicPipelinesOverLibraries),
4265 ES3_VULKAN_SWIFTSHADER().enable(Feature::PreferMonolithicPipelinesOverLibraries),
4266 ES3_VULKAN_SWIFTSHADER()
4267 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4268 .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4269 ES3_VULKAN_SWIFTSHADER()
4270 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4271 .disable(Feature::MergeProgramPipelineCachesToGlobalCache),
4272 ES3_VULKAN_SWIFTSHADER().enable(Feature::PermanentlySwitchToFramebufferFetchMode),
4273 ES3_VULKAN_SWIFTSHADER()
4274 .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4275 .enable(Feature::PreferMonolithicPipelinesOverLibraries),
4276 ES3_VULKAN_SWIFTSHADER()
4277 .enable(Feature::PermanentlySwitchToFramebufferFetchMode)
4278 .enable(Feature::PreferMonolithicPipelinesOverLibraries)
4279 .enable(Feature::SlowDownMonolithicPipelineCreationForTesting),
4280 ES3_D3D11());
4281
4282 } // namespace angle
4283