/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef GLLIBRARYEGL_H_
#define GLLIBRARYEGL_H_

#if defined(MOZ_X11)
#include "mozilla/X11Util.h"
#endif

#include "GLLibraryLoader.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/ThreadLocal.h"
#include "nsIFile.h"
#include "GeckoProfiler.h"

#include <bitset>
#include <vector>

#ifdef XP_WIN
    #ifndef WIN32_LEAN_AND_MEAN
        #define WIN32_LEAN_AND_MEAN 1
    #endif

    #include <windows.h>

    typedef HDC EGLNativeDisplayType;
    typedef HBITMAP EGLNativePixmapType;
    typedef HWND EGLNativeWindowType;
#else
    typedef void* EGLNativeDisplayType;
    typedef void* EGLNativePixmapType;
    typedef void* EGLNativeWindowType;

    #ifdef ANDROID
        // We only need to explicitly dlopen egltrace
        // on android as we can use LD_PRELOAD or other tricks
        // on other platforms. We look for it in /data/local
        // as that's writeable by all users
        //
        // This should really go in GLLibraryEGL.cpp but we currently reference
        // APITRACE_LIB in GLContextProviderEGL.cpp. Further refactoring
        // will come in subsequent patches on Bug 732865
        #define APITRACE_LIB "/data/local/tmp/egltrace.so"
    #endif
#endif

#if defined(MOZ_X11)
#define EGL_DEFAULT_DISPLAY  ((EGLNativeDisplayType)mozilla::DefaultXDisplay())
#else
#define EGL_DEFAULT_DISPLAY  ((EGLNativeDisplayType)0)
#endif

namespace angle {
class Platform;
}

namespace mozilla {

namespace gfx {
class DataSourceSurface;
}

namespace gl {

#undef BEFORE_GL_CALL
#undef AFTER_GL_CALL

#ifdef DEBUG

#ifndef MOZ_FUNCTION_NAME
# ifdef __GNUC__
#  define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
# elif defined(_MSC_VER)
#  define MOZ_FUNCTION_NAME __FUNCTION__
# else
#  define MOZ_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
# endif
#endif

#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#define BEFORE_GL_CALL                      \
    PROFILER_LABEL_FUNC(                    \
      js::ProfileEntry::Category::GRAPHICS);\
    BeforeGLCall(MOZ_FUNCTION_NAME)
#else
#define BEFORE_GL_CALL do {          \
    BeforeGLCall(MOZ_FUNCTION_NAME); \
} while (0)
#endif

#define AFTER_GL_CALL do {           \
    AfterGLCall(MOZ_FUNCTION_NAME);  \
} while (0)
#else
#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#define BEFORE_GL_CALL PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS)
#else
#define BEFORE_GL_CALL
#endif
#define AFTER_GL_CALL
#endif

class GLContext;

class GLLibraryEGL
{
public:
    GLLibraryEGL()
        : mInitialized(false),
          mEGLLibrary(nullptr),
          mEGLDisplay(EGL_NO_DISPLAY),
          mIsANGLE(false),
          mIsWARP(false)
    {
        ClearSymbols();
    }

