Edit

kc3-lang/angle/src/libGLESv2/egl_stubs.cpp

Branch :

  • Show log

    Commit

  • Author : Tim Van Patten
    Date : 2021-07-22 19:06:40
    Hash : 8bb3c827
    Message : Fix Multithreaded eglDestroyContext()/eglTerminate() The following EGL calls can lead to a crash in eglMakeCurrent(): Thread A: eglMakeCurrent(context A) Thread B: eglDestroyContext(context A) B: eglTerminate() <<--- this release context A Thread A: eglMakeCurrent(context B) The eglMakeCurrent(context B) call will assert when attempting to unMakeCurrent(), since thread A doesn't know that context A was already destroyed by thread B. To fix this: 1.) A Context will only be released once there are no Threads that currently have a reference to it (no longer have the Context current). - Context::mIsCurrent is being removed, since it was inaccurate and not thread-safe. For example, when eglTerminate() was called, the eglTerminate()'ing-Thread would "steal" the Context that was current on another Thread to destroy it. 2.) A Display will only be fully terminated and its resources released once all Contexts have been destroyed and are no longer current. Otherwise, Display::terminate() will return if any Contexts are still in use by a Thread. EGL 1.5 Specification 3.2 Initialization If contexts or surfaces, created with respect to dpy are current (see section 3.7.3) to any thread, then they are not actually destroyed while they remain current. If other resources created with respect to dpy are in use by any current context or surface, then they are also not destroyed until the corresponding context or surface is no longer current. With this fix, the app com.netmarble.sknightsmmo can start. This also exposed an issue with GlslangFinalize(), since glslang can only be initialized/finalized once per process. Otherwise, the following EGL commands will call GlslangFinalize() without ever being able to GlslangInitialize() again, leading to crashes since GlslangFinalize() cleans up glslang for the entire process. dpy1 = eglGetPlatformDisplay() | eglInitialize(dpy1) | GlslangInitialize() dpy2 = eglGetPlatformDisplay() | eglInitialize(dpy2) | GlslangInitialize() eglTerminate(dpy2) | GlslangFinalize() eglInitialize(dpy1) | isInitialized() == true Since Display::isInitialized() == true, the rest of Display::initialize() is skipped and GlslangInitialize() is not called. Later, the next test that attempts to compile a program will crash due to glslang no longer being initialized. Finally, this exposed the following tests leaking EGLContext handles: - EGLSurfaceTest::initializeContext() - EGLContextSharingTest.DisplayShareGroupContextCreation - EGLCreateContextAttribsTest.IMGContextPriorityExtension - EGLMultiContextTest.TestContextDestroySimple Other tests were failing to reset the context, preventing the Display from being terminated since there were still references to Contexts owned by the display: eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); Bug: angleproject:6208 Bug: angleproject:6304 Bug: angleproject:6322 Test: EGLContextSharingTest.EglTerminateMultiThreaded Test: EGLContextSharingTestNoFixture.EglDestoryContextManyTimesSameContext Test: Load com.netmarble.sknightsmmo Change-Id: I160922af93db6cabe0ed396be77762fa8dfc7656 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3046961 Reviewed-by: Geoff Lang <geofflang@chromium.org> Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Tim Van Patten <timvp@google.com>

  • src/libGLESv2/egl_stubs.cpp
  • //
    // Copyright 2020 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    // egl_stubs.cpp: Stubs for EGL entry points.
    //
    
    #include "libGLESv2/egl_stubs_autogen.h"
    
    #include "common/angle_version.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Display.h"
    #include "libANGLE/EGLSync.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Thread.h"
    #include "libANGLE/queryutils.h"
    #include "libANGLE/validationEGL.h"
    #include "libGLESv2/global_state.h"
    #include "libGLESv2/proc_table_egl.h"
    
    namespace egl
    {
    namespace
    {
    
    bool CompareProc(const ProcEntry &a, const char *b)
    {
        return strcmp(a.first, b) < 0;
    }
    
    void ClipConfigs(const std::vector<const Config *> &filteredConfigs,
                     EGLConfig *outputConfigs,
                     EGLint configSize,
                     EGLint *numConfigs)
    {
        EGLint resultSize = static_cast<EGLint>(filteredConfigs.size());
        if (outputConfigs)
        {
            resultSize = std::max(std::min(resultSize, configSize), 0);
            for (EGLint i = 0; i < resultSize; i++)
            {
                outputConfigs[i] = const_cast<Config *>(filteredConfigs[i]);
            }
        }
        *numConfigs = resultSize;
    }
    }  // anonymous namespace
    
    EGLBoolean BindAPI(Thread *thread, EGLenum api)
    {
        thread->setAPI(api);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean BindTexImage(Thread *thread, Display *display, Surface *eglSurface, EGLint buffer)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglBindTexImage",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        gl::Context *context = thread->getContext();
        if (context)
        {
            gl::TextureType type =
                egl_gl::EGLTextureTargetToTextureType(eglSurface->getTextureTarget());
            gl::Texture *textureObject = context->getTextureByType(type);
            ANGLE_EGL_TRY_RETURN(thread, eglSurface->bindTexImage(context, textureObject, buffer),
                                 "eglBindTexImage", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean ChooseConfig(Thread *thread,
                            Display *display,
                            const AttributeMap &attribMap,
                            EGLConfig *configs,
                            EGLint config_size,
                            EGLint *num_config)
    {
        ClipConfigs(display->chooseConfig(attribMap), configs, config_size, num_config);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLint ClientWaitSync(Thread *thread,
                          Display *display,
                          Sync *syncObject,
                          EGLint flags,
                          EGLTime timeout)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglClientWaitSync",
                             GetDisplayIfValid(display), EGL_FALSE);
        gl::Context *currentContext = thread->getContext();
        EGLint syncStatus           = EGL_FALSE;
        ANGLE_EGL_TRY_RETURN(
            thread, syncObject->clientWait(display, currentContext, flags, timeout, &syncStatus),
            "eglClientWaitSync", GetSyncIfValid(display, syncObject), EGL_FALSE);
    
        thread->setSuccess();
        return syncStatus;
    }
    
    EGLBoolean CopyBuffers(Thread *thread,
                           Display *display,
                           Surface *eglSurface,
                           EGLNativePixmapType target)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCopyBuffers",
                             GetDisplayIfValid(display), EGL_FALSE);
        UNIMPLEMENTED();  // FIXME
    
        thread->setSuccess();
        return 0;
    }
    
    EGLContext CreateContext(Thread *thread,
                             Display *display,
                             Config *configuration,
                             gl::Context *sharedGLContext,
                             const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateContext",
                             GetDisplayIfValid(display), EGL_NO_CONTEXT);
        gl::Context *context = nullptr;
        ANGLE_EGL_TRY_RETURN(thread,
                             display->createContext(configuration, sharedGLContext, thread->getAPI(),
                                                    attributes, &context),
                             "eglCreateContext", GetDisplayIfValid(display), EGL_NO_CONTEXT);
    
        thread->setSuccess();
        return static_cast<EGLContext>(context);
    }
    
    EGLImage CreateImage(Thread *thread,
                         Display *display,
                         gl::Context *context,
                         EGLenum target,
                         EGLClientBuffer buffer,
                         const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateImage",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        Image *image = nullptr;
        Error error  = display->createImage(context, target, buffer, attributes, &image);
        if (error.isError())
        {
            thread->setError(error, "eglCreateImage", GetDisplayIfValid(display));
            return EGL_NO_IMAGE;
        }
    
        thread->setSuccess();
        return static_cast<EGLImage>(image);
    }
    
    EGLSurface CreatePbufferFromClientBuffer(Thread *thread,
                                             Display *display,
                                             EGLenum buftype,
                                             EGLClientBuffer buffer,
                                             Config *configuration,
                                             const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePbufferFromClientBuffer",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
        Surface *surface = nullptr;
        ANGLE_EGL_TRY_RETURN(thread,
                             display->createPbufferFromClientBuffer(configuration, buftype, buffer,
                                                                    attributes, &surface),
                             "eglCreatePbufferFromClientBuffer", GetDisplayIfValid(display),
                             EGL_NO_SURFACE);
    
        return static_cast<EGLSurface>(surface);
    }
    
    EGLSurface CreatePbufferSurface(Thread *thread,
                                    Display *display,
                                    Config *configuration,
                                    const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePbufferSurface",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
        Surface *surface = nullptr;
        ANGLE_EGL_TRY_RETURN(thread, display->createPbufferSurface(configuration, attributes, &surface),
                             "eglCreatePbufferSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        return static_cast<EGLSurface>(surface);
    }
    
    EGLSurface CreatePixmapSurface(Thread *thread,
                                   Display *display,
                                   Config *configuration,
                                   EGLNativePixmapType pixmap,
                                   const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePixmapSurface",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
        Surface *surface = nullptr;
        ANGLE_EGL_TRY_RETURN(thread,
                             display->createPixmapSurface(configuration, pixmap, attributes, &surface),
                             "eglCreatePixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        thread->setSuccess();
        return static_cast<EGLSurface>(surface);
    }
    
    EGLSurface CreatePlatformPixmapSurface(Thread *thread,
                                           Display *display,
                                           Config *configuration,
                                           void *pixmap,
                                           const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePlatformPixmapSurface",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
        Surface *surface                 = nullptr;
        EGLNativePixmapType nativePixmap = reinterpret_cast<EGLNativePixmapType>(pixmap);
        ANGLE_EGL_TRY_RETURN(
            thread, display->createPixmapSurface(configuration, nativePixmap, attributes, &surface),
            "eglCreatePlatformPixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        thread->setSuccess();
        return static_cast<EGLSurface>(surface);
    }
    
    EGLSurface CreatePlatformWindowSurface(Thread *thread,
                                           Display *display,
                                           Config *configuration,
                                           void *win,
                                           const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreatePlatformWindowSurface",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
        Surface *surface                 = nullptr;
        EGLNativeWindowType nativeWindow = reinterpret_cast<EGLNativeWindowType>(win);
        ANGLE_EGL_TRY_RETURN(
            thread, display->createWindowSurface(configuration, nativeWindow, attributes, &surface),
            "eglPlatformCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        return static_cast<EGLSurface>(surface);
    }
    
    EGLSync CreateSync(Thread *thread, Display *display, EGLenum type, const AttributeMap &attributes)
    {
        gl::Context *currentContext = thread->getContext();
    
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateSync",
                             GetDisplayIfValid(display), EGL_FALSE);
        Sync *syncObject = nullptr;
        ANGLE_EGL_TRY_RETURN(thread, display->createSync(currentContext, type, attributes, &syncObject),
                             "eglCreateSync", GetDisplayIfValid(display), EGL_NO_SYNC);
    
        thread->setSuccess();
        return static_cast<EGLSync>(syncObject);
    }
    
    EGLSurface CreateWindowSurface(Thread *thread,
                                   Display *display,
                                   Config *configuration,
                                   EGLNativeWindowType win,
                                   const AttributeMap &attributes)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglCreateWindowSurface",
                             GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        Surface *surface = nullptr;
        ANGLE_EGL_TRY_RETURN(thread,
                             display->createWindowSurface(configuration, win, attributes, &surface),
                             "eglCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        return static_cast<EGLSurface>(surface);
    }
    
    EGLBoolean DestroyContext(Thread *thread, Display *display, gl::Context *context)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroyContext",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread);
    
        ANGLE_EGL_TRY_RETURN(thread, display->destroyContext(thread, context), "eglDestroyContext",
                             GetContextIfValid(display, context), EGL_FALSE);
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean DestroyImage(Thread *thread, Display *display, Image *img)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroyImage",
                             GetDisplayIfValid(display), EGL_FALSE);
        display->destroyImage(img);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean DestroySurface(Thread *thread, Display *display, Surface *eglSurface)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroySurface",
                             GetDisplayIfValid(display), EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, display->destroySurface(eglSurface), "eglDestroySurface",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean DestroySync(Thread *thread, Display *display, Sync *syncObject)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglDestroySync",
                             GetDisplayIfValid(display), EGL_FALSE);
        display->destroySync(syncObject);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean GetConfigAttrib(Thread *thread,
                               Display *display,
                               Config *configuration,
                               EGLint attribute,
                               EGLint *value)
    {
        QueryConfigAttrib(configuration, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean GetConfigs(Thread *thread,
                          Display *display,
                          EGLConfig *configs,
                          EGLint config_size,
                          EGLint *num_config)
    {
        ClipConfigs(display->getConfigs(AttributeMap()), configs, config_size, num_config);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLContext GetCurrentContext(Thread *thread)
    {
        gl::Context *context = thread->getContext();
    
        thread->setSuccess();
        return static_cast<EGLContext>(context);
    }
    
    EGLDisplay GetCurrentDisplay(Thread *thread)
    {
        thread->setSuccess();
        if (thread->getContext() != nullptr)
        {
            return thread->getContext()->getDisplay();
        }
        return EGL_NO_DISPLAY;
    }
    
    EGLSurface GetCurrentSurface(Thread *thread, EGLint readdraw)
    {
        if (readdraw == EGL_READ)
        {
            thread->setSuccess();
            return thread->getCurrentReadSurface();
        }
        else if (readdraw == EGL_DRAW)
        {
            thread->setSuccess();
            return thread->getCurrentDrawSurface();
        }
        else
        {
            thread->setError(EglBadParameter(), "eglGetCurrentSurface", nullptr);
            return EGL_NO_SURFACE;
        }
    }
    
    EGLDisplay GetDisplay(Thread *thread, EGLNativeDisplayType display_id)
    {
        return Display::GetDisplayFromNativeDisplay(display_id, AttributeMap());
    }
    
    EGLint GetError(Thread *thread)
    {
        EGLint error = thread->getError();
        thread->setSuccess();
        return error;
    }
    
    EGLDisplay GetPlatformDisplay(Thread *thread,
                                  EGLenum platform,
                                  void *native_display,
                                  const AttributeMap &attribMap)
    {
        if (platform == EGL_PLATFORM_ANGLE_ANGLE)
        {
            return Display::GetDisplayFromNativeDisplay(
                gl::bitCast<EGLNativeDisplayType>(native_display), attribMap);
        }
        else if (platform == EGL_PLATFORM_DEVICE_EXT)
        {
            Device *eglDevice = static_cast<Device *>(native_display);
            return Display::GetDisplayFromDevice(eglDevice, attribMap);
        }
        else
        {
            UNREACHABLE();
            return EGL_NO_DISPLAY;
        }
    }
    
    __eglMustCastToProperFunctionPointerType GetProcAddress(Thread *thread, const char *procname)
    {
        const ProcEntry *entry =
            std::lower_bound(&g_procTable[0], &g_procTable[g_numProcs], procname, CompareProc);
    
        thread->setSuccess();
    
        if (entry == &g_procTable[g_numProcs] || strcmp(entry->first, procname) != 0)
        {
            return nullptr;
        }
    
        return entry->second;
    }
    
    EGLBoolean GetSyncAttrib(Thread *thread,
                             Display *display,
                             Sync *syncObject,
                             EGLint attribute,
                             EGLAttrib *value)
    {
        EGLint valueExt;
        ANGLE_EGL_TRY_RETURN(thread, GetSyncAttrib(display, syncObject, attribute, &valueExt),
                             "eglGetSyncAttrib", GetSyncIfValid(display, syncObject), EGL_FALSE);
        *value = valueExt;
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean Initialize(Thread *thread, Display *display, EGLint *major, EGLint *minor)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->initialize(), "eglInitialize", GetDisplayIfValid(display),
                             EGL_FALSE);
    
        if (major)
            *major = 1;
        if (minor)
            *minor = 5;
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean MakeCurrent(Thread *thread,
                           Display *display,
                           Surface *drawSurface,
                           Surface *readSurface,
                           gl::Context *context)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglMakeCurrent",
                             GetDisplayIfValid(display), EGL_FALSE);
        ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread);
    
        Surface *previousDraw        = thread->getCurrentDrawSurface();
        Surface *previousRead        = thread->getCurrentReadSurface();
        gl::Context *previousContext = thread->getContext();
    
        // Only call makeCurrent if the context or surfaces have changed.
        if (previousDraw != drawSurface || previousRead != readSurface || previousContext != context)
        {
            ANGLE_EGL_TRY_RETURN(
                thread,
                display->makeCurrent(thread, previousContext, drawSurface, readSurface, context),
                "eglMakeCurrent", GetContextIfValid(display, context), EGL_FALSE);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLenum QueryAPI(Thread *thread)
    {
        EGLenum API = thread->getAPI();
    
        thread->setSuccess();
        return API;
    }
    
    EGLBoolean QueryContext(Thread *thread,
                            Display *display,
                            gl::Context *context,
                            EGLint attribute,
                            EGLint *value)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQueryContext",
                             GetDisplayIfValid(display), EGL_FALSE);
        QueryContextAttrib(context, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    const char *QueryString(Thread *thread, Display *display, EGLint name)
    {
        if (display)
        {
            ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQueryString",
                                 GetDisplayIfValid(display), nullptr);
        }
    
        const char *result = nullptr;
        switch (name)
        {
            case EGL_CLIENT_APIS:
                result = "OpenGL_ES";
                break;
            case EGL_EXTENSIONS:
                if (display == EGL_NO_DISPLAY)
                {
                    result = Display::GetClientExtensionString().c_str();
                }
                else
                {
                    result = display->getExtensionString().c_str();
                }
                break;
            case EGL_VENDOR:
                result = display->getVendorString().c_str();
                break;
            case EGL_VERSION:
                result = "1.5 (ANGLE " ANGLE_VERSION_STRING ")";
                break;
            default:
                UNREACHABLE();
                break;
        }
    
        thread->setSuccess();
        return result;
    }
    
    EGLBoolean QuerySurface(Thread *thread,
                            Display *display,
                            Surface *eglSurface,
                            EGLint attribute,
                            EGLint *value)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglQuerySurface",
                             GetDisplayIfValid(display), EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(
            thread, QuerySurfaceAttrib(display, thread->getContext(), eglSurface, attribute, value),
            "eglQuerySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean ReleaseTexImage(Thread *thread, Display *display, Surface *eglSurface, EGLint buffer)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglReleaseTexImage",
                             GetDisplayIfValid(display), EGL_FALSE);
        gl::Texture *texture = eglSurface->getBoundTexture();
    
        if (texture)
        {
            ANGLE_EGL_TRY_RETURN(thread, eglSurface->releaseTexImage(thread->getContext(), buffer),
                                 "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface),
                                 EGL_FALSE);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean ReleaseThread(Thread *thread)
    {
        ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread);
    
        Surface *previousDraw        = thread->getCurrentDrawSurface();
        Surface *previousRead        = thread->getCurrentReadSurface();
        gl::Context *previousContext = thread->getContext();
        Display *previousDisplay     = thread->getDisplay();
    
        if (previousDisplay != EGL_NO_DISPLAY)
        {
            ANGLE_EGL_TRY_RETURN(thread, previousDisplay->prepareForCall(), "eglReleaseThread",
                                 GetDisplayIfValid(previousDisplay), EGL_FALSE);
            // Only call makeCurrent if the context or surfaces have changed.
            if (previousDraw != EGL_NO_SURFACE || previousRead != EGL_NO_SURFACE ||
                previousContext != EGL_NO_CONTEXT)
            {
                ANGLE_EGL_TRY_RETURN(
                    thread,
                    previousDisplay->makeCurrent(thread, previousContext, nullptr, nullptr, nullptr),
                    "eglReleaseThread", nullptr, EGL_FALSE);
            }
            ANGLE_EGL_TRY_RETURN(thread, previousDisplay->releaseThread(), "eglReleaseThread",
                                 GetDisplayIfValid(previousDisplay), EGL_FALSE);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean SurfaceAttrib(Thread *thread,
                             Display *display,
                             Surface *eglSurface,
                             EGLint attribute,
                             EGLint value)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSurfaceAttrib",
                             GetDisplayIfValid(display), EGL_FALSE);
        SetSurfaceAttrib(eglSurface, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean SwapBuffers(Thread *thread, Display *display, Surface *eglSurface)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSwapBuffers",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->swap(thread->getContext()), "eglSwapBuffers",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean SwapInterval(Thread *thread, Display *display, EGLint interval)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglSwapInterval",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        Surface *drawSurface        = static_cast<Surface *>(thread->getCurrentDrawSurface());
        const Config *surfaceConfig = drawSurface->getConfig();
        EGLint clampedInterval      = std::min(std::max(interval, surfaceConfig->minSwapInterval),
                                          surfaceConfig->maxSwapInterval);
    
        drawSurface->setSwapInterval(clampedInterval);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean Terminate(Thread *thread, Display *display)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglTerminate",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        ScopedSyncCurrentContextFromThread scopedSyncCurrent(thread);
    
        ANGLE_EGL_TRY_RETURN(thread, display->terminate(thread), "eglTerminate",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean WaitClient(Thread *thread)
    {
        Display *display = thread->getDisplay();
        if (display == nullptr)
        {
            // EGL spec says this about eglWaitClient -
            //    If there is no current context for the current rendering API,
            //    the function has no effect but still returns EGL_TRUE.
            return EGL_TRUE;
        }
    
        gl::Context *context = thread->getContext();
    
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitClient",
                             GetDisplayIfValid(display), EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, display->waitClient(context), "eglWaitClient",
                             GetContextIfValid(display, context), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean WaitGL(Thread *thread)
    {
        Display *display = thread->getDisplay();
        if (display == nullptr)
        {
            // EGL spec says this about eglWaitGL -
            //    eglWaitGL is ignored if there is no current EGL rendering context for OpenGL ES.
            return EGL_TRUE;
        }
    
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitGL", GetDisplayIfValid(display),
                             EGL_FALSE);
    
        // eglWaitGL like calling eglWaitClient with the OpenGL ES API bound. Since we only implement
        // OpenGL ES we can do the call directly.
        ANGLE_EGL_TRY_RETURN(thread, display->waitClient(thread->getContext()), "eglWaitGL",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean WaitNative(Thread *thread, EGLint engine)
    {
        Display *display = thread->getDisplay();
        if (display == nullptr)
        {
            // EGL spec says this about eglWaitNative -
            //    eglWaitNative is ignored if there is no current EGL rendering context.
            return EGL_TRUE;
        }
    
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitNative",
                             GetDisplayIfValid(display), EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, display->waitNative(thread->getContext(), engine), "eglWaitNative",
                             GetThreadIfValid(thread), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean WaitSync(Thread *thread, Display *display, Sync *syncObject, EGLint flags)
    {
        ANGLE_EGL_TRY_RETURN(thread, display->prepareForCall(), "eglWaitSync",
                             GetDisplayIfValid(display), EGL_FALSE);
        gl::Context *currentContext = thread->getContext();
        ANGLE_EGL_TRY_RETURN(thread, syncObject->serverWait(display, currentContext, flags),
                             "eglWaitSync", GetSyncIfValid(display, syncObject), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    }  // namespace egl