Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2018-12-17 13:13:49
    Hash : 4638dc9d
    Message : Re-land "Load correct libGLESv2 on Linux and Mac." Re-land fixes build to ensure commit_id is built before libEGL. libEGL was implicitly loading libGLESv2 on startup. This is bad because on platforms like Linux and Mac we could sometimes use the incorrect rpath. This in turn meant we needed workarounds like using "_angle" extensions to our shared objects to get the correct loading behaviour. Fix this by loading libGLESv2 dynamically in libEGL. We build the loader automatically from egl.xml. The loader itself is lazily initialized on every EGL entry point call. This is necessary because on Linux, etc, there is no equivalent to Windows' DLLMain. We also use an EGL.h with different generation options so we have the proper function pointer types. A README is included for instructions on how to regenerate EGL.h. The entry point generation script is refactored into a helper class that is used in the loader generator. Also adds the libGLESv2 versions of the EGL entry points in the DEF file on Windows. This allows them to be imported properly in 32-bit configurations. Also fixes up some errors in ANGLE's entry point definitions. Also includes a clang-format disable rule for the Khronos headers. This CL will help us to run ANGLE tests against native drivers. Bug: angleproject:2871 Bug: chromium:915731 Change-Id: I4192a938d1f4117cea1bf1399c98bda7ac25ddab Reviewed-on: https://chromium-review.googlesource.com/c/1380511 Reviewed-by: Yuly Novikov <ynovikov@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>

  • src/libGLESv2/entry_points_egl_ext.cpp
  • //
    // Copyright(c) 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/Stream.h"
    #include "libANGLE/Surface.h"
    #include "libANGLE/Thread.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();
        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();
        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();
        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();
        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();
        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();
        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->getDevice(value);
                if (error.isError())
                {
                    thread->setError(error, GetDebug(), "eglQueryDeviceAttribEXT",
                                     GetDeviceIfValid(dev));
                    return EGL_FALSE;
                }
                break;
            default:
                thread->setError(EglBadAttribute(), 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();
        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();
        EVENT("(EGLDisplay dpy = 0x%016" PRIxPTR
              ", EGLint attribute = %d, EGLAttrib *value = 0x%016" PRIxPTR ")",
              (uintptr_t)dpy, attribute, (uintptr_t)value);
        Thread *thread = egl::GetCurrentThread();
    
        egl::Display *display = static_cast<egl::Display *>(dpy);
    
        Error error = ValidateDisplay(display);
        if (error.isError())
        {
            thread->setError(error, GetDebug(), "eglQueryDisplayAttribEXT", GetDisplayIfValid(display));
            return EGL_FALSE;
        }
    
        if (!display->getExtensions().deviceQuery)
        {
            thread->setError(EglBadAccess(), GetDebug(), "eglQueryDisplayAttribEXT",
                             GetDisplayIfValid(display));
            return EGL_FALSE;
        }
    
        // validate the attribute parameter
        switch (attribute)
        {
            case EGL_DEVICE_EXT:
                *value = reinterpret_cast<EGLAttrib>(display->getDevice());
                break;
    
            default:
                thread->setError(EglBadAttribute(), GetDebug(), "eglQueryDisplayAttribEXT",
                                 GetDisplayIfValid(display));
                return EGL_FALSE;
        }
    
        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();
        EVENT("(EGLDisplay dpy = 0x%016" PRIxPTR ", EGLContext ctx = 0x%016" PRIxPTR
              ", EGLenum target = 0x%X, "
              "EGLClientBuffer buffer = 0x%016" PRIxPTR
              ", const EGLAttrib *attrib_list = 0x%016" PRIxPTR ")",
              (uintptr_t)dpy, (uintptr_t)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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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;
    }
    
    EGLBoolean EGLAPIENTRY EGL_GetSyncValuesCHROMIUM(EGLDisplay dpy,
                                                     EGLSurface surface,
                                                     EGLuint64KHR *ust,
                                                     EGLuint64KHR *msc,
                                                     EGLuint64KHR *sbc)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        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();
        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();
        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)
    {
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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();
        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);
        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();
        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);
    
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetNextFrameIdANDROID(EGLDisplay dpy,
                                                                  EGLSurface surface,
                                                                  EGLuint64KHR *frameId)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        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);
    
        return EGL_TRUE;
    }
    
    ANGLE_EXPORT EGLBoolean EGLAPIENTRY EGL_GetFrameTimestampSupportedANDROID(EGLDisplay dpy,
                                                                              EGLSurface surface,
                                                                              EGLint timestamp)
    {
        ANGLE_SCOPED_GLOBAL_LOCK();
        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);
        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();
        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);
    
        return EGL_TRUE;
    }
    
    }  // extern "C"