    void ClearSymbols() {
        mSymbols.fANGLEPlatformInitialize = nullptr;
        mSymbols.fANGLEPlatformShutdown = nullptr;
        mSymbols.fGetDisplay = nullptr;
        mSymbols.fGetPlatformDisplayEXT = nullptr;
        mSymbols.fTerminate = nullptr;
        mSymbols.fGetCurrentSurface = nullptr;
        mSymbols.fGetCurrentContext = nullptr;
        mSymbols.fMakeCurrent = nullptr;
        mSymbols.fDestroyContext = nullptr;
        mSymbols.fCreateContext = nullptr;
        mSymbols.fDestroySurface = nullptr;
        mSymbols.fCreateWindowSurface = nullptr;
        mSymbols.fCreatePbufferSurface = nullptr;
        mSymbols.fCreatePixmapSurface = nullptr;
        mSymbols.fBindAPI = nullptr;
        mSymbols.fInitialize = nullptr;
        mSymbols.fChooseConfig = nullptr;
        mSymbols.fGetError = nullptr;
        mSymbols.fGetConfigAttrib = nullptr;
        mSymbols.fGetConfigs = nullptr;
        mSymbols.fWaitNative = nullptr;
        mSymbols.fGetProcAddress = nullptr;
        mSymbols.fSwapBuffers = nullptr;
        mSymbols.fCopyBuffers = nullptr;
        mSymbols.fQueryString = nullptr;
        mSymbols.fQueryStringImplementationANDROID = nullptr;
        mSymbols.fQueryContext = nullptr;
        mSymbols.fBindTexImage = nullptr;
        mSymbols.fReleaseTexImage = nullptr;
        mSymbols.fCreateImage = nullptr;
        mSymbols.fDestroyImage = nullptr;
        mSymbols.fLockSurface = nullptr;
        mSymbols.fUnlockSurface = nullptr;
        mSymbols.fQuerySurface = nullptr;
        mSymbols.fQuerySurfacePointerANGLE = nullptr;
        mSymbols.fCreateSync = nullptr;
        mSymbols.fDestroySync = nullptr;
        mSymbols.fClientWaitSync = nullptr;
        mSymbols.fGetSyncAttrib = nullptr;
        mSymbols.fDupNativeFenceFDANDROID = nullptr;
    }

    void InitClientExtensions();
    void InitDisplayExtensions();

    /**
     * Known GL extensions that can be queried by
     * IsExtensionSupported.  The results of this are cached, and as
     * such it's safe to use this even in performance critical code.
     * If you add to this array, remember to add to the string names
     * in GLContext.cpp.
     */
    enum EGLExtensions {
        KHR_image_base,
        KHR_image_pixmap,
        KHR_gl_texture_2D_image,
        KHR_lock_surface,
        ANGLE_surface_d3d_texture_2d_share_handle,
        EXT_create_context_robustness,
        KHR_image,
        KHR_fence_sync,
        ANDROID_native_fence_sync,
        EGL_ANDROID_image_crop,
        ANGLE_platform_angle,
        ANGLE_platform_angle_d3d,
        Extensions_Max
    };

    bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
        return mAvailableExtensions[aKnownExtension];
    }

    void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
        mAvailableExtensions[aKnownExtension] = false;
    }

protected:
    std::bitset<Extensions_Max> mAvailableExtensions;

