Edit

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

Branch :

  • Show log

    Commit

  • Author : Kenneth Russell
    Date : 2020-06-10 21:55:43
    Hash : af727792
    Message : Improve EGL_ANGLE_power_preference on dual-GPU MacBook Pros. Add the ability to release and reacquire the high-power GPU, and to respond to changes in the active GPU. In Chromium, the GPU process can not access the WindowServer. An external process must inform ANGLE that the active GPU has changed, and that ANGLE should switch its internal context to the new GPU. Incorporates a couple of functions from WebKit, used with permission, to effect this GPU switch. A follow-on change in Chromium which uses these new APIs will make the existing dual-GPU tests pass with ANGLE and the passthrough command decoder. Carry forward Chromium's workaround of disabling GPU switching on older MacBook Pros to ensure stability. Document the process of adding new EGL extensions to ANGLE. Bug: chromium:1091824 Change-Id: I499739156e851b493555d4d6e4aef87d8b97fa31 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2240638 Commit-Queue: Kenneth Russell <kbr@chromium.org> Reviewed-by: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: Geoff Lang <geofflang@chromium.org>

  • src/libGLESv2/entry_points_egl_ext.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_ext.cpp : Implements the EGL extension entry points.
    
    #include "libGLESv2/entry_points_egl_ext.h"
    
    #include "common/debug.h"
    #include "libANGLE/Context.h"
    #include "libANGLE/Device.h"
    #include "libANGLE/Display.h"
    #include "libANGLE/EGLSync.h"
    #include "libANGLE/Stream.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Thread.h"
    #include "libANGLE/entry_points_utils.h"
    #include "libANGLE/queryutils.h"
    #include "libANGLE/validationEGL.h"
    #include "libGLESv2/global_state.h"
    
    using namespace egl;
    
    extern "C" {
    
    // EGL_ANGLE_query_surface_pointer
    EGLBoolean EGLAPIENTRY EGL_QuerySurfacePointerANGLE(EGLDisplay dpy,
                                                        EGLSurface surface,
                                                        EGLint attribute,
                                                        void **value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, void "
                   "**value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        Error error = ValidateSurface(display, eglSurface);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQuerySurfacePointerANGLE",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        if (!display->getExtensions().querySurfacePointer)
        {
            thread->setSuccess();
            return EGL_FALSE;
        }
    
        if (surface == EGL_NO_SURFACE)
        {
            thread->setError(EglBadSurface(), GetDebug(), "eglQuerySurfacePointerANGLE",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        // validate the attribute parameter
        switch (attribute)
        {
            case EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE:
                if (!display->getExtensions().surfaceD3DTexture2DShareHandle)
                {
                    thread->setError(EglBadAttribute(), GetDebug(), "eglQuerySurfacePointerANGLE",
                                     GetSurfaceIfValid(display, eglSurface));
                    return EGL_FALSE;
                }
                break;
            case EGL_DXGI_KEYED_MUTEX_ANGLE:
                if (!display->getExtensions().keyedMutex)
                {
                    thread->setError(EglBadAttribute(), GetDebug(), "eglQuerySurfacePointerANGLE",
                                     GetSurfaceIfValid(display, eglSurface));
                    return EGL_FALSE;
                }
                break;
            default:
                thread->setError(EglBadAttribute(), GetDebug(), "eglQuerySurfacePointerANGLE",
                                 GetSurfaceIfValid(display, eglSurface));
                return EGL_FALSE;
        }
    
        error = eglSurface->querySurfacePointerANGLE(attribute, value);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQuerySurfacePointerANGLE",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_NV_post_sub_buffer
    EGLBoolean EGLAPIENTRY EGL_PostSubBufferNV(EGLDisplay dpy,
                                               EGLSurface surface,
                                               EGLint x,
                                               EGLint y,
                                               EGLint width,
                                               EGLint height)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint x = %d, EGLint y = %d, "
                   "EGLint width = %d, EGLint height = %d",
                   (uintptr_t)dpy, (uintptr_t)surface, x, y, width, height);
        Thread *thread        = egl::GetCurrentThread();
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        if (x < 0 || y < 0 || width < 0 || height < 0)
        {
            thread->setError(EglBadParameter(), GetDebug(), "eglPostSubBufferNV",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        Error error = ValidateSurface(display, eglSurface);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglPostSubBufferNV",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        if (display->testDeviceLost())
        {
            thread->setError(EglContextLost(), GetDebug(), "eglPostSubBufferNV",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        if (surface == EGL_NO_SURFACE)
        {
            thread->setError(EglBadSurface(), GetDebug(), "eglPostSubBufferNV",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        if (!display->getExtensions().postSubBuffer)
        {
            // Spec is not clear about how this should be handled.
            thread->setSuccess();
            return EGL_TRUE;
        }
    
        // TODO(jmadill): Validate Surface is bound to the thread.
        error = eglSurface->postSubBuffer(thread->getContext(), x, y, width, height);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglPostSubBufferNV",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_EXT_platform_base
    EGLDisplay EGLAPIENTRY EGL_GetPlatformDisplayEXT(EGLenum platform,
                                                     void *native_display,
                                                     const EGLint *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();
    
        Error err = ValidateGetPlatformDisplayEXT(platform, native_display, attrib_list);
        thread->setError(err, GetDebug(), "eglGetPlatformDisplayEXT", GetThreadIfValid(thread));
        if (err.isError())
        {
            return EGL_NO_DISPLAY;
        }
    
        const auto &attribMap = AttributeMap::CreateFromIntArray(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_CreatePlatformWindowSurfaceEXT(EGLDisplay dpy,
                                                              EGLConfig config,
                                                              void *native_window,
                                                              const EGLint *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);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(
            thread,
            ValidateCreatePlatformWindowSurfaceEXT(display, configuration, native_window, attributes),
            "eglCreatePlatformWindowSurfaceEXT", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        thread->setError(EglBadDisplay() << "CreatePlatformWindowSurfaceEXT unimplemented.", GetDebug(),
                         "eglCreatePlatformWindowSurfaceEXT", GetDisplayIfValid(display));
        return EGL_NO_SURFACE;
    }
    
    EGLSurface EGLAPIENTRY EGL_CreatePlatformPixmapSurfaceEXT(EGLDisplay dpy,
                                                              EGLConfig config,
                                                              void *native_pixmap,
                                                              const EGLint *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);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        ANGLE_EGL_TRY_RETURN(
            thread,
            ValidateCreatePlatformPixmapSurfaceEXT(display, configuration, native_pixmap, attributes),
            "eglCreatePlatformPixmapSurfaceEXT", GetDisplayIfValid(display), EGL_NO_SURFACE);
    
        thread->setError(EglBadDisplay() << "CreatePlatformPixmapSurfaceEXT unimplemented.", GetDebug(),
                         "eglCreatePlatformPixmapSurfaceEXT", GetDisplayIfValid(display));
        return EGL_NO_SURFACE;
    }
    
    // EGL_EXT_device_query
    EGLBoolean EGLAPIENTRY EGL_QueryDeviceAttribEXT(EGLDeviceEXT device,
                                                    EGLint attribute,
                                                    EGLAttrib *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDeviceEXT device = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLAttrib *value = 0x%016" PRIxPTR,
                   (uintptr_t)device, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        Device *dev = static_cast<Device *>(device);
    
        Error error = ValidateDevice(dev);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryDeviceAttribEXT", GetDeviceIfValid(dev));
            return EGL_FALSE;
        }
    
        // If the device was created by (and is owned by) a display, and that display doesn't support
        // device querying, then this call should fail
        egl::Display *owningDisplay = dev->getOwningDisplay();
        if (owningDisplay != nullptr && !owningDisplay->getExtensions().deviceQuery)
        {
            thread->setError(EglBadAccess() << "Device wasn't created using eglCreateDeviceANGLE, "
                                               "and the egl::Display that created it doesn't support "
                                               "device querying",
                             GetDebug(), "eglQueryDeviceAttribEXT", GetDeviceIfValid(dev));
            return EGL_FALSE;
        }
    
        // validate the attribute parameter
        switch (attribute)
        {
            case EGL_D3D11_DEVICE_ANGLE:
            case EGL_D3D9_DEVICE_ANGLE:
                if (!dev->getExtensions().deviceD3D || dev->getType() != attribute)
                {
                    thread->setError(EglBadAttribute(), GetDebug(), "eglQueryDeviceAttribEXT",
                                     GetDeviceIfValid(dev));
                    return EGL_FALSE;
                }
                error = dev->getAttribute(attribute, value);
                break;
            case EGL_EAGL_CONTEXT_ANGLE:
                if (!dev->getExtensions().deviceEAGL)
                {
                    thread->setError(EglBadAttribute(), GetDebug(), "eglQueryDeviceAttribEXT",
                                     GetDeviceIfValid(dev));
                    return EGL_FALSE;
                }
                error = dev->getAttribute(attribute, value);
                break;
            case EGL_CGL_CONTEXT_ANGLE:
            case EGL_CGL_PIXEL_FORMAT_ANGLE:
                if (!dev->getExtensions().deviceCGL)
                {
                    thread->setError(EglBadAttribute(), GetDebug(), "eglQueryDeviceAttribEXT",
                                     GetDeviceIfValid(dev));
                    return EGL_FALSE;
                }
                error = dev->getAttribute(attribute, value);
                break;
            default:
                thread->setError(EglBadAttribute(), GetDebug(), "eglQueryDeviceAttribEXT",
                                 GetDeviceIfValid(dev));
                return EGL_FALSE;
        }
    
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryDeviceAttribEXT", GetDeviceIfValid(dev));
            return EGL_FALSE;
        }
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_EXT_device_query
    const char *EGLAPIENTRY EGL_QueryDeviceStringEXT(EGLDeviceEXT device, EGLint name)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDeviceEXT device = 0x%016" PRIxPTR ", EGLint name = %d", (uintptr_t)device,
                   name);
        Thread *thread = egl::GetCurrentThread();
    
        Device *dev = static_cast<Device *>(device);
    
        Error error = ValidateDevice(dev);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryDeviceStringEXT", GetDeviceIfValid(dev));
            return EGL_FALSE;
        }
    
        const char *result;
        switch (name)
        {
            case EGL_EXTENSIONS:
                result = dev->getExtensionString().c_str();
                break;
            default:
                thread->setError(EglBadDevice(), GetDebug(), "eglQueryDeviceStringEXT",
                                 GetDeviceIfValid(dev));
                return nullptr;
        }
    
        thread->setSuccess();
        return result;
    }
    
    // EGL_EXT_device_query
    EGLBoolean EGLAPIENTRY EGL_QueryDisplayAttribEXT(EGLDisplay dpy, EGLint attribute, EGLAttrib *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLAttrib *value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, attribute, (uintptr_t)value);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateQueryDisplayAttribEXT(display, attribute),
                             "eglQueryDisplayAttribEXT", GetDisplayIfValid(display), EGL_FALSE);
    
        *value = display->queryAttrib(attribute);
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_ANGLE_feature_control
    EGLBoolean EGLAPIENTRY EGL_QueryDisplayAttribANGLE(EGLDisplay dpy,
                                                       EGLint attribute,
                                                       EGLAttrib *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLint attribute = %d, EGLAttrib *value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, attribute, (uintptr_t)value);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateQueryDisplayAttribANGLE(display, attribute),
                             "eglQueryDisplayAttribANGLE", GetDisplayIfValid(display), EGL_FALSE);
    
        *value = display->queryAttrib(attribute);
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLImageKHR EGLAPIENTRY EGL_CreateImageKHR(EGLDisplay dpy,
                                                            EGLContext ctx,
                                                            EGLenum target,
                                                            EGLClientBuffer buffer,
                                                            const EGLint *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(attrib_list);
    
        Error error = ValidateCreateImageKHR(display, context, target, buffer, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateImageKHR", GetDisplayIfValid(display));
            return EGL_NO_IMAGE;
        }
    
        Image *image = nullptr;
        error        = display->createImage(context, target, buffer, attributes, &image);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateImageKHR", GetDisplayIfValid(display));
            return EGL_NO_IMAGE;
        }
    
        thread->setSuccess();
        return static_cast<EGLImage>(image);
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_DestroyImageKHR(EGLDisplay dpy, EGLImageKHR 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 = ValidateDestroyImageKHR(display, img);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglDestroyImageKHR", GetImageIfValid(display, img));
            return EGL_FALSE;
        }
    
        display->destroyImage(img);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLDeviceEXT EGLAPIENTRY EGL_CreateDeviceANGLE(EGLint device_type,
                                                                void *native_device,
                                                                const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLint device_type = %d, void* native_device = 0x%016" PRIxPTR
                   ", const EGLAttrib* attrib_list = "
                   "0x%016" PRIxPTR,
                   device_type, (uintptr_t)native_device, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        Error error = ValidateCreateDeviceANGLE(device_type, native_device, attrib_list);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateDeviceANGLE", GetThreadIfValid(thread));
            return EGL_NO_DEVICE_EXT;
        }
    
        Device *device = nullptr;
        error          = Device::CreateDevice(device_type, native_device, &device);
        if (error.isError())
        {
            ASSERT(device == nullptr);
            thread->setError(error, GetDebug(), "eglCreateDeviceANGLE", GetThreadIfValid(thread));
            return EGL_NO_DEVICE_EXT;
        }
    
        thread->setSuccess();
        return device;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_ReleaseDeviceANGLE(EGLDeviceEXT device)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDeviceEXT device = 0x%016" PRIxPTR, (uintptr_t)device);
        Thread *thread = egl::GetCurrentThread();
    
        Device *dev = static_cast<Device *>(device);
    
        Error error = ValidateReleaseDeviceANGLE(dev);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglReleaseDeviceANGLE", GetDeviceIfValid(dev));
            return EGL_FALSE;
        }
    
        SafeDelete(dev);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_KHR_stream
    EGLStreamKHR EGLAPIENTRY EGL_CreateStreamKHR(EGLDisplay dpy, const EGLint *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", const EGLAttrib* attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        AttributeMap attributes = AttributeMap::CreateFromIntArray(attrib_list);
    
        Error error = ValidateCreateStreamKHR(display, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateStreamKHR", GetDisplayIfValid(display));
            return EGL_NO_STREAM_KHR;
        }
    
        Stream *stream;
        error = display->createStream(attributes, &stream);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateStreamKHR", GetDisplayIfValid(display));
            return EGL_NO_STREAM_KHR;
        }
    
        thread->setSuccess();
        return static_cast<EGLStreamKHR>(stream);
    }
    
    EGLBoolean EGLAPIENTRY EGL_DestroyStreamKHR(EGLDisplay dpy, EGLStreamKHR stream)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR = 0x%016" PRIxPTR, (uintptr_t)dpy,
                   (uintptr_t)stream);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
    
        Error error = ValidateDestroyStreamKHR(display, streamObject);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglDestroyStreamKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        display->destroyStream(streamObject);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamAttribKHR(EGLDisplay dpy,
                                               EGLStreamKHR stream,
                                               EGLenum attribute,
                                               EGLint value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", EGLenum attribute = 0x%X, "
                   "EGLint value = 0x%X",
                   (uintptr_t)dpy, (uintptr_t)stream, attribute, value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
    
        Error error = ValidateStreamAttribKHR(display, streamObject, attribute, value);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamAttribKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        switch (attribute)
        {
            case EGL_CONSUMER_LATENCY_USEC_KHR:
                streamObject->setConsumerLatency(value);
                break;
            case EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR:
                streamObject->setConsumerAcquireTimeout(value);
                break;
            default:
                UNREACHABLE();
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_QueryStreamKHR(EGLDisplay dpy,
                                              EGLStreamKHR stream,
                                              EGLenum attribute,
                                              EGLint *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", EGLenum attribute = 0x%X, "
                   "EGLint value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)stream, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
    
        Error error = ValidateQueryStreamKHR(display, streamObject, attribute, value);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryStreamKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        switch (attribute)
        {
            case EGL_STREAM_STATE_KHR:
                *value = streamObject->getState();
                break;
            case EGL_CONSUMER_LATENCY_USEC_KHR:
                *value = streamObject->getConsumerLatency();
                break;
            case EGL_CONSUMER_ACQUIRE_TIMEOUT_USEC_KHR:
                *value = streamObject->getConsumerAcquireTimeout();
                break;
            default:
                UNREACHABLE();
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_QueryStreamu64KHR(EGLDisplay dpy,
                                                 EGLStreamKHR stream,
                                                 EGLenum attribute,
                                                 EGLuint64KHR *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", EGLenum attribute = 0x%X, "
                   "EGLuint64KHR value = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)stream, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
    
        Error error = ValidateQueryStreamu64KHR(display, streamObject, attribute, value);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryStreamu64KHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        switch (attribute)
        {
            case EGL_PRODUCER_FRAME_KHR:
                *value = streamObject->getProducerFrame();
                break;
            case EGL_CONSUMER_FRAME_KHR:
                *value = streamObject->getConsumerFrame();
                break;
            default:
                UNREACHABLE();
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamConsumerGLTextureExternalKHR(EGLDisplay dpy, EGLStreamKHR stream)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR = 0x%016" PRIxPTR, (uintptr_t)dpy,
                   (uintptr_t)stream);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
        gl::Context *context  = gl::GetValidGlobalContext();
    
        Error error = ValidateStreamConsumerGLTextureExternalKHR(display, context, streamObject);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerGLTextureExternalKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->createConsumerGLTextureExternal(AttributeMap(), context);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerGLTextureExternalKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamConsumerAcquireKHR(EGLDisplay dpy, EGLStreamKHR stream)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR = 0x%016" PRIxPTR, (uintptr_t)dpy,
                   (uintptr_t)stream);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
        gl::Context *context  = gl::GetValidGlobalContext();
    
        Error error = ValidateStreamConsumerAcquireKHR(display, context, streamObject);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerAcquireKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->consumerAcquire(context);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerAcquireKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamConsumerReleaseKHR(EGLDisplay dpy, EGLStreamKHR stream)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR = 0x%016" PRIxPTR, (uintptr_t)dpy,
                   (uintptr_t)stream);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Stream *streamObject  = static_cast<Stream *>(stream);
        gl::Context *context  = gl::GetValidGlobalContext();
    
        Error error = ValidateStreamConsumerReleaseKHR(display, context, streamObject);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglSStreamConsumerReleaseKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->consumerRelease(context);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerReleaseKHR",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamConsumerGLTextureExternalAttribsNV(EGLDisplay dpy,
                                                                        EGLStreamKHR stream,
                                                                        const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", EGLAttrib attrib_list = 0x%016" PRIxPTR "",
                   (uintptr_t)dpy, (uintptr_t)stream, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Stream *streamObject    = static_cast<Stream *>(stream);
        gl::Context *context    = gl::GetValidGlobalContext();
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        Error error = ValidateStreamConsumerGLTextureExternalAttribsNV(display, context, streamObject,
                                                                       attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerGLTextureExternalAttribsNV",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->createConsumerGLTextureExternal(attributes, context);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamConsumerGLTextureExternalAttribsNV",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_CreateStreamProducerD3DTextureANGLE(EGLDisplay dpy,
                                                                   EGLStreamKHR stream,
                                                                   const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", EGLAttrib attrib_list = 0x%016" PRIxPTR "",
                   (uintptr_t)dpy, (uintptr_t)stream, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Stream *streamObject    = static_cast<Stream *>(stream);
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        Error error = ValidateCreateStreamProducerD3DTextureANGLE(display, streamObject, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateStreamProducerD3DTextureANGLE",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->createProducerD3D11Texture(attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglCreateStreamProducerD3DTextureANGLE",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_StreamPostD3DTextureANGLE(EGLDisplay dpy,
                                                         EGLStreamKHR stream,
                                                         void *texture,
                                                         const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLStreamKHR stream = 0x%016" PRIxPTR
                   ", void* texture = 0x%016" PRIxPTR
                   ", "
                   "EGLAttrib attrib_list = 0x%016" PRIxPTR "",
                   (uintptr_t)dpy, (uintptr_t)stream, (uintptr_t)texture, (uintptr_t)attrib_list);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display   = static_cast<egl::Display *>(dpy);
        Stream *streamObject    = static_cast<Stream *>(stream);
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        Error error = ValidateStreamPostD3DTextureANGLE(display, streamObject, texture, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamPostD3DTextureANGLE",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        error = streamObject->postD3D11Texture(texture, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglStreamPostD3DTextureANGLE",
                             GetStreamIfValid(display, streamObject));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_KHR_fence_sync
    ANGLE_EXPORT EGLSync EGLAPIENTRY EGL_CreateSyncKHR(EGLDisplay dpy,
                                                       EGLenum type,
                                                       const EGLint *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::CreateFromIntArray(attrib_list);
    
        gl::Context *currentContext  = thread->getContext();
        egl::Display *currentDisplay = currentContext ? currentContext->getDisplay() : nullptr;
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateCreateSyncKHR(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);
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_DestroySyncKHR(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;
    }
    
    ANGLE_EXPORT EGLint EGLAPIENTRY EGL_ClientWaitSyncKHR(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;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetSyncAttribKHR(EGLDisplay dpy,
                                                             EGLSync sync,
                                                             EGLint attribute,
                                                             EGLint *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, ValidateGetSyncAttribKHR(display, syncObject, attribute, value),
                             "eglGetSyncAttrib", GetSyncIfValid(display, syncObject), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, GetSyncAttrib(display, syncObject, attribute, value),
                             "eglGetSyncAttrib", GetSyncIfValid(display, syncObject), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_KHR_wait_sync
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_WaitSyncKHR(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;
    }
    
    EGLBoolean EGLAPIENTRY EGL_GetMscRateANGLE(EGLDisplay dpy,
                                               EGLSurface surface,
                                               EGLint *numerator,
                                               EGLint *denominator)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint* numerator = 0x%016" PRIxPTR
                   ", "
                   "EGLint* denomintor = 0x%016" PRIxPTR "",
                   (uintptr_t)dpy, (uintptr_t)surface, (uintptr_t)numerator, (uintptr_t)denominator);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        Error error = ValidateGetMscRateANGLE(display, eglSurface, numerator, denominator);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglGetMscRateANGLE",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        error = eglSurface->getMscRate(numerator, denominator);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglGetMscRateANGLE",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_GetSyncValuesCHROMIUM(EGLDisplay dpy,
                                                     EGLSurface surface,
                                                     EGLuint64KHR *ust,
                                                     EGLuint64KHR *msc,
                                                     EGLuint64KHR *sbc)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLuint64KHR* ust = 0x%016" PRIxPTR
                   ", "
                   "EGLuint64KHR* msc = 0x%016" PRIxPTR ", EGLuint64KHR* sbc = 0x%016" PRIxPTR "",
                   (uintptr_t)dpy, (uintptr_t)surface, (uintptr_t)ust, (uintptr_t)msc, (uintptr_t)sbc);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        Error error = ValidateGetSyncValuesCHROMIUM(display, eglSurface, ust, msc, sbc);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglGetSyncValuesCHROMIUM",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        error = eglSurface->getSyncValues(ust, msc, sbc);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglGetSyncValuesCHROMIUM",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_SwapBuffersWithDamageKHR(EGLDisplay dpy,
                                                        EGLSurface surface,
                                                        EGLint *rects,
                                                        EGLint n_rects)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint *rects = 0x%016" PRIxPTR
                   ", EGLint "
                   "n_rects = %d",
                   (uintptr_t)dpy, (uintptr_t)surface, (uintptr_t)rects, n_rects);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        Error error = ValidateSwapBuffersWithDamageKHR(display, eglSurface, rects, n_rects);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglSwapBuffersWithDamageEXT",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        error = eglSurface->swapWithDamage(thread->getContext(), rects, n_rects);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglSwapBuffersWithDamageEXT",
                             GetSurfaceIfValid(display, eglSurface));
            return EGL_FALSE;
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLBoolean EGLAPIENTRY EGL_PresentationTimeANDROID(EGLDisplay dpy,
                                                       EGLSurface surface,
                                                       EGLnsecsANDROID time)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLnsecsANDROID time = %llu",
                   (uintptr_t)dpy, (uintptr_t)surface, static_cast<unsigned long long>(time));
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
    
        ANGLE_EGL_TRY_RETURN(thread, ValidatePresentationTimeANDROID(display, eglSurface, time),
                             "eglPresentationTimeANDROID", GetSurfaceIfValid(display, eglSurface),
                             EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->setPresentationTime(time),
                             "eglPresentationTimeANDROID", GetSurfaceIfValid(display, eglSurface),
                             EGL_FALSE);
    
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT void EGLAPIENTRY EGL_SetBlobCacheFuncsANDROID(EGLDisplay dpy,
                                                               EGLSetBlobFuncANDROID set,
                                                               EGLGetBlobFuncANDROID get)
    {
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSetBlobFuncANDROID set = 0x%016" PRIxPTR
                   ", EGLGetBlobFuncANDROID get "
                   "= 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)set, (uintptr_t)get);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
    
        ANGLE_EGL_TRY(thread, ValidateSetBlobCacheANDROID(display, set, get),
                      "eglSetBlobCacheFuncsANDROID", GetDisplayIfValid(display));
    
        thread->setSuccess();
        display->setBlobCacheFuncs(set, get);
    }
    
    EGLint EGLAPIENTRY EGL_ProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLenum attrib = 0x%X", (uintptr_t)dpy, attrib);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateProgramCacheGetAttribANGLE(display, attrib),
                             "eglProgramCacheGetAttribANGLE", GetDisplayIfValid(display), 0);
    
        thread->setSuccess();
        return display->programCacheGetAttrib(attrib);
    }
    
    void EGLAPIENTRY EGL_ProgramCacheQueryANGLE(EGLDisplay dpy,
                                                EGLint index,
                                                void *key,
                                                EGLint *keysize,
                                                void *binary,
                                                EGLint *binarysize)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint index = %d, void *key = 0x%016" PRIxPTR
                   ", EGLint *keysize = "
                   "0x%016" PRIxPTR ", void *binary = 0x%016" PRIxPTR ", EGLint *size = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, index, (uintptr_t)key, (uintptr_t)keysize, (uintptr_t)binary,
                   (uintptr_t)binarysize);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY(thread,
                      ValidateProgramCacheQueryANGLE(display, index, key, keysize, binary, binarysize),
                      "eglProgramCacheQueryANGLE", GetDisplayIfValid(display));
    
        ANGLE_EGL_TRY(thread, display->programCacheQuery(index, key, keysize, binary, binarysize),
                      "eglProgramCacheQueryANGLE", GetDisplayIfValid(display));
    
        thread->setSuccess();
    }
    
    void EGLAPIENTRY EGL_ProgramCachePopulateANGLE(EGLDisplay dpy,
                                                   const void *key,
                                                   EGLint keysize,
                                                   const void *binary,
                                                   EGLint binarysize)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", void *key = 0x%016" PRIxPTR
                   ", EGLint keysize = %d, void *binary = "
                   "0x%016" PRIxPTR ", EGLint size = %d",
                   (uintptr_t)dpy, (uintptr_t)key, keysize, (uintptr_t)binary, binarysize);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY(thread,
                      ValidateProgramCachePopulateANGLE(display, key, keysize, binary, binarysize),
                      "eglProgramCachePopulateANGLE", GetDisplayIfValid(display));
    
        ANGLE_EGL_TRY(thread, display->programCachePopulate(key, keysize, binary, binarysize),
                      "eglProgramCachePopulateANGLE", GetDisplayIfValid(display));
    
        thread->setSuccess();
    }
    
    EGLint EGLAPIENTRY EGL_ProgramCacheResizeANGLE(EGLDisplay dpy, EGLint limit, EGLenum mode)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint limit = %d, EGLenum mode = 0x%X",
                   (uintptr_t)dpy, limit, mode);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateProgramCacheResizeANGLE(display, limit, mode),
                             "eglProgramCacheResizeANGLE", GetDisplayIfValid(display), 0);
    
        thread->setSuccess();
        return display->programCacheResize(limit, mode);
    }
    
    EGLint EGLAPIENTRY EGL_DebugMessageControlKHR(EGLDEBUGPROCKHR callback,
                                                  const EGLAttrib *attrib_list)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDEBUGPROCKHR callback = 0x%016" PRIxPTR
                   ", EGLAttrib attrib_list = 0x%016" PRIxPTR,
                   (uintptr_t)callback, (uintptr_t)attrib_list);
    
        Thread *thread = egl::GetCurrentThread();
    
        AttributeMap attributes = AttributeMap::CreateFromAttribArray(attrib_list);
    
        Error error = ValidateDebugMessageControlKHR(callback, attributes);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglDebugMessageControlKHR", nullptr);
            return error.getCode();
        }
    
        Debug *debug = GetDebug();
        debug->setCallback(callback, attributes);
    
        thread->setSuccess();
        return EGL_SUCCESS;
    }
    
    EGLBoolean EGLAPIENTRY EGL_QueryDebugKHR(EGLint attribute, EGLAttrib *value)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLint attribute = 0x%X, EGLAttrib* value = 0x%016" PRIxPTR, attribute,
                   (uintptr_t)value);
    
        Thread *thread = egl::GetCurrentThread();
    
        Error error = ValidateQueryDebugKHR(attribute, value);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryDebugKHR", nullptr);
            return EGL_FALSE;
        }
    
        Debug *debug = GetDebug();
        switch (attribute)
        {
            case EGL_DEBUG_MSG_CRITICAL_KHR:
            case EGL_DEBUG_MSG_ERROR_KHR:
            case EGL_DEBUG_MSG_WARN_KHR:
            case EGL_DEBUG_MSG_INFO_KHR:
                *value = debug->isMessageTypeEnabled(FromEGLenum<MessageType>(attribute)) ? EGL_TRUE
                                                                                          : EGL_FALSE;
                break;
            case EGL_DEBUG_CALLBACK_KHR:
                *value = reinterpret_cast<EGLAttrib>(debug->getCallback());
                break;
    
            default:
                UNREACHABLE();
        }
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    EGLint EGLAPIENTRY EGL_LabelObjectKHR(EGLDisplay dpy,
                                          EGLenum objectType,
                                          EGLObjectKHR object,
                                          EGLLabelKHR label)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR
                   ", EGLenum objectType = 0x%X, EGLObjectKHR object = 0x%016" PRIxPTR
                   ", "
                   "EGLLabelKHR label = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, objectType, (uintptr_t)object, (uintptr_t)label);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ObjectType objectTypePacked = FromEGLenum<ObjectType>(objectType);
        Error error = ValidateLabelObjectKHR(thread, display, objectTypePacked, object, label);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglLabelObjectKHR",
                             GetLabeledObjectIfValid(thread, display, objectTypePacked, object));
            return error.getCode();
        }
    
        LabeledObject *labeledObject =
            GetLabeledObjectIfValid(thread, display, objectTypePacked, object);
        ASSERT(labeledObject != nullptr);
        labeledObject->setLabel(label);
    
        thread->setSuccess();
        return EGL_SUCCESS;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetCompositorTimingSupportedANDROID(EGLDisplay dpy,
                                                                                EGLSurface surface,
                                                                                EGLint name)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint name = 0x%X",
                   (uintptr_t)dpy, (uintptr_t)surface, name);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
        Thread *thread        = egl::GetCurrentThread();
    
        CompositorTiming nameInternal = FromEGLenum<CompositorTiming>(name);
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateGetCompositorTimingSupportedANDROID(display, eglSurface, nameInternal),
            "eglQueryTimestampSupportedANDROID", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return eglSurface->getSupportedCompositorTimings().test(nameInternal);
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetCompositorTimingANDROID(EGLDisplay dpy,
                                                                       EGLSurface surface,
                                                                       EGLint numTimestamps,
                                                                       const EGLint *names,
                                                                       EGLnsecsANDROID *values)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint numTimestamps = %d, const EGLint *names = 0x%016" PRIxPTR
                   ", EGLnsecsANDROID *values = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface, numTimestamps, (uintptr_t)names,
                   (uintptr_t)values);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(
            thread,
            ValidateGetCompositorTimingANDROID(display, eglSurface, numTimestamps, names, values),
            "eglGetCompositorTimingANDROIDD", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->getCompositorTiming(numTimestamps, names, values),
                             "eglGetCompositorTimingANDROIDD", GetSurfaceIfValid(display, eglSurface),
                             EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetNextFrameIdANDROID(EGLDisplay dpy,
                                                                  EGLSurface surface,
                                                                  EGLuint64KHR *frameId)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLuint64KHR *frameId = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)surface, (uintptr_t)frameId);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetNextFrameIdANDROID(display, eglSurface, frameId),
                             "eglGetNextFrameIdANDROID", GetSurfaceIfValid(display, eglSurface),
                             EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->getNextFrameId(frameId), "eglGetNextFrameIdANDROID",
                             GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetFrameTimestampSupportedANDROID(EGLDisplay dpy,
                                                                              EGLSurface surface,
                                                                              EGLint timestamp)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLint timestamp = 0x%X",
                   (uintptr_t)dpy, (uintptr_t)surface, timestamp);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
        Thread *thread        = egl::GetCurrentThread();
    
        Timestamp timestampInternal = FromEGLenum<Timestamp>(timestamp);
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateGetFrameTimestampSupportedANDROID(display, eglSurface, timestampInternal),
            "eglQueryTimestampSupportedANDROID", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return eglSurface->getSupportedTimestamps().test(timestampInternal);
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetFrameTimestampsANDROID(EGLDisplay dpy,
                                                                      EGLSurface surface,
                                                                      EGLuint64KHR frameId,
                                                                      EGLint numTimestamps,
                                                                      const EGLint *timestamps,
                                                                      EGLnsecsANDROID *values)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT(
            "EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
            ", EGLuint64KHR frameId = %llu, EGLint numTimestamps = %d, const EGLint *timestamps = "
            "0x%016" PRIxPTR ", EGLnsecsANDROID *values = 0x%016" PRIxPTR,
            (uintptr_t)dpy, (uintptr_t)surface, (unsigned long long)frameId, numTimestamps,
            (uintptr_t)timestamps, (uintptr_t)values);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Surface *eglSurface   = static_cast<Surface *>(surface);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread,
                             ValidateGetFrameTimestampsANDROID(display, eglSurface, frameId,
                                                               numTimestamps, timestamps, values),
                             "eglGetFrameTimestampsANDROID", GetSurfaceIfValid(display, eglSurface),
                             EGL_FALSE);
        ANGLE_EGL_TRY_RETURN(
            thread, eglSurface->getFrameTimestamps(frameId, numTimestamps, timestamps, values),
            "eglGetFrameTimestampsANDROID", GetSurfaceIfValid(display, eglSurface), EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    // EGL_ANGLE_feature_control
    ANGLE_EXPORT const char *EGLAPIENTRY EGL_QueryStringiANGLE(EGLDisplay dpy,
                                                               EGLint name,
                                                               EGLint index)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLint name = %d, EGLint index = %d",
                   (uintptr_t)dpy, name, index);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateQueryStringiANGLE(display, name, index),
                             "eglQueryStringiANGLE", GetDisplayIfValid(display), nullptr);
    
        thread->setSuccess();
        return display->queryStringi(name, index);
    }
    
    EGLClientBuffer EGLAPIENTRY EGL_GetNativeClientBufferANDROID(const struct AHardwareBuffer *buffer)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("const struct AHardwareBuffer *buffer = 0x%016" PRIxPTR, (uintptr_t)buffer);
    
        Thread *thread = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateGetNativeClientBufferANDROID(buffer),
                             "eglGetNativeClientBufferANDROID", nullptr, nullptr);
    
        thread->setSuccess();
        return egl::Display::GetNativeClientBuffer(buffer);
    }
    
    EGLint EGLAPIENTRY EGL_DupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSyncKHR sync = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)sync);
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        Sync *syncObject      = static_cast<Sync *>(sync);
        Thread *thread        = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(thread, ValidateDupNativeFenceFDANDROID(display, syncObject),
                             "eglDupNativeFenceFDANDROID", GetSyncIfValid(display, syncObject),
                             EGL_NO_NATIVE_FENCE_FD_ANDROID);
    
        EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
        ANGLE_EGL_TRY_RETURN(thread, syncObject->dupNativeFenceFD(display, &result),
                             "eglDupNativeFenceFDANDROID", GetSyncIfValid(display, syncObject),
                             EGL_NO_NATIVE_FENCE_FD_ANDROID);
    
        thread->setSuccess();
        return result;
    }
    
    EGLBoolean EGLAPIENTRY EGL_SwapBuffersWithFrameTokenANGLE(EGLDisplay dpy,
                                                              EGLSurface surface,
                                                              EGLFrameTokenANGLE frametoken)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLSurface surface = 0x%016" PRIxPTR
                   ", EGLFrameTokenANGLE frametoken = 0x%llX",
                   (uintptr_t)dpy, (uintptr_t)surface, (unsigned long long)frametoken);
    
        egl::Display *display    = static_cast<egl::Display *>(dpy);
        egl::Surface *eglSurface = static_cast<egl::Surface *>(surface);
        Thread *thread           = egl::GetCurrentThread();
    
        ANGLE_EGL_TRY_RETURN(
            thread, ValidateSwapBuffersWithFrameTokenANGLE(display, eglSurface, frametoken),
            "eglSwapBuffersWithFrameTokenANGLE", GetDisplayIfValid(display), EGL_FALSE);
    
        ANGLE_EGL_TRY_RETURN(thread, eglSurface->swapWithFrameToken(thread->getContext(), frametoken),
                             "eglSwapBuffersWithFrameTokenANGLE", GetDisplayIfValid(display),
                             EGL_FALSE);
    
        thread->setSuccess();
        return EGL_TRUE;
    }
    
    void EGLAPIENTRY EGL_ReleaseHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)ctx);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        gl::Context *context  = static_cast<gl::Context *>(ctx);
    
        ANGLE_EGL_TRY(thread, ValidateContext(display, context), "eglReleaseHighPowerGPUANGLE",
                      GetDisplayIfValid(display));
        ANGLE_EGL_TRY(thread, context->releaseHighPowerGPU(), "eglReleaseHighPowerGPUANGLE",
                      GetDisplayIfValid(display));
    
        thread->setSuccess();
    }
    
    void EGLAPIENTRY EGL_ReacquireHighPowerGPUANGLE(EGLDisplay dpy, EGLContext ctx)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        FUNC_EVENT("EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = 0x%016" PRIxPTR,
                   (uintptr_t)dpy, (uintptr_t)ctx);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
        gl::Context *context  = static_cast<gl::Context *>(ctx);
    
        ANGLE_EGL_TRY(thread, ValidateContext(display, context), "eglReacquireHighPowerGPUANGLE",
                      GetDisplayIfValid(display));
        ANGLE_EGL_TRY(thread, context->reacquireHighPowerGPU(), "eglReacquireHighPowerGPUANGLE",
                      GetDisplayIfValid(display));
    
        thread->setSuccess();
    }
    
    void EGLAPIENTRY EGL_HandleGPUSwitchANGLE(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(thread, ValidateDisplay(display), "eglHandleGPUSwitchANGLE",
                      GetDisplayIfValid(display));
        ANGLE_EGL_TRY(thread, display->handleGPUSwitch(), "eglHandleGPUSwitchANGLE",
                      GetDisplayIfValid(display));
    
        thread->setSuccess();
    }
    
    }  // extern "C"