Edit

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

Branch :

  • Show log

    Commit

  • Author : Ian Elliott
    Date : 2020-09-28 21:40:57
    Hash : f074d61a
    Message : Plumb EntryPoint & Context to DebugAnnotator/EVENT() This makes it easier to plumb debug labels to a future DebugAnnotatorVk class. Bug: b/162068318 Bug: b/169243237 Change-Id: I01e3779569c27c91252dc2874f6deaec526afd6f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2451516 Commit-Queue: Ian Elliott <ianelliott@google.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/libGLESv2/entry_points_egl.cpp
  • //
    // Copyright 2014 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.
    //
    
    // entry_points_egl.cpp : Implements the EGL entry points.
    
    #include "libGLESv2/entry_points_egl.h"
    
    #include "common/angle_version.h"
    #include "common/debug.h"
    #include "common/utilities.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Display.h"
    #include "libANGLE/EGLSync.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Texture.h"
    #include "libANGLE/Thread.h"
    #include "libANGLE/entry_points_utils.h"
    #include "libANGLE/queryutils.h"
    #include "libANGLE/validationEGL.h"
    #include "libGLESv2/global_state.h"
    #include "libGLESv2/proc_table_egl.h"
    
    using 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 *output_configs,
                     EGLint config_size,
                     EGLint *num_config)
    {
        EGLint result_size = static_cast<EGLint>(filteredConfigs.size());
        if (output_configs)
        {
            result_size = std::max(std::min(result_size, config_size), 0);
            for (EGLint i = 0; i < result_size; i++)
            {
                output_configs[i] = const_cast<Config *>(filteredConfigs[i]);
            }
        }
        *num_config = result_size;
    }
    }  // anonymous namespace
    
    extern "C" {
    // EGL 1.0
    EGLint EGLAPIENTRY EGL_GetError(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        EGLint error = thread->getError();
        thread->setSuccess();
        return error;
    }
    
    EGLDisplay EGLAPIENTRY EGL_GetDisplay(EGLNativeDisplayType display_id)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLNativeDisplayType display_id = 0x%016" PRIxPTR, (uintptr_t)display_id);
    
        return egl::Display::GetDisplayFromNativeDisplay(display_id, AttributeMap());
    }
    
    EGLBoolean EGLAPIENTRY EGL_Initialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint *major = 0x%016" PRIxPTR
                   ", EGLint *minor = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)major, (uintptr_t)minor);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        ANGLE_EGL_TRY_RETURN(thread, ValidateInitialize(display), "eglInitialize",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        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 EGLAPIENTRY EGL_Terminate(EGLDisplay dpy)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR, (uintptr_t)dpy);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        ANGLE_EGL_TRY_RETURN(thread, ValidateTerminate(display), "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;
    }
    
    const char *EGLAPIENTRY EGL_QueryString(EGLDisplay dpy, EGLint name)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint name = %d", (uintptr_t)dpy, name);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        if (!(display == EGL_NO_DISPLAY && name == EGL_EXTENSIONS))
        {
            ANGLE_EGL_TRY_RETURN(thread, ValidateDisplay(display), "eglQueryString",
                                 GetDisplayIfValid(display), nullptr);
        }
    
        const char *result;
        switch (name)
        {
            case EGL_CLIENT_APIS:
                result = "OpenGL_ES";
                break;
            case EGL_EXTENSIONS:
                if (display == EGL_NO_DISPLAY)
                {
                    result = egl::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:
                thread->setError(EglBadParameter(), GetDebug(), "eglQueryString",
                                 GetDisplayIfValid(display));
                return nullptr;
        }
    
        thread->setSuccess();
        return result;
    }
    
    EGLBoolean EGLAPIENTRY EGL_GetConfigs(EGLDisplay dpy,
                                          EGLConfig *configs,
                                          EGLint config_size,
                                          EGLint *num_config)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig *configs = 0x%016" PRIxPTR
                   ", "
                   "EGLint config_size = %d, EGLint *num_config = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)configs, config_size, (uintptr_t)num_config);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetConfigs(display, config_size, num_config),
                             "eglGetConfigs", GetDisplayIfValid(display), EGL_FALSE);
    
        ClipConfigs(display->getConfigs(AttributeMap()), configs, config_size, num_config);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_ChooseConfig(EGLDisplay dpy,
                                            const EGLint *attrib_list,
                                            EGLConfig *configs,
                                            EGLint config_size,
                                            EGLint *num_config)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", const EGLint *attrib_list = 0x%016" PRIxPTR
                   ", "
                   "EGLConfig *configs = 0x%016" PRIxPTR
                   ", EGLint config_size = %d, EGLint *num_config = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)attrib_list, (uintptr_t)configs, config_size,
                   (uintptr_t)num_config);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display  = static_cast<egl::Display *>(dpy);
        AttributeMap attribMap = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateChooseConfig(display, attribMap, config_size, num_config),
                             "eglChooseConfig", GetDisplayIfValid(display), EGL_FALSE);
    
        ClipConfigs(display->chooseConfig(attribMap), configs, config_size, num_config);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_GetConfigAttrib(EGLDisplay dpy,
                                               EGLConfig config,
                                               EGLint attribute,
                                               EGLint *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLint "
                   "*value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Config *configuration = static_cast<Config *>(config);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetConfigAttrib(display, configuration, attribute),
                             "eglGetConfigAttrib", GetDisplayIfValid(display), EGL_FALSE);
    
        QueryConfigAttrib(configuration, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLSurface EGLAPIENTRY EGL_CreateWindowSurface(EGLDisplay dpy,
                                                   EGLConfig config,
                                                   EGLNativeWindowType win,
                                                   const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", EGLNativeWindowType win = 0x%016" PRIxPTR
                   ", "
                   "const EGLint *attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, (uintptr_t)win, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Config *configuration   = static_cast<Config *>(config);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread,
                             ValidateCreateWindowSurface(display, configuration, win, attributes),
                             "eglCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::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);
    }
    
    EGLSurface EGLAPIENTRY EGL_CreatePbufferSurface(EGLDisplay dpy,
                                                    EGLConfig config,
                                                    const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", const EGLint *attrib_list = "
                   "0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Config *configuration   = static_cast<Config *>(config);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateCreatePbufferSurface(display, configuration, attributes),
                             "eglCreatePbufferSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::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 EGLAPIENTRY EGL_CreatePixmapSurface(EGLDisplay dpy,
                                                   EGLConfig config,
                                                   EGLNativePixmapType pixmap,
                                                   const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", EGLNativePixmapType pixmap = "
                   "0x%016" PRIxPTR
                   ", "
                   "const EGLint *attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, (uintptr_t)pixmap, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Config *configuration   = static_cast<Config *>(config);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread,
                             ValidateCreatePixmapSurface(display, configuration, pixmap, attributes),
                             "eglCreatePixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::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);
    }
    
    EGLBoolean EGLAPIENTRY EGL_DestroySurface(EGLDisplay dpy, EGLSurface surface)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDestroySurface(display, eglSurface, surface),
                             "eglDestroySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, display->destroySurface(eglSurface), "eglDestroySurface",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_QuerySurface(EGLDisplay dpy,
                                            EGLSurface surface,
                                            EGLint attribute,
                                            EGLint *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLint "
                   "*value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        const egl::Display *display = static_cast<const egl::Display *>(dpy);
        const Surface *eglSurface   = static_cast<const Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateQuerySurface(display, eglSurface, attribute, value),
                             "eglQuerySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, QuerySurfaceAttrib(display, eglSurface, attribute, value),
                             "eglQuerySurface", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLContext EGLAPIENTRY EGL_CreateContext(EGLDisplay dpy,
                                             EGLConfig config,
                                             EGLContext share_context,
                                             const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", EGLContext share_context = %d, "
                   "const EGLint *attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, CID(dpy, share_context), (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display        = static_cast<egl::Display *>(dpy);
        Config *configuration        = static_cast<Config *>(config);
        gl::Context *sharedGLContext = static_cast<gl::Context *>(share_context);
        AttributeMap attributes      = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread,
                             ValidateCreateContext(display, configuration, sharedGLContext, attributes),
                             "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);
    }
    
    EGLBoolean EGLAPIENTRY EGL_DestroyContext(EGLDisplay dpy, EGLContext ctx)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = %d", (uintptr_t)dpy,
                   CID(dpy, ctx));
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        gl::Context *context  = static_cast<gl::Context *>(ctx);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDestroyContext(display, context, ctx), "eglDestroyContext",
                             GetContextIfValid(display, context), EGL_FALSE);
    
        bool contextWasCurrent = context == thread->getContext();
    
        ANGLE_EGL_TRY_RETURN(thread, display->destroyContext(thread, context), "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);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_MakeCurrent(EGLDisplay dpy,
                                           EGLSurface draw,
                                           EGLSurface read,
                                           EGLContext ctx)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface draw = 0x%016" PRIxPTR
                   ", EGLSurface read = 0x%016" PRIxPTR
                   ", "
                   "EGLContext ctx = %d",
                   (uintptr_t)dpy, (uintptr_t)draw, (uintptr_t)read, CID(dpy, ctx));
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *drawSurface  = static_cast<Surface *>(draw);
        Surface *readSurface  = static_cast<Surface *>(read);
        gl::Context *context  = static_cast<gl::Context *>(ctx);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateMakeCurrent(display, drawSurface, readSurface, context),
                             "eglMakeCurrent", GetContextIfValid(display, context), 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;
    }
    
    EGLSurface EGLAPIENTRY EGL_GetCurrentSurface(EGLint readdraw)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLint readdraw = %d", readdraw);
        Thread *thread = egl::GetCurrentThread();
    
        if (readdraw == EGL_READ)
        {
            thread->setSuccess();
            return thread->getCurrentReadSurface();
        }
        else if (readdraw == EGL_DRAW)
        {
            thread->setSuccess();
            return thread->getCurrentDrawSurface();
        }
        else
        {
            thread->setError(EglBadParameter(), GetDebug(), "eglGetCurrentSurface", nullptr);
            return EGL_NO_SURFACE;
        }
    }
    
    EGLDisplay EGLAPIENTRY EGL_GetCurrentDisplay(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        thread->setSuccess();
        if (thread->getContext() != nullptr)
        {
            return thread->getContext()->getDisplay();
        }
        return EGL_NO_DISPLAY;
    }
    
    EGLBoolean EGLAPIENTRY EGL_QueryContext(EGLDisplay dpy,
                                            EGLContext ctx,
                                            EGLint attribute,
                                            EGLint *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLContext ctx = %d"
                   ", EGLint attribute = %d, EGLint *value "
                   "= 0x%016" PRIxPTR,
                   (uintptr_t)dpy, CID(dpy, ctx), attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        gl::Context *context  = static_cast<gl::Context *>(ctx);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateQueryContext(display, context, attribute, value),
                             "eglQueryContext", GetContextIfValid(display, context), EGL_FALSE);
    
        QueryContextAttrib(context, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_WaitGL(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = thread->getDisplay();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDisplay(display), "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 EGLAPIENTRY EGL_WaitNative(EGLint engine)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLint engine = %d", engine);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = thread->getDisplay();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateWaitNative(display, engine), "eglWaitNative",
                             GetThreadIfValid(thread), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, display->waitNative(thread->getContext(), engine), "eglWaitNative",
                             GetThreadIfValid(thread), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_SwapBuffers(EGLDisplay dpy, EGLSurface surface)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = (Surface *)surface;
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateSwapBuffers(thread, display, eglSurface), "eglSwapBuffers",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->swap(thread->getContext()), "eglSwapBuffers",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_CopyBuffers(EGLDisplay dpy,
                                           EGLSurface surface,
                                           EGLNativePixmapType target)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLNativePixmapType target = "
                   "0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface, (uintptr_t)target);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateCopyBuffers(display, eglSurface), "eglCopyBuffers",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        UNIMPLEMENTED();  // FIXME
    
        thread->setSuccess();
        return 0;
    }
    
    // EGL 1.1
    EGLBoolean EGLAPIENTRY EGL_BindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint buffer = %d",
                   (uintptr_t)dpy, (uintptr_t)surface, buffer);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display      = static_cast<egl::Display *>(dpy);
        Surface *eglSurface        = static_cast<Surface *>(surface);
        gl::Context *context       = thread->getContext();
        gl::Texture *textureObject = nullptr;
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateBindTexImage(display, eglSurface, surface, buffer, context, &textureObject),
            "eglBindTexImage", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        if (context)
        {
            ANGLE_EGL_TRY_RETURN(thread, eglSurface->bindTexImage(context, textureObject, buffer),
                                 "eglBindTexImage", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_SurfaceAttrib(EGLDisplay dpy,
                                             EGLSurface surface,
                                             EGLint attribute,
                                             EGLint value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLint "
                   "value = %d",
                   (uintptr_t)dpy, (uintptr_t)surface, attribute, value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateSurfaceAttrib(display, eglSurface, attribute, value),
                             "eglSurfaceAttrib", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        SetSurfaceAttrib(eglSurface, attribute, value);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_ReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint buffer = %d",
                   (uintptr_t)dpy, (uintptr_t)surface, buffer);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateReleaseTexImage(display, eglSurface, surface, buffer),
                             "eglReleaseTexImage", GetSurfaceIfValid(display, eglSurface), 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 EGLAPIENTRY EGL_SwapInterval(EGLDisplay dpy, EGLint interval)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint interval = %d", (uintptr_t)dpy,
                   interval);
        Thread *thread       = egl::GetCurrentThread();
        gl::Context *context = thread->getContext();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *draw_surface = static_cast<Surface *>(thread->getCurrentDrawSurface());
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateSwapInterval(display, draw_surface, context),
                             "eglSwapInterval", GetDisplayIfValid(display), EGL_FALSE);
    
        const egl::Config *surfaceConfig = draw_surface->getConfig();
        EGLint clampedInterval           = std::min(std::max(interval, surfaceConfig->minSwapInterval),
                                          surfaceConfig->maxSwapInterval);
    
        draw_surface->setSwapInterval(clampedInterval);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL 1.2
    EGLBoolean EGLAPIENTRY EGL_BindAPI(EGLenum api)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLenum api = 0x%X", api);
        Thread *thread = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateBindAPI(api), "eglBindAPI", GetThreadIfValid(thread),
                             EGL_FALSE);
    
        thread->setAPI(api);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLenum EGLAPIENTRY EGL_QueryAPI(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        EGLenum API = thread->getAPI();
    
        thread->setSuccess();
        return API;
    }
    
    EGLSurface EGLAPIENTRY EGL_CreatePbufferFromClientBuffer(EGLDisplay dpy,
                                                             EGLenum buftype,
                                                             EGLClientBuffer buffer,
                                                             EGLConfig config,
                                                             const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLenum buftype = 0x%X, EGLClientBuffer buffer = 0x%016" PRIxPTR
                   ", "
                   "EGLConfig config = 0x%016" PRIxPTR ", const EGLint *attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, buftype, (uintptr_t)buffer, (uintptr_t)config,
                   (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Config *configuration   = static_cast<Config *>(config);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(
            thread,
            ValidateCreatePbufferFromClientBuffer(display, buftype, buffer, configuration, attributes),
            "eglCreatePbufferFromClientBuffer", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::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);
    }
    
    EGLBoolean EGLAPIENTRY EGL_ReleaseThread(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        Surface *previousDraw         = thread->getCurrentDrawSurface();
        Surface *previousRead         = thread->getCurrentReadSurface();
        gl::Context *previousContext  = thread->getContext();
        egl::Display *previousDisplay = thread->getDisplay();
    
        // Only call makeCurrent if the context or surfaces have changed.
        if (previousDraw != EGL_NO_SURFACE || previousRead != EGL_NO_SURFACE ||
            previousContext != EGL_NO_CONTEXT)
        {
            if (previousDisplay != EGL_NO_DISPLAY)
            {
                ANGLE_EGL_TRY_RETURN(
                    thread, previousDisplay->makeCurrent(previousContext, nullptr, nullptr, nullptr),
                    "eglReleaseThread", nullptr, EGL_FALSE);
            }
    
            SetContextCurrent(thread, nullptr);
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_WaitClient(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = thread->getDisplay();
        gl::Context *context  = thread->getContext();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDisplay(display), "eglWaitClient",
                             GetContextIfValid(display, context), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, display->waitClient(context), "eglWaitClient",
                             GetContextIfValid(display, context), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL 1.4
    EGLContext EGLAPIENTRY EGL_GetCurrentContext(void)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        EVENT(nullptr, gl::EntryPoint::Begin, __FUNCTION__, "");
        Thread *thread = egl::GetCurrentThread();
    
        gl::Context *context = thread->getContext();
    
        thread->setSuccess();
        return static_cast<EGLContext>(context);
    }
    
    // EGL 1.5
    EGLSync EGLAPIENTRY EGL_CreateSync(EGLDisplay dpy, EGLenum type, const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLenum type = 0x%X, const EGLint* attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, type, (uintptr_t)attrib_list);
    
        Thread *thread          = egl::GetCurrentThread();
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        gl::Context *currentContext  = thread->getContext();
        egl::Display *currentDisplay = currentContext ? currentContext->getDisplay() : nullptr;
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateCreateSync(display, type, attributes, currentDisplay, currentContext),
            "eglCreateSync", GetDisplayIfValid(display), EGL_NO_SYNC);
    
        egl::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);
    }
    
    EGLBoolean EGLAPIENTRY EGL_DestroySync(EGLDisplay dpy, EGLSync sync)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSync sync = 0x%016" PRIxPTR, (uintptr_t)dpy,
                   (uintptr_t)sync);
    
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        egl::Sync *syncObject = static_cast<Sync *>(sync);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDestroySync(display, syncObject), "eglDestroySync",
                             GetDisplayIfValid(display), EGL_FALSE);
    
        display->destroySync(syncObject);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLint EGLAPIENTRY EGL_ClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSync sync = 0x%016" PRIxPTR
                   ", EGLint flags = 0x%X, EGLTime timeout = "
                   "%llu",
                   (uintptr_t)dpy, (uintptr_t)sync, flags, static_cast<unsigned long long>(timeout));
    
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        egl::Sync *syncObject = static_cast<Sync *>(sync);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateClientWaitSync(display, syncObject, flags, timeout),
                             "eglClientWaitSync", GetSyncIfValid(display, syncObject), 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 EGLAPIENTRY EGL_GetSyncAttrib(EGLDisplay dpy,
                                             EGLSync sync,
                                             EGLint attribute,
                                             EGLAttrib *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSync sync = 0x%016" PRIxPTR
                   ", EGLint attribute = 0x%X, EGLAttrib "
                   "*value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)sync, attribute, (uintptr_t)value);
    
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        egl::Sync *syncObject = static_cast<Sync *>(sync);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetSyncAttrib(display, syncObject, attribute, value),
                             "eglGetSyncAttrib", GetSyncIfValid(display, syncObject), EGL_FALSE);
    
        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;
    }
    
    EGLImage EGLAPIENTRY EGL_CreateImage(EGLDisplay dpy,
                                         EGLContext ctx,
                                         EGLenum target,
                                         EGLClientBuffer buffer,
                                         const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLContext ctx = %d"
                   ", EGLenum target = 0x%X, "
                   "EGLClientBuffer buffer = 0x%016" PRIxPTR
                   ", const EGLAttrib *attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, CID(dpy, ctx), target, (uintptr_t)buffer, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        gl::Context *context    = static_cast<gl::Context *>(ctx);
        AttributeMap attributes = AttributeMap::CreateFromIntArray((const EGLint *)attrib_list);
    
        Error error = ValidateCreateImage(display, context, target, buffer, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateImage", GetDisplayIfValid(display));
            return EGL_NO_IMAGE;
        }
    
        Image *image = nullptr;
        error        = display->createImage(context, target, buffer, attributes, &image);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateImage", GetDisplayIfValid(display));
            return EGL_NO_IMAGE;
        }
    
        thread->setSuccess();
        return static_cast<EGLImage>(image);
    }
    
    EGLBoolean EGLAPIENTRY EGL_DestroyImage(EGLDisplay dpy, EGLImage image)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLImage image = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)image);
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Image *img            = static_cast<Image *>(image);
    
        Error error = ValidateDestroyImage(display, img);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglDestroyImage", GetImageIfValid(display, img));
            return EGL_FALSE;
        }
    
        display->destroyImage(img);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLDisplay EGLAPIENTRY EGL_GetPlatformDisplay(EGLenum platform,
                                                  void *native_display,
                                                  const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLenum platform = %d, void* native_display = 0x%016" PRIxPTR
                   ", const EGLint* attrib_list = "
                   "0x%016" PRIxPTR,
                   platform, (uintptr_t)native_display, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetPlatformDisplay(platform, native_display, attrib_list),
                             "eglGetPlatformDisplay", GetThreadIfValid(thread), EGL_NO_DISPLAY);
    
        const auto &attribMap = AttributeMap::CreateFromAttribArray(attrib_list);
        if (platform == EGL_PLATFORM_ANGLE_ANGLE)
        {
            return egl::Display::GetDisplayFromNativeDisplay(
                gl::bitCast<EGLNativeDisplayType>(native_display), attribMap);
        }
        else if (platform == EGL_PLATFORM_DEVICE_EXT)
        {
            Device *eglDevice = static_cast<Device *>(native_display);
            return egl::Display::GetDisplayFromDevice(eglDevice, attribMap);
        }
        else
        {
            UNREACHABLE();
            return EGL_NO_DISPLAY;
        }
    }
    
    EGLSurface EGLAPIENTRY EGL_CreatePlatformWindowSurface(EGLDisplay dpy,
                                                           EGLConfig config,
                                                           void *native_window,
                                                           const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", void* native_window = 0x%016" PRIxPTR
                   ", "
                   "const EGLint* attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, (uintptr_t)native_window, (uintptr_t)attrib_list);
    
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
    
        Config *configuration = static_cast<Config *>(config);
        // Use reinterpret_cast since native_window could be a pointer or an actual value.
        EGLNativeWindowType win = reinterpret_cast<EGLNativeWindowType>(native_window);
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(thread,
                             ValidateCreateWindowSurface(display, configuration, win, attributes),
                             "eglCreateWindowSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::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);
    }
    
    EGLSurface EGLAPIENTRY EGL_CreatePlatformPixmapSurface(EGLDisplay dpy,
                                                           EGLConfig config,
                                                           void *native_pixmap,
                                                           const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLConfig config = 0x%016" PRIxPTR
                   ", void* native_pixmap = 0x%016" PRIxPTR
                   ", "
                   "const EGLint* attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)config, (uintptr_t)native_pixmap, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Config *configuration = static_cast<Config *>(config);
        // Use reinterpret_cast since native_window could be a pointer or an actual value.
        EGLNativePixmapType pixmap = reinterpret_cast<EGLNativePixmapType>(native_pixmap);
        AttributeMap attributes    = AttributeMap::CreateFromAttribArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateCreatePixmapSurface(display, configuration, pixmap, attributes),
            "eglCreatePlatformPixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        egl::Surface *surface = nullptr;
        ANGLE_EGL_TRY_RETURN(
            thread, display->createPixmapSurface(configuration, pixmap, attributes, &surface),
            "eglCreatePlatformPixmapSurface", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        thread->setSuccess();
        return static_cast<EGLSurface>(surface);
    }
    
    EGLBoolean EGLAPIENTRY EGL_WaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy =0x%016" PRIxPTR "p, EGLSync sync = 0x%016" PRIxPTR
                   ", EGLint flags = 0x%X",
                   (uintptr_t)dpy, (uintptr_t)sync, flags);
    
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        gl::Context *context  = thread->getContext();
        egl::Sync *syncObject = static_cast<Sync *>(sync);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateWaitSync(display, context, syncObject, flags),
                             "eglWaitSync", GetSyncIfValid(display, syncObject), 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;
    }
    
    __eglMustCastToProperFunctionPointerType EGLAPIENTRY EGL_GetProcAddress(const char *procname)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("const char *procname = \"%s\"", procname);
        Thread *thread = egl::GetCurrentThread();
    
        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;
    }
    }  // extern "C"