public:

    EGLDisplay fGetDisplay(void* display_id)
    {
        BEFORE_GL_CALL;
        EGLDisplay disp = mSymbols.fGetDisplay(display_id);
        AFTER_GL_CALL;
        return disp;
    }

    EGLDisplay fGetPlatformDisplayEXT(EGLenum platform, void* native_display, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLDisplay disp = mSymbols.fGetPlatformDisplayEXT(platform, native_display, attrib_list);
        AFTER_GL_CALL;
        return disp;
    }

    EGLBoolean fTerminate(EGLDisplay display)
    {
        BEFORE_GL_CALL;
        EGLBoolean ret = mSymbols.fTerminate(display);
        AFTER_GL_CALL;
        return ret;
    }

    EGLSurface fGetCurrentSurface(EGLint id)
    {
        BEFORE_GL_CALL;
        EGLSurface surf = mSymbols.fGetCurrentSurface(id);
        AFTER_GL_CALL;
        return surf;
    }

    EGLContext fGetCurrentContext()
    {
        BEFORE_GL_CALL;
        EGLContext context = mSymbols.fGetCurrentContext();
        AFTER_GL_CALL;
        return context;
    }

    EGLBoolean fMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fMakeCurrent(dpy, draw, read, ctx);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fDestroyContext(EGLDisplay dpy, EGLContext ctx)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fDestroyContext(dpy, ctx);
        AFTER_GL_CALL;
        return b;
    }

    EGLContext fCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLContext ctx = mSymbols.fCreateContext(dpy, config, share_context, attrib_list);
        AFTER_GL_CALL;
        return ctx;
    }

    EGLBoolean fDestroySurface(EGLDisplay dpy, EGLSurface surface)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fDestroySurface(dpy, surface);
        AFTER_GL_CALL;
        return b;
    }

    EGLSurface fCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLSurface surf = mSymbols.fCreateWindowSurface(dpy, config, win, attrib_list);
        AFTER_GL_CALL;
        return surf;
    }

    EGLSurface fCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLSurface surf = mSymbols.fCreatePbufferSurface(dpy, config, attrib_list);
        AFTER_GL_CALL;
        return surf;
    }

    EGLSurface fCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLSurface surf = mSymbols.fCreatePixmapSurface(dpy, config, pixmap, attrib_list);
        AFTER_GL_CALL;
        return surf;
    }

    EGLBoolean fBindAPI(EGLenum api)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fBindAPI(api);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fInitialize(EGLDisplay dpy, EGLint* major, EGLint* minor)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fInitialize(dpy, major, minor);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fChooseConfig(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fChooseConfig(dpy, attrib_list, configs, config_size, num_config);
        AFTER_GL_CALL;
        return b;
    }

    EGLint fGetError()
    {
        BEFORE_GL_CALL;
        EGLint i = mSymbols.fGetError();
        AFTER_GL_CALL;
        return i;
    }

    EGLBoolean fGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fGetConfigAttrib(dpy, config, attribute, value);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fGetConfigs(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fGetConfigs(dpy, configs, config_size, num_config);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fWaitNative(EGLint engine)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fWaitNative(engine);
        AFTER_GL_CALL;
        return b;
    }

    EGLCastToRelevantPtr fGetProcAddress(const char* procname)
    {
        BEFORE_GL_CALL;
        EGLCastToRelevantPtr p = mSymbols.fGetProcAddress(procname);
        AFTER_GL_CALL;
        return p;
    }

    EGLBoolean fSwapBuffers(EGLDisplay dpy, EGLSurface surface)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fSwapBuffers(dpy, surface);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fCopyBuffers(dpy, surface, target);
        AFTER_GL_CALL;
        return b;
    }

    const GLubyte* fQueryString(EGLDisplay dpy, EGLint name)
    {
        BEFORE_GL_CALL;
        const GLubyte* b;
        if (mSymbols.fQueryStringImplementationANDROID) {
          b = mSymbols.fQueryStringImplementationANDROID(dpy, name);
        } else {
          b = mSymbols.fQueryString(dpy, name);
        }
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint* value)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fQueryContext(dpy, ctx, attribute, value);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fBindTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fBindTexImage(dpy, surface, buffer);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fReleaseTexImage(dpy, surface, buffer);
        AFTER_GL_CALL;
        return b;
    }

    EGLImage fCreateImage(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list)
    {
         BEFORE_GL_CALL;
         EGLImage i = mSymbols.fCreateImage(dpy, ctx, target, buffer, attrib_list);
         AFTER_GL_CALL;
         return i;
    }

    EGLBoolean fDestroyImage(EGLDisplay dpy, EGLImage image)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fDestroyImage(dpy, image);
        AFTER_GL_CALL;
        return b;
    }

    // New extension which allow us to lock texture and get raw image pointer
    EGLBoolean fLockSurface(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fLockSurface(dpy, surface, attrib_list);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fUnlockSurface(EGLDisplay dpy, EGLSurface surface)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fUnlockSurface(dpy, surface);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fQuerySurface(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fQuerySurface(dpy, surface, attribute, value);
        AFTER_GL_CALL;
        return b;
    }

    EGLBoolean fQuerySurfacePointerANGLE(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fQuerySurfacePointerANGLE(dpy, surface, attribute, value);
        AFTER_GL_CALL;
        return b;
    }

    EGLSync fCreateSync(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list)
    {
        BEFORE_GL_CALL;
        EGLSync ret = mSymbols.fCreateSync(dpy, type, attrib_list);
        AFTER_GL_CALL;
        return ret;
    }

    EGLBoolean fDestroySync(EGLDisplay dpy, EGLSync sync)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fDestroySync(dpy, sync);
        AFTER_GL_CALL;
        return b;
    }

    EGLint fClientWaitSync(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout)
    {
        BEFORE_GL_CALL;
        EGLint ret = mSymbols.fClientWaitSync(dpy, sync, flags, timeout);
        AFTER_GL_CALL;
        return ret;
    }

    EGLBoolean fGetSyncAttrib(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint* value)
    {
        BEFORE_GL_CALL;
        EGLBoolean b = mSymbols.fGetSyncAttrib(dpy, sync, attribute, value);
        AFTER_GL_CALL;
        return b;
    }

    EGLint fDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSync sync)
    {
        MOZ_ASSERT(mSymbols.fDupNativeFenceFDANDROID);
        BEFORE_GL_CALL;
        EGLint ret = mSymbols.fDupNativeFenceFDANDROID(dpy, sync);
        AFTER_GL_CALL;
        return ret;
    }

    void fANGLEPlatformInitialize(angle::Platform* platform)
    {
        MOZ_ASSERT(mSymbols.fANGLEPlatformInitialize);
        BEFORE_GL_CALL;
        mSymbols.fANGLEPlatformInitialize(platform);
        AFTER_GL_CALL;
    }

    void fANGLEPlatformShutdown()
    {
        MOZ_ASSERT(mSymbols.fANGLEPlatformShutdown);
        BEFORE_GL_CALL;
        mSymbols.fANGLEPlatformShutdown();
        AFTER_GL_CALL;
    }

    EGLDisplay Display() {
        MOZ_ASSERT(mInitialized);
        return mEGLDisplay;
    }

    bool IsANGLE() const {
        MOZ_ASSERT(mInitialized);
        return mIsANGLE;
    }

    bool IsWARP() const {
        MOZ_ASSERT(mInitialized);
        return mIsWARP;
    }

    bool HasKHRImageBase() {
        return IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base);
    }

    bool HasKHRImagePixmap() {
        return IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_pixmap);
    }

    bool HasKHRImageTexture2D() {
        return IsExtensionSupported(KHR_gl_texture_2D_image);
    }

    bool HasANGLESurfaceD3DTexture2DShareHandle() {
        return IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle);
    }

    bool HasRobustness() const {
        return IsExtensionSupported(EXT_create_context_robustness);
    }

    bool ReadbackEGLImage(EGLImage image, gfx::DataSourceSurface* out_surface);

    bool EnsureInitialized(bool forceAccel, nsACString* const out_failureId);

    void DumpEGLConfig(EGLConfig cfg);
    void DumpEGLConfigs();

    struct {
        typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void* display_id);
        pfnGetDisplay fGetDisplay;
        typedef EGLDisplay(GLAPIENTRY * pfnGetPlatformDisplayEXT)(EGLenum platform, void* native_display, const EGLint* attrib_list);
        pfnGetPlatformDisplayEXT fGetPlatformDisplayEXT;
        typedef EGLBoolean (GLAPIENTRY * pfnTerminate)(EGLDisplay dpy);
        pfnTerminate fTerminate;
        typedef EGLSurface (GLAPIENTRY * pfnGetCurrentSurface)(EGLint);
        pfnGetCurrentSurface fGetCurrentSurface;
        typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
        pfnGetCurrentContext fGetCurrentContext;
        typedef EGLBoolean (GLAPIENTRY * pfnMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
        pfnMakeCurrent fMakeCurrent;
        typedef EGLBoolean (GLAPIENTRY * pfnDestroyContext)(EGLDisplay dpy, EGLContext ctx);
        pfnDestroyContext fDestroyContext;
        typedef EGLContext (GLAPIENTRY * pfnCreateContext)(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint* attrib_list);
        pfnCreateContext fCreateContext;
        typedef EGLBoolean (GLAPIENTRY * pfnDestroySurface)(EGLDisplay dpy, EGLSurface surface);
        pfnDestroySurface fDestroySurface;
        typedef EGLSurface (GLAPIENTRY * pfnCreateWindowSurface)(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint* attrib_list);
        pfnCreateWindowSurface fCreateWindowSurface;
        typedef EGLSurface (GLAPIENTRY * pfnCreatePbufferSurface)(EGLDisplay dpy, EGLConfig config, const EGLint* attrib_list);
        pfnCreatePbufferSurface fCreatePbufferSurface;
        typedef EGLSurface (GLAPIENTRY * pfnCreatePixmapSurface)(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint* attrib_list);
        pfnCreatePixmapSurface fCreatePixmapSurface;
        typedef EGLBoolean (GLAPIENTRY * pfnBindAPI)(EGLenum api);
        pfnBindAPI fBindAPI;
        typedef EGLBoolean (GLAPIENTRY * pfnInitialize)(EGLDisplay dpy, EGLint* major, EGLint* minor);
        pfnInitialize fInitialize;
        typedef EGLBoolean (GLAPIENTRY * pfnChooseConfig)(EGLDisplay dpy, const EGLint* attrib_list, EGLConfig* configs, EGLint config_size, EGLint* num_config);
        pfnChooseConfig fChooseConfig;
        typedef EGLint (GLAPIENTRY * pfnGetError)(void);
        pfnGetError fGetError;
        typedef EGLBoolean (GLAPIENTRY * pfnGetConfigAttrib)(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint* value);
        pfnGetConfigAttrib fGetConfigAttrib;
        typedef EGLBoolean (GLAPIENTRY * pfnGetConfigs)(EGLDisplay dpy, EGLConfig* configs, EGLint config_size, EGLint* num_config);
        pfnGetConfigs fGetConfigs;
        typedef EGLBoolean (GLAPIENTRY * pfnWaitNative)(EGLint engine);
        pfnWaitNative fWaitNative;
        typedef EGLCastToRelevantPtr (GLAPIENTRY * pfnGetProcAddress)(const char* procname);
        pfnGetProcAddress fGetProcAddress;
        typedef EGLBoolean (GLAPIENTRY * pfnSwapBuffers)(EGLDisplay dpy, EGLSurface surface);
        pfnSwapBuffers fSwapBuffers;
        typedef EGLBoolean (GLAPIENTRY * pfnCopyBuffers)(EGLDisplay dpy, EGLSurface surface,
                                                         EGLNativePixmapType target);
        pfnCopyBuffers fCopyBuffers;
        typedef const GLubyte* (GLAPIENTRY * pfnQueryString)(EGLDisplay, EGLint name);
        pfnQueryString fQueryString;
        pfnQueryString fQueryStringImplementationANDROID;
        typedef EGLBoolean (GLAPIENTRY * pfnQueryContext)(EGLDisplay dpy, EGLContext ctx,
                                                          EGLint attribute, EGLint* value);
        pfnQueryContext fQueryContext;
        typedef EGLBoolean (GLAPIENTRY * pfnBindTexImage)(EGLDisplay, EGLSurface surface, EGLint buffer);
        pfnBindTexImage fBindTexImage;
        typedef EGLBoolean (GLAPIENTRY * pfnReleaseTexImage)(EGLDisplay, EGLSurface surface, EGLint buffer);
        pfnReleaseTexImage fReleaseTexImage;
        typedef EGLImage (GLAPIENTRY * pfnCreateImage)(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint* attrib_list);
        pfnCreateImage fCreateImage;
        typedef EGLBoolean (GLAPIENTRY * pfnDestroyImage)(EGLDisplay dpy, EGLImage image);
        pfnDestroyImage fDestroyImage;

        // New extension which allow us to lock texture and get raw image pointer
        typedef EGLBoolean (GLAPIENTRY * pfnLockSurface)(EGLDisplay dpy, EGLSurface surface, const EGLint* attrib_list);
        pfnLockSurface fLockSurface;
        typedef EGLBoolean (GLAPIENTRY * pfnUnlockSurface)(EGLDisplay dpy, EGLSurface surface);
        pfnUnlockSurface fUnlockSurface;
        typedef EGLBoolean (GLAPIENTRY * pfnQuerySurface)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint* value);
        pfnQuerySurface fQuerySurface;

        typedef EGLBoolean (GLAPIENTRY * pfnQuerySurfacePointerANGLE)(EGLDisplay dpy, EGLSurface surface, EGLint attribute, void** value);
        pfnQuerySurfacePointerANGLE fQuerySurfacePointerANGLE;

        typedef EGLSync (GLAPIENTRY * pfnCreateSync)(EGLDisplay dpy, EGLenum type, const EGLint* attrib_list);
        pfnCreateSync fCreateSync;
        typedef EGLBoolean (GLAPIENTRY * pfnDestroySync)(EGLDisplay dpy, EGLSync sync);
        pfnDestroySync fDestroySync;
        typedef EGLint (GLAPIENTRY * pfnClientWaitSync)(EGLDisplay dpy, EGLSync sync, EGLint flags, EGLTime timeout);
        pfnClientWaitSync fClientWaitSync;
        typedef EGLBoolean (GLAPIENTRY * pfnGetSyncAttrib)(EGLDisplay dpy, EGLSync sync, EGLint attribute, EGLint* value);
        pfnGetSyncAttrib fGetSyncAttrib;
        typedef EGLint (GLAPIENTRY * pfnDupNativeFenceFDANDROID)(EGLDisplay dpy, EGLSync sync);
        pfnDupNativeFenceFDANDROID fDupNativeFenceFDANDROID;

        typedef void (GLAPIENTRY * pfnANGLEPlatformInitialize)(angle::Platform* platform);
        pfnANGLEPlatformInitialize fANGLEPlatformInitialize;
        typedef void (GLAPIENTRY * pfnANGLEPlatformShutdown)();
        pfnANGLEPlatformShutdown fANGLEPlatformShutdown;
    } mSymbols;

