Edit

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

Branch :

  • Show log

    Commit

  • Author : Doug Horn
    Date : 2021-03-31 12:52:00
    Hash : fdba40fe
    Message : Reland "Fix multithreaded crash on draw commands on D3D11 backend." This is a reland of 8b9889bf62272fea2495331b622bde1f7c781bd0 The previous CL relied on a define which now only exists in a specific gn configuration. This reland removes D3D11 as a multithread-supported platform in the test configuration. Original change's description: > Fix multithreaded crash on draw commands on D3D11 backend. > > A crash can occur if thread A is executing eglDestroyContext while > thread B issues a draw call, if the threads are interleaved in such a > manner that a makeCurrent occurs without triggering a change to the > global context and a dirtyAllState call. We handle that case by > explicitly making current the proper contexts in the eglDestroyContext > call. > > A test has been added that triggers a crash without this fix when > running on the D3D11 backend. In addition, all of MultithreadingTest > is enabled for the D3D11 backend. > > Test: Ran MultithreadingTest. Test exhibits a crash before this > change, and does not after this change. Also ran: > dEQP-EGL.functional.sharing.gles2.multithread.* > dEQP-EGL.functional.multithread.* > > Bug: b/183756357 > Change-Id: Ic6f76a062868b2f3b4e60d29dc087ec180bfb7cd > Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2798591 > Reviewed-by: Geoff Lang <geofflang@chromium.org> > Reviewed-by: Jamie Madill <jmadill@chromium.org> > Commit-Queue: Doug Horn <doughorn@google.com> Bug: b/183756357 Change-Id: I5be9a011ea99a69730eddc9e4da23bcf92ed3bf2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2815243 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Doug Horn <doughorn@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/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);
    
        gl::Context *contextForThread = thread->getContext();
        bool contextWasCurrent        = context == contextForThread;
    
        bool shouldMakeCurrent =
            !contextWasCurrent && !context->isExternal() && context->getRefCount() <= 1;
    
        // Display can't access the current global context, but does exhibit a context switch,
        // so ensuring the current global context is correct needs to happen here.
        Surface *currentDrawSurface = thread->getCurrentDrawSurface();
        Surface *currentReadSurface = thread->getCurrentReadSurface();
    
        if (shouldMakeCurrent)
        {
            SetContextCurrent(thread, context);
        }
    
        ANGLE_EGL_TRY_RETURN(
            thread,
            display->destroyContextWithSurfaces(thread, context, contextForThread, currentDrawSurface,
                                                currentReadSurface),
            "eglDestroyContext", GetContextIfValid(display, context), EGL_FALSE);
    
        if (contextWasCurrent)
        {
            ANGLE_EGL_TRY_RETURN(thread, display->makeCurrent(context, nullptr, nullptr, nullptr),
                                 "eglDestroyContext", GetContextIfValid(display, context), EGL_FALSE);
            SetContextCurrent(thread, nullptr);
        }
        else if (shouldMakeCurrent)
        {
            SetContextCurrent(thread, contextForThread);
        }
    
        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);
        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(previousContext, drawSurface, readSurface, context),
                "eglMakeCurrent", GetContextIfValid(display, context), EGL_FALSE);
            SetContextCurrent(thread, context);
        }
    
        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)
    {
        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(previousContext, nullptr, nullptr, nullptr),
                    "eglReleaseThread", nullptr, EGL_FALSE);
            }
            ANGLE_EGL_TRY_RETURN(thread, previousDisplay->releaseThread(), "eglReleaseThread",
                                 GetDisplayIfValid(previousDisplay), EGL_FALSE);
            SetContextCurrent(thread, nullptr);
        }
    
        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);
        ANGLE_EGL_TRY_RETURN(thread,
                             display->makeCurrent(thread->getContext(), nullptr, nullptr, nullptr),
                             "eglTerminate", GetDisplayIfValid(display), EGL_FALSE);
        SetContextCurrent(thread, nullptr);
        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();
        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();
    
        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();
        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