Path: blob/main_old/src/tests/egl_tests/EGLContextSharingTest.cpp
1693 views
//1// Copyright 2016 The ANGLE Project Authors. All rights reserved.2// Use of this source code is governed by a BSD-style license that can be3// found in the LICENSE file.4//5// EGLContextSharingTest.cpp:6// Tests relating to shared Contexts.78#include <gtest/gtest.h>910#include "test_utils/ANGLETest.h"11#include "test_utils/MultiThreadSteps.h"12#include "test_utils/angle_test_configs.h"13#include "test_utils/gl_raii.h"14#include "util/EGLWindow.h"15#include "util/OSWindow.h"1617using namespace angle;1819namespace20{2122EGLBoolean SafeDestroyContext(EGLDisplay display, EGLContext &context)23{24EGLBoolean result = EGL_TRUE;25if (context != EGL_NO_CONTEXT)26{27result = eglDestroyContext(display, context);28context = EGL_NO_CONTEXT;29}30return result;31}3233class EGLContextSharingTest : public ANGLETest34{35public:36EGLContextSharingTest() : mContexts{EGL_NO_CONTEXT, EGL_NO_CONTEXT}, mTexture(0) {}3738void testTearDown() override39{40glDeleteTextures(1, &mTexture);4142EGLDisplay display = getEGLWindow()->getDisplay();4344if (display != EGL_NO_DISPLAY)45{46for (auto &context : mContexts)47{48SafeDestroyContext(display, context);49}50}5152// Set default test state to not give an error on shutdown.53getEGLWindow()->makeCurrent();54}5556EGLContext mContexts[2] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};57GLuint mTexture;58};5960class EGLContextSharingTestNoFixture : public EGLContextSharingTest61{62public:63EGLContextSharingTestNoFixture() : EGLContextSharingTest() {}6465void testSetUp() override66{67mOsWindow = OSWindow::New();68mMajorVersion = GetParam().majorVersion;69}7071void testTearDown() override72{73if (mDisplay != EGL_NO_DISPLAY)74{75eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);7677if (mSurface != EGL_NO_SURFACE)78{79eglDestroySurface(mDisplay, mSurface);80ASSERT_EGL_SUCCESS();81mSurface = EGL_NO_SURFACE;82}8384for (auto &context : mContexts)85{86SafeDestroyContext(mDisplay, context);87}8889eglTerminate(mDisplay);90mDisplay = EGL_NO_DISPLAY;91ASSERT_EGL_SUCCESS();92eglReleaseThread();93ASSERT_EGL_SUCCESS();94}9596mOsWindow->destroy();97OSWindow::Delete(&mOsWindow);98ASSERT_EGL_SUCCESS() << "Error during test TearDown";99}100101bool chooseConfig(EGLConfig *config) const102{103bool result = false;104EGLint count = 0;105EGLint clientVersion = mMajorVersion == 3 ? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT;106EGLint attribs[] = {EGL_RED_SIZE,1078,108EGL_GREEN_SIZE,1098,110EGL_BLUE_SIZE,1118,112EGL_ALPHA_SIZE,1130,114EGL_RENDERABLE_TYPE,115clientVersion,116EGL_SURFACE_TYPE,117EGL_WINDOW_BIT,118EGL_NONE};119120result = eglChooseConfig(mDisplay, attribs, config, 1, &count);121EXPECT_EGL_TRUE(result && (count > 0));122return result;123}124125bool createContext(EGLConfig config, EGLContext *context)126{127bool result = false;128EGLint attribs[] = {EGL_CONTEXT_MAJOR_VERSION, mMajorVersion, EGL_NONE};129130*context = eglCreateContext(mDisplay, config, nullptr, attribs);131result = (*context != EGL_NO_CONTEXT);132EXPECT_TRUE(result);133return result;134}135136bool createWindowSurface(EGLConfig config, EGLNativeWindowType win, EGLSurface *surface)137{138bool result = false;139EGLint attribs[] = {EGL_NONE};140141*surface = eglCreateWindowSurface(mDisplay, config, win, attribs);142result = (*surface != EGL_NO_SURFACE);143EXPECT_TRUE(result);144return result;145}146147OSWindow *mOsWindow;148EGLDisplay mDisplay = EGL_NO_DISPLAY;149EGLSurface mSurface = EGL_NO_SURFACE;150const EGLint kWidth = 64;151const EGLint kHeight = 64;152EGLint mMajorVersion = 0;153};154155// Tests that creating resources works after freeing the share context.156TEST_P(EGLContextSharingTest, BindTextureAfterShareContextFree)157{158EGLDisplay display = getEGLWindow()->getDisplay();159EGLConfig config = getEGLWindow()->getConfig();160EGLSurface surface = getEGLWindow()->getSurface();161162const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION,163getEGLWindow()->getClientMajorVersion(), EGL_NONE};164165mContexts[0] = eglCreateContext(display, config, nullptr, contextAttribs);166ASSERT_EGL_SUCCESS();167ASSERT_TRUE(mContexts[0] != EGL_NO_CONTEXT);168mContexts[1] = eglCreateContext(display, config, mContexts[1], contextAttribs);169ASSERT_EGL_SUCCESS();170ASSERT_TRUE(mContexts[1] != EGL_NO_CONTEXT);171172ASSERT_EGL_TRUE(SafeDestroyContext(display, mContexts[0]));173ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));174ASSERT_EGL_SUCCESS();175176glGenTextures(1, &mTexture);177glBindTexture(GL_TEXTURE_2D, mTexture);178ASSERT_GL_NO_ERROR();179}180181// Tests the creation of contexts using EGL_ANGLE_display_texture_share_group182TEST_P(EGLContextSharingTest, DisplayShareGroupContextCreation)183{184EGLDisplay display = getEGLWindow()->getDisplay();185EGLConfig config = getEGLWindow()->getConfig();186187const EGLint inShareGroupContextAttribs[] = {188EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};189190// Test creating two contexts in the global share group191mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);192mContexts[1] = eglCreateContext(display, config, mContexts[1], inShareGroupContextAttribs);193194if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))195{196// Make sure an error is generated and early-exit197ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);198ASSERT_EQ(EGL_NO_CONTEXT, mContexts[0]);199return;200}201202ASSERT_EGL_SUCCESS();203204ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);205ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);206eglDestroyContext(display, mContexts[0]);207mContexts[0] = EGL_NO_CONTEXT;208209// Try creating a context that is not in the global share group but tries to share with a210// context that is211const EGLint notInShareGroupContextAttribs[] = {212EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_FALSE, EGL_NONE};213mContexts[0] = eglCreateContext(display, config, mContexts[1], notInShareGroupContextAttribs);214ASSERT_EGL_ERROR(EGL_BAD_ATTRIBUTE);215ASSERT_TRUE(mContexts[0] == EGL_NO_CONTEXT);216}217218// Tests the sharing of textures using EGL_ANGLE_display_texture_share_group219TEST_P(EGLContextSharingTest, DisplayShareGroupObjectSharing)220{221EGLDisplay display = getEGLWindow()->getDisplay();222if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))223{224std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."225<< std::endl;226return;227}228229EGLConfig config = getEGLWindow()->getConfig();230EGLSurface surface = getEGLWindow()->getSurface();231232const EGLint inShareGroupContextAttribs[] = {233EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};234235// Create two contexts in the global share group but not in the same context share group236mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);237mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);238239ASSERT_EGL_SUCCESS();240241ASSERT_NE(EGL_NO_CONTEXT, mContexts[0]);242ASSERT_NE(EGL_NO_CONTEXT, mContexts[1]);243244ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));245ASSERT_EGL_SUCCESS();246247// Create a texture and buffer in ctx 0248GLTexture textureFromCtx0;249glBindTexture(GL_TEXTURE_2D, textureFromCtx0);250glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);251glBindTexture(GL_TEXTURE_2D, 0);252ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));253254GLBuffer bufferFromCtx0;255glBindBuffer(GL_ARRAY_BUFFER, bufferFromCtx0);256glBufferData(GL_ARRAY_BUFFER, 1, nullptr, GL_STATIC_DRAW);257glBindBuffer(GL_ARRAY_BUFFER, 0);258ASSERT_GL_TRUE(glIsBuffer(bufferFromCtx0));259260ASSERT_GL_NO_ERROR();261262// Switch to context 1 and verify that the texture is accessible but the buffer is not263ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));264ASSERT_EGL_SUCCESS();265266ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));267268ASSERT_GL_FALSE(glIsBuffer(bufferFromCtx0));269ASSERT_GL_NO_ERROR();270271// Call readpixels on the texture to verify that the backend has proper support272GLFramebuffer fbo;273glBindFramebuffer(GL_FRAMEBUFFER, fbo);274glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureFromCtx0, 0);275276GLubyte pixel[4];277glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);278ASSERT_GL_NO_ERROR();279280// Switch back to context 0 and delete the buffer281ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));282ASSERT_EGL_SUCCESS();283}284285// Tests that shared textures using EGL_ANGLE_display_texture_share_group are released when the last286// context is destroyed287TEST_P(EGLContextSharingTest, DisplayShareGroupReleasedWithLastContext)288{289EGLDisplay display = getEGLWindow()->getDisplay();290if (!IsEGLDisplayExtensionEnabled(display, "EGL_ANGLE_display_texture_share_group"))291{292std::cout << "Test skipped because EGL_ANGLE_display_texture_share_group is not present."293<< std::endl;294return;295}296297EGLConfig config = getEGLWindow()->getConfig();298EGLSurface surface = getEGLWindow()->getSurface();299300const EGLint inShareGroupContextAttribs[] = {301EGL_CONTEXT_CLIENT_VERSION, 2, EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE, EGL_TRUE, EGL_NONE};302303// Create two contexts in the global share group but not in the same context share group304mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);305mContexts[1] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);306307// Create a texture and buffer in ctx 0308ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));309GLTexture textureFromCtx0;310glBindTexture(GL_TEXTURE_2D, textureFromCtx0);311glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);312glBindTexture(GL_TEXTURE_2D, 0);313ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));314315// Switch to context 1 and verify that the texture is accessible316ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[1]));317ASSERT_GL_TRUE(glIsTexture(textureFromCtx0));318319// Destroy both contexts, the texture should be cleaned up automatically320ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[0]));321mContexts[0] = EGL_NO_CONTEXT;322ASSERT_EGL_TRUE(eglDestroyContext(display, mContexts[1]));323mContexts[1] = EGL_NO_CONTEXT;324325// Unmake current, so the context can be released.326ASSERT_EGL_TRUE(eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));327328// Create a new context and verify it cannot access the texture previously created329mContexts[0] = eglCreateContext(display, config, nullptr, inShareGroupContextAttribs);330ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));331332ASSERT_GL_FALSE(glIsTexture(textureFromCtx0));333}334335// Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused336// on the Vulkan back-end where we manage object lifetime manually.337TEST_P(EGLContextSharingTest, TextureLifetime)338{339EGLWindow *eglWindow = getEGLWindow();340EGLConfig config = getEGLWindow()->getConfig();341EGLDisplay display = getEGLWindow()->getDisplay();342343// Create a pbuffer surface for use with a shared context.344EGLSurface surface = eglWindow->getSurface();345EGLContext mainContext = eglWindow->getContext();346347// Initialize a shared context.348mContexts[0] = eglCreateContext(display, config, mainContext, nullptr);349ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);350351// Create a Texture on the shared context.352ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));353354constexpr GLsizei kTexSize = 2;355const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,356GLColor::yellow};357GLTexture tex;358glBindTexture(GL_TEXTURE_2D, tex);359glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,360kTexData);361glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);362glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);363364// Make the main Context current and draw with the texture.365ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));366367glBindTexture(GL_TEXTURE_2D, tex);368ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());369glUseProgram(program);370371// No uniform update because the update seems to hide the error on Vulkan.372373// Enqueue the draw call.374drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);375EXPECT_GL_NO_ERROR();376377// Delete the texture in the main context to orphan it.378// Do not read back the data to keep the commands in the graph.379tex.reset();380381// Bind and delete the test context. This should trigger texture garbage collection.382ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));383SafeDestroyContext(display, mContexts[0]);384385// Bind the main context to clean up the test.386ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));387}388389// Tests that deleting an object on one Context doesn't destroy it ahead-of-time. Mostly focused390// on the Vulkan back-end where we manage object lifetime manually.391TEST_P(EGLContextSharingTest, SamplerLifetime)392{393EGLWindow *eglWindow = getEGLWindow();394EGLConfig config = getEGLWindow()->getConfig();395EGLDisplay display = getEGLWindow()->getDisplay();396397ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);398ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(display, "EGL_KHR_create_context"));399400// Create a pbuffer surface for use with a shared context.401EGLSurface surface = eglWindow->getSurface();402EGLContext mainContext = eglWindow->getContext();403404std::vector<EGLint> contextAttributes;405contextAttributes.push_back(EGL_CONTEXT_MAJOR_VERSION_KHR);406contextAttributes.push_back(getClientMajorVersion());407contextAttributes.push_back(EGL_NONE);408409// Initialize a shared context.410mContexts[0] = eglCreateContext(display, config, mainContext, contextAttributes.data());411ASSERT_NE(mContexts[0], EGL_NO_CONTEXT);412413// Create a Texture on the shared context. Also create a Sampler object.414ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));415416constexpr GLsizei kTexSize = 2;417const GLColor kTexData[kTexSize * kTexSize] = {GLColor::red, GLColor::green, GLColor::blue,418GLColor::yellow};419GLTexture tex;420glBindTexture(GL_TEXTURE_2D, tex);421glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,422kTexData);423424GLSampler sampler;425glBindSampler(0, sampler);426glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_NEAREST);427glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST);428429// Make the main Context current and draw with the texture and sampler.430ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));431432glBindTexture(GL_TEXTURE_2D, tex);433glBindSampler(0, sampler);434ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());435glUseProgram(program);436437// No uniform update because the update seems to hide the error on Vulkan.438439// Enqueue the draw call.440drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);441EXPECT_GL_NO_ERROR();442443// Delete the texture and sampler in the main context to orphan them.444// Do not read back the data to keep the commands in the graph.445tex.reset();446sampler.reset();447448// Bind and delete the test context. This should trigger texture garbage collection.449ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mContexts[0]));450SafeDestroyContext(display, mContexts[0]);451452// Bind the main context to clean up the test.453ASSERT_EGL_TRUE(eglMakeCurrent(display, surface, surface, mainContext));454}455456// Test that deleting an object reading from a shared object in one context doesn't cause the other457// context to crash. Mostly focused on the Vulkan back-end where we track resource dependencies in458// a graph.459TEST_P(EGLContextSharingTest, DeleteReaderOfSharedTexture)460{461ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());462// GL Fences require GLES 3.0+463ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3);464465// Initialize contexts466EGLWindow *window = getEGLWindow();467EGLDisplay dpy = window->getDisplay();468EGLConfig config = window->getConfig();469470constexpr size_t kThreadCount = 2;471EGLSurface surface[kThreadCount] = {EGL_NO_SURFACE, EGL_NO_SURFACE};472EGLContext ctx[kThreadCount] = {EGL_NO_CONTEXT, EGL_NO_CONTEXT};473474EGLint pbufferAttributes[] = {EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE, EGL_NONE};475476for (size_t t = 0; t < kThreadCount; ++t)477{478surface[t] = eglCreatePbufferSurface(dpy, config, pbufferAttributes);479EXPECT_EGL_SUCCESS();480481ctx[t] = window->createContext(t == 0 ? EGL_NO_CONTEXT : ctx[0]);482EXPECT_NE(EGL_NO_CONTEXT, ctx[t]);483}484485// Initialize test resources. They are done outside the threads to reduce the sources of486// errors and thus deadlock-free teardown.487ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));488489// Shared texture to read from.490constexpr GLsizei kTexSize = 1;491const GLColor kTexData = GLColor::red;492493GLTexture sharedTex;494glBindTexture(GL_TEXTURE_2D, sharedTex);495glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,496&kTexData);497glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);498glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);499500// Resources for each context.501GLRenderbuffer renderbuffer[kThreadCount];502GLFramebuffer fbo[kThreadCount];503GLProgram program[kThreadCount];504505for (size_t t = 0; t < kThreadCount; ++t)506{507ASSERT_EGL_TRUE(eglMakeCurrent(dpy, surface[t], surface[t], ctx[t]));508509glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer[t]);510constexpr int kRenderbufferSize = 4;511glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kRenderbufferSize, kRenderbufferSize);512513glBindFramebuffer(GL_FRAMEBUFFER, fbo[t]);514glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,515renderbuffer[t]);516517glBindTexture(GL_TEXTURE_2D, sharedTex);518program[t].makeRaster(essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());519ASSERT_TRUE(program[t].valid());520}521522EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));523524// Synchronization tools to ensure the two threads are interleaved as designed by this test.525std::mutex mutex;526std::condition_variable condVar;527std::atomic<GLsync> deletingThreadSyncObj;528std::atomic<GLsync> continuingThreadSyncObj;529530enum class Step531{532Start,533Thread0Draw,534Thread1Draw,535Thread0Delete,536Finish,537Abort,538};539Step currentStep = Step::Start;540541std::thread deletingThread = std::thread([&]() {542ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);543544EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[0], surface[0], ctx[0]));545EXPECT_EGL_SUCCESS();546547ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));548549// Draw using the shared texture.550drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);551552deletingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);553ASSERT_GL_NO_ERROR();554// Force the fence to be created555glFlush();556557// Wait for the other thread to also draw using the shared texture.558threadSynchronization.nextStep(Step::Thread0Draw);559ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Draw));560561ASSERT_TRUE(continuingThreadSyncObj != nullptr);562glWaitSync(continuingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);563ASSERT_GL_NO_ERROR();564glDeleteSync(continuingThreadSyncObj);565ASSERT_GL_NO_ERROR();566continuingThreadSyncObj = nullptr;567568// Delete this thread's framebuffer (reader of the shared texture).569fbo[0].reset();570571// Wait for the other thread to use the shared texture again before unbinding the572// context (so no implicit flush happens).573threadSynchronization.nextStep(Step::Thread0Delete);574ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));575576EXPECT_GL_NO_ERROR();577EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));578EXPECT_EGL_SUCCESS();579});580581std::thread continuingThread = std::thread([&]() {582ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);583584EXPECT_EGL_TRUE(eglMakeCurrent(dpy, surface[1], surface[1], ctx[1]));585EXPECT_EGL_SUCCESS();586587// Wait for first thread to draw using the shared texture.588ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Draw));589590ASSERT_TRUE(deletingThreadSyncObj != nullptr);591glWaitSync(deletingThreadSyncObj, 0, GL_TIMEOUT_IGNORED);592ASSERT_GL_NO_ERROR();593glDeleteSync(deletingThreadSyncObj);594ASSERT_GL_NO_ERROR();595deletingThreadSyncObj = nullptr;596597// Draw using the shared texture.598drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);599600continuingThreadSyncObj = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);601ASSERT_GL_NO_ERROR();602// Force the fence to be created603glFlush();604605// Wait for the other thread to delete its framebuffer.606threadSynchronization.nextStep(Step::Thread1Draw);607ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Delete));608609// Write to the shared texture differently, so a dependency is created from the previous610// readers of the shared texture (the two framebuffers of the two threads) to the new611// commands being recorded for the shared texture.612//613// If the backend attempts to create a dependency from nodes associated with the614// previous readers of the texture to the new node that will contain the following615// commands, there will be a use-after-free error.616const GLColor kTexData2 = GLColor::green;617glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTexSize, kTexSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,618&kTexData2);619drawQuad(program[0].get(), essl1_shaders::PositionAttrib(), 0.5f);620621threadSynchronization.nextStep(Step::Finish);622623EXPECT_EGL_TRUE(eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));624EXPECT_EGL_SUCCESS();625});626627deletingThread.join();628continuingThread.join();629630ASSERT_NE(currentStep, Step::Abort);631632// Clean up633for (size_t t = 0; t < kThreadCount; ++t)634{635eglDestroySurface(dpy, surface[t]);636eglDestroyContext(dpy, ctx[t]);637}638}639640// Test that eglTerminate() with a thread doesn't cause other threads to crash.641TEST_P(EGLContextSharingTestNoFixture, EglTerminateMultiThreaded)642{643// http://anglebug.com/6208644// The following EGL calls led to a crash in eglMakeCurrent():645//646// Thread A: eglMakeCurrent(context A)647// Thread B: eglDestroyContext(context A)648// B: eglTerminate() <<--- this release context A649// Thread A: eglMakeCurrent(context B)650651EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};652mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,653reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);654EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);655EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));656657EGLConfig config = EGL_NO_CONFIG_KHR;658EXPECT_TRUE(chooseConfig(&config));659660mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);661EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));662ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";663664EXPECT_TRUE(createContext(config, &mContexts[0]));665EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));666667// Must be after the eglMakeCurrent() so renderer string is initialized.668ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());669// TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.670ANGLE_SKIP_TEST_IF(IsOpenGLES());671672// Synchronization tools to ensure the two threads are interleaved as designed by this test.673std::mutex mutex;674std::condition_variable condVar;675676enum class Step677{678Start,679Thread0Clear,680Thread1Terminate,681Thread0MakeCurrentContext1,682Finish,683Abort,684};685Step currentStep = Step::Start;686687std::thread thread0 = std::thread([&]() {688ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);689690ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));691692EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));693694// Clear and read back to make sure thread 0 uses context 0.695glClearColor(1.0, 0.0, 0.0, 1.0);696glClear(GL_COLOR_BUFFER_BIT);697EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);698699// Wait for thread 1 to clear.700threadSynchronization.nextStep(Step::Thread0Clear);701ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));702703// First Display was terminated, so we need to create a new one to create a new Context.704mDisplay = eglGetPlatformDisplayEXT(705EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);706EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);707EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));708config = EGL_NO_CONFIG_KHR;709EXPECT_TRUE(chooseConfig(&config));710EXPECT_TRUE(createContext(config, &mContexts[1]));711EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));712713// Clear and read back to make sure thread 0 uses context 1.714glClearColor(1.0, 1.0, 0.0, 1.0);715glClear(GL_COLOR_BUFFER_BIT);716EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);717718// Cleanup719EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));720EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));721eglDestroySurface(mDisplay, mSurface);722mSurface = EGL_NO_SURFACE;723eglTerminate(mDisplay);724mDisplay = EGL_NO_DISPLAY;725EXPECT_EGL_SUCCESS();726727threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);728ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));729});730731std::thread thread1 = std::thread([&]() {732ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);733734// Wait for thread 0 to clear.735ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));736737// Destroy context 0 while thread1 has it current.738EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));739EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[0]));740EXPECT_EGL_SUCCESS();741eglTerminate(mDisplay);742mDisplay = EGL_NO_DISPLAY;743EXPECT_EGL_SUCCESS();744745// Wait for the thread 0 to make a new context and glClear().746threadSynchronization.nextStep(Step::Thread1Terminate);747ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));748749threadSynchronization.nextStep(Step::Finish);750});751752thread0.join();753thread1.join();754755ASSERT_NE(currentStep, Step::Abort);756}757758// Test that eglDestoryContext() can be called multiple times on the same Context without causing759// errors.760TEST_P(EGLContextSharingTestNoFixture, EglDestoryContextManyTimesSameContext)761{762EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};763mDisplay = eglGetPlatformDisplayEXT(EGL_PLATFORM_ANGLE_ANGLE,764reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);765EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);766EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));767768EGLConfig config = EGL_NO_CONFIG_KHR;769EXPECT_TRUE(chooseConfig(&config));770771mOsWindow->initialize("EGLContextSharingTestNoFixture", kWidth, kHeight);772EXPECT_TRUE(createWindowSurface(config, mOsWindow->getNativeWindow(), &mSurface));773ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";774775EXPECT_TRUE(createContext(config, &mContexts[0]));776EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));777778// Must be after the eglMakeCurrent() so renderer string is initialized.779ANGLE_SKIP_TEST_IF(!platformSupportsMultithreading());780// TODO(http://www.anglebug.com/6304): Fails with OpenGL ES backend.781ANGLE_SKIP_TEST_IF(IsOpenGLES());782783// Synchronization tools to ensure the two threads are interleaved as designed by this test.784std::mutex mutex;785std::condition_variable condVar;786787enum class Step788{789Start,790Thread0Clear,791Thread1Terminate,792Thread0MakeCurrentContext1,793Finish,794Abort,795};796Step currentStep = Step::Start;797798std::thread thread0 = std::thread([&]() {799ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);800801ASSERT_TRUE(threadSynchronization.waitForStep(Step::Start));802803EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[0]));804805// Clear and read back to make sure thread 0 uses context 0.806glClearColor(1.0, 0.0, 0.0, 1.0);807glClear(GL_COLOR_BUFFER_BIT);808EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);809810// Wait for thread 1 to clear.811threadSynchronization.nextStep(Step::Thread0Clear);812ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread1Terminate));813814// First Display was terminated, so we need to create a new one to create a new Context.815mDisplay = eglGetPlatformDisplayEXT(816EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);817EXPECT_TRUE(mDisplay != EGL_NO_DISPLAY);818EXPECT_EGL_TRUE(eglInitialize(mDisplay, nullptr, nullptr));819config = EGL_NO_CONFIG_KHR;820EXPECT_TRUE(chooseConfig(&config));821EXPECT_TRUE(createContext(config, &mContexts[1]));822EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, mSurface, mSurface, mContexts[1]));823824// Clear and read back to make sure thread 0 uses context 1.825glClearColor(1.0, 1.0, 0.0, 1.0);826glClear(GL_COLOR_BUFFER_BIT);827EXPECT_PIXEL_EQ(0, 0, 255, 255, 0, 255);828829// Cleanup830EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));831EXPECT_TRUE(SafeDestroyContext(mDisplay, mContexts[1]));832eglDestroySurface(mDisplay, mSurface);833mSurface = EGL_NO_SURFACE;834eglTerminate(mDisplay);835mDisplay = EGL_NO_DISPLAY;836EXPECT_EGL_SUCCESS();837838threadSynchronization.nextStep(Step::Thread0MakeCurrentContext1);839ASSERT_TRUE(threadSynchronization.waitForStep(Step::Finish));840});841842std::thread thread1 = std::thread([&]() {843ThreadSynchronization<Step> threadSynchronization(¤tStep, &mutex, &condVar);844845// Wait for thread 0 to clear.846ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0Clear));847848EXPECT_EGL_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT));849850// Destroy context 0 5 times while thread1 has it current.851EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));852EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));853EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));854EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));855EXPECT_EGL_TRUE(eglDestroyContext(mDisplay, mContexts[0]));856mContexts[0] = EGL_NO_CONTEXT;857858eglTerminate(mDisplay);859mDisplay = EGL_NO_DISPLAY;860EXPECT_EGL_SUCCESS();861862// Wait for the thread 0 to make a new context and glClear().863threadSynchronization.nextStep(Step::Thread1Terminate);864ASSERT_TRUE(threadSynchronization.waitForStep(Step::Thread0MakeCurrentContext1));865866threadSynchronization.nextStep(Step::Finish);867});868869thread0.join();870thread1.join();871872ASSERT_NE(currentStep, Step::Abort);873}874} // anonymous namespace875876GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EGLContextSharingTest);877ANGLE_INSTANTIATE_TEST(EGLContextSharingTest,878ES2_D3D9(),879ES2_D3D11(),880ES3_D3D11(),881ES2_METAL(),882ES2_OPENGL(),883ES3_OPENGL(),884ES2_VULKAN(),885ES3_VULKAN());886887ANGLE_INSTANTIATE_TEST(EGLContextSharingTestNoFixture,888WithNoFixture(ES2_OPENGLES()),889WithNoFixture(ES3_OPENGLES()),890WithNoFixture(ES2_OPENGL()),891WithNoFixture(ES3_OPENGL()),892WithNoFixture(ES2_VULKAN()),893WithNoFixture(ES3_VULKAN()));894895