#ifdef DEBUG
    static void BeforeGLCall(const char* glFunction);
    static void AfterGLCall(const char* glFunction);
#endif

#ifdef MOZ_B2G
    EGLContext CachedCurrentContext() {
        return sCurrentContext.get();
    }
    void UnsetCachedCurrentContext() {
        sCurrentContext.set(nullptr);
    }
    void SetCachedCurrentContext(EGLContext aCtx) {
        sCurrentContext.set(aCtx);
    }
    bool CachedCurrentContextMatches() {
        return sCurrentContext.get() == fGetCurrentContext();
    }

private:
    static MOZ_THREAD_LOCAL(EGLContext) sCurrentContext;
public:

#else
    EGLContext CachedCurrentContext() {
        return nullptr;
    }
    void UnsetCachedCurrentContext() {}
    void SetCachedCurrentContext(EGLContext aCtx) { }
    bool CachedCurrentContextMatches() { return true; }
#endif

private:
    bool mInitialized;
    PRLibrary* mEGLLibrary;
    EGLDisplay mEGLDisplay;
    RefPtr<GLContext> mReadbackGL;

    bool mIsANGLE;
    bool mIsWARP;
    static StaticMutex sMutex;
};

extern GLLibraryEGL sEGLLibrary;
#define EGL_DISPLAY()        sEGLLibrary.Display()

} /* namespace gl */
} /* namespace mozilla */

#endif /* GLLIBRARYEGL_H_ */