/* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is mozilla.org code.
 *
 * The Initial Developer of the Original Code is
 *   Mozilla Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Vladimir Vukicevic <vladimir@pobox.com>
 *   Mark Steele <mwsteele@gmail.com>
 *   Bas Schouten <bschouten@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#ifndef GLCONTEXT_H_
#define GLCONTEXT_H_

#include <stdio.h>

#ifdef WIN32
#include <windows.h>
#endif

#include "GLDefs.h"
#include "gfxASurface.h"
#include "gfxContext.h"
#include "gfxRect.h"
#include "nsISupportsImpl.h"
#include "prlink.h"

#include "nsDataHashtable.h"
#include "nsHashKeys.h"
#include "nsRegion.h"
#include "nsAutoPtr.h"

#ifndef GLAPIENTRY
#ifdef XP_WIN
#define GLAPIENTRY __stdcall
#else
#define GLAPIENTRY
#endif
#define GLAPI
#endif

#if defined(MOZ_PLATFORM_MAEMO) || defined(ANDROID)
#define USE_GLES2 1
#endif

typedef char realGLboolean;

namespace mozilla {
namespace gl {

class GLContext;

class LibrarySymbolLoader
{
public:
    PRBool OpenLibrary(const char *library);

    typedef PRFuncPtr (GLAPIENTRY * PlatformLookupFunction) (const char *);

    enum {
        MAX_SYMBOL_NAMES = 5,
        MAX_SYMBOL_LENGTH = 128
    };

    typedef struct {
        PRFuncPtr *symPointer;
        const char *symNames[MAX_SYMBOL_NAMES];
    } SymLoadStruct;

    PRBool LoadSymbols(SymLoadStruct *firstStruct,
                       PRBool tryplatform = PR_FALSE,
                       const char *prefix = nsnull);

    /*
     * Static version of the functions in this class
     */
    static PRFuncPtr LookupSymbol(PRLibrary *lib,
                                  const char *symname,
                                  PlatformLookupFunction lookupFunction = nsnull);
    static PRBool LoadSymbols(PRLibrary *lib,
                              SymLoadStruct *firstStruct,
                              PlatformLookupFunction lookupFunction = nsnull,
                              const char *prefix = nsnull);
protected:
    LibrarySymbolLoader() {
        mLibrary = nsnull;
        mLookupFunc = nsnull;
    }

    PRLibrary *mLibrary;
    PlatformLookupFunction mLookupFunc;
};


/**
 * A TextureImage encapsulates a surface that can be drawn to by a
 * Thebes gfxContext and (hopefully efficiently!) synchronized to a
 * texture in the server.  TextureImages are associated with one and
 * only one GLContext.
 *
 * Implementation note: TextureImages attempt to unify two categories
 * of backends
 *
 *  (1) proxy to server-side object that can be bound to a texture;
 *      e.g. Pixmap on X11.
 *
 *  (2) efficient manager of texture memory; e.g. by having clients draw
 *      into a scratch buffer which is then uploaded with
 *      glTexSubImage2D().
 */
class TextureImage
{
    NS_INLINE_DECL_REFCOUNTING(TextureImage)
public:
    typedef gfxASurface::gfxContentType ContentType;

    virtual ~TextureImage() {}

    /**
     * Return a gfxContext for updating |aRegion| of the client's
     * image if successul, NULL if not.  |aRegion|'s bounds must fit
     * within Size(); its coordinate space (if any) is ignored.  If
     * the update begins successfully, the returned gfxContext is
     * owned by this.  Otherwise, NULL is returned.
     *
     * |aRegion| is an inout param: the returned region is what the
     * client must repaint.  Category (1) regions above can
     * efficiently handle repaints to "scattered" regions, while (2)
     * can only efficiently handle repaints to rects.
     *
     * The returned context is neither translated nor clipped: it's a
     * context for rect(<0,0>, Size()).  Painting the returned context
     * outside of |aRegion| results in undefined behavior.
     *
     * BeginUpdate() calls cannot be "nested", and each successful
     * BeginUpdate() must be followed by exactly one EndUpdate() (see
     * below).  Failure to do so can leave this in a possibly
     * inconsistent state.  Unsuccessful BeginUpdate()s must not be
     * followed by EndUpdate().
     */
    virtual gfxContext* BeginUpdate(nsIntRegion& aRegion) = 0;
    /**
     * Finish the active update and synchronize with the server, if
     * necessary.  Return PR_TRUE iff this's texture is already bound.
     *
     * BeginUpdate() must have been called exactly once before
     * EndUpdate().
     */
    virtual PRBool EndUpdate() = 0;

    /**
     * Return this TextureImage's texture ID for use with GL APIs.
     * Callers are responsible for properly binding the texture etc.
     *
     * The effects of using a texture after BeginUpdate() but before
     * EndUpdate() are undefined.
     */
    GLuint Texture() { return mTexture; }

    /** Can be called safely at any time. */

    /**
     * If this TextureImage has a permanent gfxASurface backing,
     * return it.  Otherwise return NULL.
     */
    virtual already_AddRefed<gfxASurface> GetBackingSurface()
    { return NULL; }

    const nsIntSize& GetSize() const { return mSize; }
    ContentType GetContentType() const { return mContentType; }
    virtual PRBool InUpdate() const = 0;

protected:
    /**
     * After the ctor, the TextureImage is invalid.  Implementations
     * must allocate resources successfully before returning the new
     * TextureImage from GLContext::CreateTextureImage().  That is,
     * clients must not be given partially-constructed TextureImages.
     */
    TextureImage(GLuint aTexture, const nsIntSize& aSize, ContentType aContentType)
        : mTexture(aTexture)
        , mSize(aSize)
        , mContentType(aContentType)
    {}

    GLuint mTexture;
    nsIntSize mSize;
    ContentType mContentType;
};

/**
 * BasicTextureImage is the baseline TextureImage implementation ---
 * it updates its texture by allocating a scratch buffer for the
 * client to draw into, then using glTexSubImage2D() to upload the new
 * pixels.  Platforms must provide the code to create a new surface
 * into which the updated pixels will be drawn, and the code to
 * convert the update surface's pixels into an image on which we can
 * glTexSubImage2D().
 */
class BasicTextureImage
    : public TextureImage
{
public:
    virtual ~BasicTextureImage();

    virtual gfxContext* BeginUpdate(nsIntRegion& aRegion);
    virtual PRBool EndUpdate();

    virtual PRBool InUpdate() const { return !!mUpdateContext; }

protected:
    typedef gfxASurface::gfxImageFormat ImageFormat;

    BasicTextureImage(GLuint aTexture,
                      const nsIntSize& aSize,
                      ContentType aContentType,
                      GLContext* aContext)
        : TextureImage(aTexture, aSize, aContentType)
        , mTextureInited(PR_FALSE)
        , mGLContext(aContext)
    {}

    virtual already_AddRefed<gfxASurface>
    CreateUpdateSurface(const gfxIntSize& aSize, ImageFormat aFmt) = 0;

    virtual already_AddRefed<gfxImageSurface>
    GetImageForUpload(gfxASurface* aUpdateSurface) = 0;

    PRBool mTextureInited;
    GLContext* mGLContext;
    nsRefPtr<gfxContext> mUpdateContext;
    nsIntRect mUpdateRect;
};

struct THEBES_API ContextFormat {
    static const ContextFormat BasicRGBA32Format;

    enum StandardContextFormat {
        Empty,
        BasicRGBA32,
        StrictBasicRGBA32,
        BasicRGB24,
        StrictBasicRGB24,
        BasicRGB16_565,
        StrictBasicRGB16_565
    };

    ContextFormat() {
        memset(this, 0, sizeof(ContextFormat));
    }

    ContextFormat(const StandardContextFormat cf) {
        memset(this, 0, sizeof(ContextFormat));

        switch (cf) {
        case BasicRGBA32:
            red = green = blue = alpha = 8;
            minRed = minGreen = minBlue = minAlpha = 1;
            break;

        case StrictBasicRGBA32:
            red = green = blue = alpha = 8;
            minRed = minGreen = minBlue = minAlpha = 8;
            break;

        case BasicRGB24:
            red = green = blue = 8;
            minRed = minGreen = minBlue = 1;
            break;

        case StrictBasicRGB24:
            red = green = blue = 8;
            minRed = minGreen = minBlue = 8;
            break;

        case StrictBasicRGB16_565:
            red = minRed = 5;
            green = minGreen = 6;
            blue = minBlue = 5;
            break;

        default:
            break;
        }
    }

    int depth, minDepth;
    int stencil, minStencil;
    int red, minRed;
    int green, minGreen;
    int blue, minBlue;
    int alpha, minAlpha;

    int colorBits() const { return red + green + blue; }
};

class GLContext
    : public LibrarySymbolLoader
{
    THEBES_INLINE_DECL_THREADSAFE_REFCOUNTING(GLContext)
public:
    GLContext(const ContextFormat& aFormat,
              PRBool aIsOffscreen = PR_FALSE,
              GLContext *aSharedContext = nsnull)
      : mInitialized(PR_FALSE),
        mIsOffscreen(aIsOffscreen),
#ifdef USE_GLES2
        mIsGLES2(PR_TRUE),
#else
        mIsGLES2(PR_FALSE),
#endif
        mCreationFormat(aFormat),
        mSharedContext(aSharedContext),
        mOffscreenTexture(0),
        mOffscreenFBO(0),
        mOffscreenDepthRB(0),
        mOffscreenStencilRB(0)
    {
        mUserData.Init();
    }

    virtual ~GLContext() {
#ifdef DEBUG
        if (mSharedContext) {
            GLContext *tip = mSharedContext;
            while (tip->mSharedContext)
                tip = tip->mSharedContext;
            tip->SharedContextDestroyed(this);
            tip->ReportOutstandingNames();
        }
#endif
    }

    enum GLContextType {
        ContextTypeUnknown,
        ContextTypeWGL,
        ContextTypeCGL,
        ContextTypeGLX,
        ContextTypeEGL,
        ContextTypeOSMesa
    };

    virtual GLContextType GetContextType() { return ContextTypeUnknown; }
    virtual PRBool MakeCurrent() = 0;
    virtual PRBool SetupLookupFunction() = 0;

    virtual void WindowDestroyed() {}

    void *GetUserData(void *aKey) {
        void *result = nsnull;
        mUserData.Get(aKey, &result);
        return result;
    }

    void SetUserData(void *aKey, void *aValue) {
        mUserData.Put(aKey, aValue);
    }

    // Mark this context as destroyed.  This will NULL out all
    // the GL function pointers!
    void THEBES_API MarkDestroyed();

    enum NativeDataType {
      NativeGLContext,
      NativeImageSurface,
      NativeThebesSurface,
      NativeDataTypeMax
    };

    virtual void *GetNativeData(NativeDataType aType) { return NULL; }
    GLContext *GetSharedContext() { return mSharedContext; }

    PRBool IsGlobalSharedContext() { return mIsGlobalSharedContext; }
    void SetIsGlobalSharedContext(PRBool aIsOne) { mIsGlobalSharedContext = aIsOne; }

    const ContextFormat& CreationFormat() { return mCreationFormat; }
    const ContextFormat& ActualFormat() { return mActualFormat; }

    /**
     * If this context is double-buffered, returns TRUE.
     */
    virtual PRBool IsDoubleBuffered() { return PR_FALSE; }

    /**
     * If this context is the GLES2 API, returns TRUE.
     * This means that various GLES2 restrictions might be in effect (modulo
     * extensions).
     */
    PRBool IsGLES2() {
        return mIsGLES2;
    }
 
    /**
     * If this context wraps a double-buffered target, swap the back
     * and front buffers.  It should be assumed that after a swap, the
     * contents of the new back buffer are undefined.
     */
    virtual PRBool SwapBuffers() { return PR_FALSE; }

    /**
     * Defines a two-dimensional texture image for context target surface
     */
    virtual PRBool BindTexImage() { return PR_FALSE; }
    /*
     * Releases a color buffer that is being used as a texture
     */
    virtual PRBool ReleaseTexImage() { return PR_FALSE; }

    /*
     * Offscreen support API
     */

    /*
     * Bind aOffscreen's color buffer as a texture to the TEXTURE_2D
     * target.  Returns TRUE on success, otherwise FALSE.  If
     * aOffscreen is not an offscreen context, returns FALSE.  If
     * BindOffscreenNeedsTexture() returns TRUE, then you should have
     * a 2D texture name bound whose image will be replaced by the
     * contents of the offscreen context.  If it returns FALSE,
     * the current 2D texture binding will be replaced.
     *
     * After a successul call to BindTex2DOffscreen, UnbindTex2DOffscreen
     * *must* be called once rendering is complete.
     *
     * The same texture unit must be active for Bind/Unbind of a given
     * context.
     */
    virtual PRBool BindOffscreenNeedsTexture(GLContext *aOffscreen) {
        return aOffscreen->mOffscreenTexture == 0;
    }

    virtual PRBool BindTex2DOffscreen(GLContext *aOffscreen) {
        if (aOffscreen->GetContextType() != GetContextType()) {
          return PR_FALSE;
        }

        if (!aOffscreen->mOffscreenFBO) {
            return PR_FALSE;
        }

        if (!aOffscreen->mSharedContext ||
            aOffscreen->mSharedContext != mSharedContext)
        {
            return PR_FALSE;
        }

        fBindTexture(LOCAL_GL_TEXTURE_2D, aOffscreen->mOffscreenTexture);

        return PR_TRUE;
    }

    virtual void UnbindTex2DOffscreen(GLContext *aOffscreen) { }

    PRBool IsOffscreen() {
        return mIsOffscreen;
    }

    /*
     * All the methods below are only valid if IsOffscreen() returns
     * true.
     */

    /*
     * Resize the current offscreen buffer.  Returns true on success.
     * If it returns false, the context should be treated as unusable
     * and should be recreated.  After the resize, the viewport is not
     * changed; glViewport should be called as appropriate.
     */
    virtual PRBool ResizeOffscreen(const gfxIntSize& aNewSize) {
        if (mOffscreenFBO)
            return ResizeOffscreenFBO(aNewSize);
        return PR_FALSE;
    }

    /*
     * Return size of this offscreen context.
     */
    gfxIntSize OffscreenSize() {
        return mOffscreenSize;
    }

    /*
     * In some cases, we have to allocate a bigger offscreen buffer
     * than what's requested.  This is the bigger size.
     */
    gfxIntSize OffscreenActualSize() {
        return mOffscreenActualSize;
    }

    /*
     * If this context is FBO-backed, return the FBO or the color
     * buffer texture.  If the context is not FBO-backed, 0 is
     * returned (which is also a valid FBO binding).
     */
    GLuint GetOffscreenFBO() {
        return mOffscreenFBO;
    }
    GLuint GetOffscreenTexture() {
        return mOffscreenTexture;
    }

    /**
     * Return a valid, allocated TextureImage of |aSize| with
     * |aContentType|.  The TextureImage's texture is configured to
     * use |aWrapMode| (usually GL_CLAMP_TO_EDGE or GL_REPEAT) and by
     * default, GL_LINEAR filtering.  Specify
     * |aUseNearestFilter=PR_TRUE| for GL_NEAREST filtering.  Return
     * NULL if creating the TextureImage fails.
     *
     * The returned TextureImage may only be used with this GLContext.
     * Attempting to use the returned TextureImage after this
     * GLContext is destroyed will result in undefined (and likely
     * crashy) behavior.
     */
    virtual already_AddRefed<TextureImage>
    CreateTextureImage(const nsIntSize& aSize,
                       TextureImage::ContentType aContentType,
                       GLint aWrapMode,
                       PRBool aUseNearestFilter=PR_FALSE);

    /**
     * Read the image data contained in aTexture, and return it as an ImageSurface.
     * If GL_RGBA is given as the format, a ImageFormatARGB32 surface is returned.
     * Not implemented yet:
     * If GL_RGB is given as the format, a ImageFormatRGB24 surface is returned.
     * If GL_LUMINANCE is given as the format, a ImageFormatA8 surface is returned.
     *
     * THIS IS EXPENSIVE.  It is ridiculously expensive.  Only do this
     * if you absolutely positively must, and never in any performance
     * critical path.
     */
    already_AddRefed<gfxImageSurface> ReadTextureImage(GLuint aTexture,
                                                       const gfxIntSize& aSize,
                                                       GLenum aTextureFormat);

    /**
     * Call ReadPixels into an existing gfxImageSurface for the given bounds.
     * The image surface must be using image format RGBA32 or RGB24.
     */
    void ReadPixelsIntoImageSurface(GLint aX, GLint aY, GLsizei aWidth, GLsizei aHeight,
                                    gfxImageSurface *aDest);

    /**
     * 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 GLExtensions {
        EXT_framebuffer_object,
        ARB_framebuffer_object,
        ARB_texture_rectangle,
        EXT_bgra,
        EXT_texture_format_BGRA8888,
        OES_depth24,
        OES_depth32,
        OES_stencil8,
        OES_texture_npot,
        OES_depth_texture,
        OES_packed_depth_stencil,
        IMG_read_format,
        EXT_read_format_bgra,
        Extensions_Max
    };

    PRBool IsExtensionSupported(GLExtensions aKnownExtension) {
        return mAvailableExtensions[aKnownExtension];
    }

protected:
    PRPackedBool mInitialized;
    PRPackedBool mIsOffscreen;
    PRPackedBool mIsGLES2;
    PRPackedBool mIsGlobalSharedContext;
    ContextFormat mCreationFormat;
    nsRefPtr<GLContext> mSharedContext;

    void UpdateActualFormat();
    ContextFormat mActualFormat;

    gfxIntSize mOffscreenSize;
    gfxIntSize mOffscreenActualSize;
    GLuint mOffscreenTexture;

    // helper to create/resize an offscreen FBO,
    // for offscreen implementations that use FBOs.
    PRBool ResizeOffscreenFBO(const gfxIntSize& aSize);
    void DeleteOffscreenFBO();
    GLuint mOffscreenFBO;
    GLuint mOffscreenDepthRB;
    GLuint mOffscreenStencilRB;

    // this should just be a std::bitset, but that ended up breaking
    // MacOS X builds; see bug 584919.  We can replace this with one
    // later on.
    template<size_t setlen>
    struct ExtensionBitset {
        ExtensionBitset() {
            for (size_t i = 0; i < setlen; ++i)
                values[i] = false;
        }

        bool& operator[](size_t index) {
            NS_ASSERTION(index < setlen, "out of range");
            return values[index];
        }

        bool values[setlen];
    };
    ExtensionBitset<Extensions_Max> mAvailableExtensions;

    // Clear to transparent black, with 0 depth and stencil,
    // while preserving current ClearColor etc. values.
    // Useful for resizing offscreen buffers.
    void ClearSafely();

    nsDataHashtable<nsVoidPtrHashKey, void*> mUserData;

    void SetIsGLES2(PRBool aIsGLES2) {
        NS_ASSERTION(!mInitialized, "SetIsGLES2 can only be called before initialization!");
        mIsGLES2 = aIsGLES2;
    }

    PRBool InitWithPrefix(const char *prefix, PRBool trygl);

    void InitExtensions();
    PRBool IsExtensionSupported(const char *extension);

    virtual already_AddRefed<TextureImage>
    CreateBasicTextureImage(GLuint aTexture,
                            const nsIntSize& aSize,
                            TextureImage::ContentType aContentType,
                            GLContext* aContext)
    { return NULL; }

    //
    // the wrapped functions
    //
public:
    /* One would think that this would live in some nice
     * perl-or-python-or-js script somewhere and would be
     * autogenerated; one would be wrong.
     */
    // Keep this at the start of the function pointers
    void *mFunctionListStartSentinel;

    typedef void (GLAPIENTRY * PFNGLACTIVETEXTUREPROC) (GLenum texture);
    PFNGLACTIVETEXTUREPROC fActiveTexture;
    typedef void (GLAPIENTRY * PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader);
    PFNGLATTACHSHADERPROC fAttachShader;
    typedef void (GLAPIENTRY * PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar* name);
    PFNGLBINDATTRIBLOCATIONPROC fBindAttribLocation;
    typedef void (GLAPIENTRY * PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer);
    PFNGLBINDBUFFERPROC fBindBuffer;
    typedef void (GLAPIENTRY * PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture);
    PFNGLBINDTEXTUREPROC fBindTexture;
    typedef void (GLAPIENTRY * PFNGLBLENDCOLORPROC) (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
    PFNGLBLENDCOLORPROC fBlendColor;
    typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONPROC) (GLenum mode);
    PFNGLBLENDEQUATIONPROC fBlendEquation;
    typedef void (GLAPIENTRY * PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum, GLenum);
    PFNGLBLENDEQUATIONSEPARATEPROC fBlendEquationSeparate;
    typedef void (GLAPIENTRY * PFNGLBLENDFUNCPROC) (GLenum, GLenum);
    PFNGLBLENDFUNCPROC fBlendFunc;
    typedef void (GLAPIENTRY * PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha);
    PFNGLBLENDFUNCSEPARATEPROC fBlendFuncSeparate;
    typedef void (GLAPIENTRY * PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
    PFNGLBUFFERDATAPROC fBufferData;
    typedef void (GLAPIENTRY * PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
    PFNGLBUFFERSUBDATAPROC fBufferSubData;
    typedef void (GLAPIENTRY * PFNGLCLEARPROC) (GLbitfield);
    PFNGLCLEARPROC fClear;
    typedef void (GLAPIENTRY * PFNGLCLEARCOLORPROC) (GLclampf, GLclampf, GLclampf, GLclampf);
    PFNGLCLEARCOLORPROC fClearColor;
    typedef void (GLAPIENTRY * PFNGLCLEARSTENCILPROC) (GLint);
    PFNGLCLEARSTENCILPROC fClearStencil;
    typedef void (GLAPIENTRY * PFNGLCOLORMASKPROC) (realGLboolean red, realGLboolean green, realGLboolean blue, realGLboolean alpha);
    PFNGLCOLORMASKPROC fColorMask;
    typedef void (GLAPIENTRY * PFNGLCULLFACEPROC) (GLenum mode);
    PFNGLCULLFACEPROC fCullFace;
    typedef void (GLAPIENTRY * PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader);
    PFNGLDETACHSHADERPROC fDetachShader;
    typedef void (GLAPIENTRY * PFNGLDEPTHFUNCPROC) (GLenum);
    PFNGLDEPTHFUNCPROC fDepthFunc;
    typedef void (GLAPIENTRY * PFNGLDEPTHMASKPROC) (realGLboolean);
    PFNGLDEPTHMASKPROC fDepthMask;
    typedef void (GLAPIENTRY * PFNGLDISABLEPROC) (GLenum);
    PFNGLDISABLEPROC fDisable;
    typedef void (GLAPIENTRY * PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint);
    PFNGLDISABLEVERTEXATTRIBARRAYPROC fDisableVertexAttribArray;
    typedef void (GLAPIENTRY * PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count);
    PFNGLDRAWARRAYSPROC fDrawArrays;
    typedef void (GLAPIENTRY * PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);
    PFNGLDRAWELEMENTSPROC fDrawElements;
    typedef void (GLAPIENTRY * PFNGLENABLEPROC) (GLenum);
    PFNGLENABLEPROC fEnable;
    typedef void (GLAPIENTRY * PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint);
    PFNGLENABLEVERTEXATTRIBARRAYPROC fEnableVertexAttribArray;
    typedef void (GLAPIENTRY * PFNGLFINISHPROC) (void);
    PFNGLFINISHPROC fFinish;
    typedef void (GLAPIENTRY * PFNGLFLUSHPROC) (void);
    PFNGLFLUSHPROC fFlush;
    typedef void (GLAPIENTRY * PFNGLFRONTFACEPROC) (GLenum);
    PFNGLFRONTFACEPROC fFrontFace;
    typedef void (GLAPIENTRY * PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
    PFNGLGETACTIVEATTRIBPROC fGetActiveAttrib;
    typedef void (GLAPIENTRY * PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei maxLength, GLsizei* length, GLint* size, GLenum* type, GLchar* name);
    PFNGLGETACTIVEUNIFORMPROC fGetActiveUniform;
    typedef void (GLAPIENTRY * PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei* count, GLuint* shaders);
    PFNGLGETATTACHEDSHADERSPROC fGetAttachedShaders;
    typedef GLint (GLAPIENTRY * PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar* name);
    PFNGLGETATTRIBLOCATIONPROC fGetAttribLocation;
    typedef void (GLAPIENTRY * PFNGLGETINTEGERVPROC) (GLenum pname, GLint *params);
    PFNGLGETINTEGERVPROC fGetIntegerv;
    typedef void (GLAPIENTRY * PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *params);
    PFNGLGETFLOATVPROC fGetFloatv;
    typedef void (GLAPIENTRY * PFNGLGETBOOLEANBPROC) (GLenum pname, realGLboolean *params);
    PFNGLGETBOOLEANBPROC fGetBooleanv;
    typedef void (GLAPIENTRY * PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint* params);
    PFNGLGETBUFFERPARAMETERIVPROC fGetBufferParameteriv;
    typedef void (GLAPIENTRY * PFNGLGENERATEMIPMAPPROC) (GLenum target);
    PFNGLGENERATEMIPMAPPROC fGenerateMipmap;
    typedef GLenum (GLAPIENTRY * PFNGLGETERRORPROC) (void);
    PFNGLGETERRORPROC fGetError;
    typedef void (GLAPIENTRY * PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint* param);
    PFNGLGETPROGRAMIVPROC fGetProgramiv;
    typedef void (GLAPIENTRY * PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
    PFNGLGETPROGRAMINFOLOGPROC fGetProgramInfoLog;
    typedef void (GLAPIENTRY * PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
    PFNGLTEXPARAMETERIPROC fTexParameteri;
    typedef void (GLAPIENTRY * PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param);
    PFNGLTEXPARAMETERFPROC fTexParameterf;
    typedef GLubyte* (GLAPIENTRY * PFNGLGETSTRINGPROC) (GLenum);
    PFNGLGETSTRINGPROC fGetString;
    typedef void (GLAPIENTRY * PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params);
    PFNGLGETTEXPARAMETERFVPROC fGetTexParameterfv;
    typedef void (GLAPIENTRY * PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params);
    PFNGLGETTEXPARAMETERIVPROC fGetTexParameteriv;
    typedef void (GLAPIENTRY * PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat* params);
    PFNGLGETUNIFORMFVPROC fGetUniformfv;
    typedef void (GLAPIENTRY * PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint* params);
    PFNGLGETUNIFORMIVPROC fGetUniformiv;
    typedef GLint (GLAPIENTRY * PFNGLGETUNIFORMLOCATIONPROC) (GLint programObj, const GLchar* name);
    PFNGLGETUNIFORMLOCATIONPROC fGetUniformLocation;
    typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBFVPROC) (GLuint, GLenum, GLfloat*);
    PFNGLGETVERTEXATTRIBFVPROC fGetVertexAttribfv;
    typedef void (GLAPIENTRY * PFNGLGETVERTEXATTRIBIVPROC) (GLuint, GLenum, GLint*);
    PFNGLGETVERTEXATTRIBIVPROC fGetVertexAttribiv;
    typedef void (GLAPIENTRY * PFNGLHINTPROC) (GLenum target, GLenum mode);
    PFNGLHINTPROC fHint;
    typedef realGLboolean (GLAPIENTRY * PFNGLISBUFFERPROC) (GLuint buffer);
    PFNGLISBUFFERPROC fIsBuffer;
    typedef realGLboolean (GLAPIENTRY * PFNGLISENABLEDPROC) (GLenum cap);
    PFNGLISENABLEDPROC fIsEnabled;
    typedef realGLboolean (GLAPIENTRY * PFNGLISPROGRAMPROC) (GLuint program);
    PFNGLISPROGRAMPROC fIsProgram;
    typedef realGLboolean (GLAPIENTRY * PFNGLISSHADERPROC) (GLuint shader);
    PFNGLISSHADERPROC fIsShader;
    typedef realGLboolean (GLAPIENTRY * PFNGLISTEXTUREPROC) (GLuint texture);
    PFNGLISTEXTUREPROC fIsTexture;
    typedef void (GLAPIENTRY * PFNGLLINEWIDTHPROC) (GLfloat width);
    PFNGLLINEWIDTHPROC fLineWidth;
    typedef void (GLAPIENTRY * PFNGLLINKPROGRAMPROC) (GLuint program);
    PFNGLLINKPROGRAMPROC fLinkProgram;
    typedef void (GLAPIENTRY * PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param);
    PFNGLPIXELSTOREIPROC fPixelStorei;
    typedef void (GLAPIENTRY * PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat bias);
    PFNGLPOLYGONOFFSETPROC fPolygonOffset;
    typedef void (GLAPIENTRY * PFNGLREADBUFFERPROC) (GLenum);
    PFNGLREADBUFFERPROC fReadBuffer;
    typedef void (GLAPIENTRY * PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels);
    PFNGLREADPIXELSPROC fReadPixels;
    typedef void (GLAPIENTRY * PFNGLSAMPLECOVERAGEPROC) (GLclampf value, realGLboolean invert);
    PFNGLSAMPLECOVERAGEPROC fSampleCoverage;
    typedef void (GLAPIENTRY * PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
    PFNGLSCISSORPROC fScissor;
    typedef void (GLAPIENTRY * PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask);
    PFNGLSTENCILFUNCPROC fStencilFunc;
    typedef void (GLAPIENTRY * PFNGLSTENCILFUNCSEPARATEPROC) (GLenum frontfunc, GLenum backfunc, GLint ref, GLuint mask);
    PFNGLSTENCILFUNCSEPARATEPROC fStencilFuncSeparate;
    typedef void (GLAPIENTRY * PFNGLSTENCILMASKPROC) (GLuint mask);
    PFNGLSTENCILMASKPROC fStencilMask;
    typedef void (GLAPIENTRY * PFNGLSTENCILMASKSEPARATEPROC) (GLenum, GLuint);
    PFNGLSTENCILMASKSEPARATEPROC fStencilMaskSeparate;
    typedef void (GLAPIENTRY * PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass);
    PFNGLSTENCILOPPROC fStencilOp;
    typedef void (GLAPIENTRY * PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass);
    PFNGLSTENCILOPSEPARATEPROC fStencilOpSeparate;
    typedef void (GLAPIENTRY * PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels);
    PFNGLTEXIMAGE2DPROC fTexImage2D;
    typedef void (GLAPIENTRY * PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void* pixels);
    PFNGLTEXSUBIMAGE2DPROC fTexSubImage2D;
    typedef void (GLAPIENTRY * PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0);
    PFNGLUNIFORM1FPROC fUniform1f;
    typedef void (GLAPIENTRY * PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat* value);
    PFNGLUNIFORM1FVPROC fUniform1fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM1IPROC) (GLint location, GLint v0);
    PFNGLUNIFORM1IPROC fUniform1i;
    typedef void (GLAPIENTRY * PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint* value);
    PFNGLUNIFORM1IVPROC fUniform1iv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1);
    PFNGLUNIFORM2FPROC fUniform2f;
    typedef void (GLAPIENTRY * PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat* value);
    PFNGLUNIFORM2FVPROC fUniform2fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1);
    PFNGLUNIFORM2IPROC fUniform2i;
    typedef void (GLAPIENTRY * PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint* value);
    PFNGLUNIFORM2IVPROC fUniform2iv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2);
    PFNGLUNIFORM3FPROC fUniform3f;
    typedef void (GLAPIENTRY * PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat* value);
    PFNGLUNIFORM3FVPROC fUniform3fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2);
    PFNGLUNIFORM3IPROC fUniform3i;
    typedef void (GLAPIENTRY * PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint* value);
    PFNGLUNIFORM3IVPROC fUniform3iv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3);
    PFNGLUNIFORM4FPROC fUniform4f;
    typedef void (GLAPIENTRY * PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat* value);
    PFNGLUNIFORM4FVPROC fUniform4fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3);
    PFNGLUNIFORM4IPROC fUniform4i;
    typedef void (GLAPIENTRY * PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint* value);
    PFNGLUNIFORM4IVPROC fUniform4iv;
    typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
    PFNGLUNIFORMMATRIX2FVPROC fUniformMatrix2fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
    PFNGLUNIFORMMATRIX3FVPROC fUniformMatrix3fv;
    typedef void (GLAPIENTRY * PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, realGLboolean transpose, const GLfloat* value);
    PFNGLUNIFORMMATRIX4FVPROC fUniformMatrix4fv;
    typedef void (GLAPIENTRY * PFNGLUSEPROGRAMPROC) (GLuint program);
    PFNGLUSEPROGRAMPROC fUseProgram;
    typedef void (GLAPIENTRY * PFNGLVALIDATEPROGRAMPROC) (GLuint program);
    PFNGLVALIDATEPROGRAMPROC fValidateProgram;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, realGLboolean normalized, GLsizei stride, const GLvoid* pointer);
    PFNGLVERTEXATTRIBPOINTERPROC fVertexAttribPointer;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x);
    PFNGLVERTEXATTRIB1FPROC fVertexAttrib1f;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y);
    PFNGLVERTEXATTRIB2FPROC fVertexAttrib2f;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z);
    PFNGLVERTEXATTRIB3FPROC fVertexAttrib3f;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w);
    PFNGLVERTEXATTRIB4FPROC fVertexAttrib4f;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat* v);
    PFNGLVERTEXATTRIB1FVPROC fVertexAttrib1fv;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat* v);
    PFNGLVERTEXATTRIB2FVPROC fVertexAttrib2fv;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat* v);
    PFNGLVERTEXATTRIB3FVPROC fVertexAttrib3fv;
    typedef void (GLAPIENTRY * PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat* v);
    PFNGLVERTEXATTRIB4FVPROC fVertexAttrib4fv;
    typedef void (GLAPIENTRY * PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height);
    PFNGLVIEWPORTPROC fViewport;
    typedef void (GLAPIENTRY * PFNGLCOMPILESHADERPROC) (GLuint shader);
    PFNGLCOMPILESHADERPROC fCompileShader;
    typedef void (GLAPIENTRY * PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border);
    PFNGLCOPYTEXIMAGE2DPROC fCopyTexImage2D;
    typedef void (GLAPIENTRY * PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height);
    PFNGLCOPYTEXSUBIMAGE2DPROC fCopyTexSubImage2D;
    typedef void (GLAPIENTRY * PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint* param);
    PFNGLGETSHADERIVPROC fGetShaderiv;
    typedef void (GLAPIENTRY * PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
    PFNGLGETSHADERINFOLOGPROC fGetShaderInfoLog;
    typedef void (GLAPIENTRY * PFNGLGETSHADERSOURCEPROC) (GLint obj, GLsizei maxLength, GLsizei* length, GLchar* source);
    PFNGLGETSHADERSOURCEPROC fGetShaderSource;
    typedef void (GLAPIENTRY * PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar** strings, const GLint* lengths);
    PFNGLSHADERSOURCEPROC fShaderSource;

    typedef void (GLAPIENTRY * PFNGLBINDFRAMEBUFFER) (GLenum target, GLuint framebuffer);
    PFNGLBINDFRAMEBUFFER fBindFramebuffer;
    typedef void (GLAPIENTRY * PFNGLBINDRENDERBUFFER) (GLenum target, GLuint renderbuffer);
    PFNGLBINDRENDERBUFFER fBindRenderbuffer;
    typedef GLenum (GLAPIENTRY * PFNGLCHECKFRAMEBUFFERSTATUS) (GLenum target);
    PFNGLCHECKFRAMEBUFFERSTATUS fCheckFramebufferStatus;
    typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERRENDERBUFFER) (GLenum target, GLenum attachmentPoint, GLenum renderbufferTarget, GLuint renderbuffer);
    PFNGLFRAMEBUFFERRENDERBUFFER fFramebufferRenderbuffer;
    typedef void (GLAPIENTRY * PFNGLFRAMEBUFFERTEXTURE2D) (GLenum target, GLenum attachmentPoint, GLenum textureTarget, GLuint texture, GLint level);
    PFNGLFRAMEBUFFERTEXTURE2D fFramebufferTexture2D;
    typedef void (GLAPIENTRY * PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIV) (GLenum target, GLenum attachment, GLenum pname, GLint* value);
    PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIV fGetFramebufferAttachmentParameteriv;
    typedef void (GLAPIENTRY * PFNGLGETRENDERBUFFERPARAMETERIV) (GLenum target, GLenum pname, GLint* value);
    PFNGLGETRENDERBUFFERPARAMETERIV fGetRenderbufferParameteriv;
    typedef realGLboolean (GLAPIENTRY * PFNGLISFRAMEBUFFER) (GLuint framebuffer);
    PFNGLISFRAMEBUFFER fIsFramebuffer;
    typedef realGLboolean (GLAPIENTRY * PFNGLISRENDERBUFFER) (GLuint renderbuffer);
    PFNGLISRENDERBUFFER fIsRenderbuffer;
    typedef void (GLAPIENTRY * PFNGLRENDERBUFFERSTORAGE) (GLenum target, GLenum internalFormat, GLsizei width, GLsizei height);
    PFNGLRENDERBUFFERSTORAGE fRenderbufferStorage;

    // keep this at the end of the function pointers
    void *mFunctionListEndSentinel;

    void fDepthRange(GLclampf a, GLclampf b) {
        if (mIsGLES2) {
            priv_fDepthRangef(a, b);
        } else {
            priv_fDepthRange(a, b);
        }
    }

    void fClearDepth(GLclampf v) {
        if (mIsGLES2) {
            priv_fClearDepthf(v);
        } else {
            priv_fClearDepth(v);
        }
    }

protected:
    /* These are different between GLES2 and desktop GL; we hide those differences, use the GL
     * names, but the most limited data type.
     */
    typedef void (GLAPIENTRY * PFNGLDEPTHRANGEFPROC) (GLclampf, GLclampf);
    PFNGLDEPTHRANGEFPROC priv_fDepthRangef;
    typedef void (GLAPIENTRY * PFNGLCLEARDEPTHFPROC) (GLclampf);
    PFNGLCLEARDEPTHFPROC priv_fClearDepthf;

    typedef void (GLAPIENTRY * PFNGLDEPTHRANGEPROC) (GLclampd, GLclampd);
    PFNGLDEPTHRANGEPROC priv_fDepthRange;
    typedef void (GLAPIENTRY * PFNGLCLEARDEPTHPROC) (GLclampd);
    PFNGLCLEARDEPTHPROC priv_fClearDepth;

    /* These are special -- they create or delete GL resources that can live
     * in a shared namespace.  In DEBUG, we wrap these calls so that we can
     * check when we have something that failed to do cleanup at the time the
     * final context is destroyed.
     */

    typedef GLuint (GLAPIENTRY * PFNGLCREATEPROGRAMPROC) (void);
    PFNGLCREATEPROGRAMPROC priv_fCreateProgram;
    typedef GLuint (GLAPIENTRY * PFNGLCREATESHADERPROC) (GLenum type);
    PFNGLCREATESHADERPROC priv_fCreateShader;
    typedef void (GLAPIENTRY * PFNGLGENBUFFERSPROC) (GLsizei n, GLuint* buffers);
    PFNGLGENBUFFERSPROC priv_fGenBuffers;
    typedef void (GLAPIENTRY * PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures);
    PFNGLGENTEXTURESPROC priv_fGenTextures;
    typedef void (GLAPIENTRY * PFNGLGENFRAMEBUFFERS) (GLsizei n, GLuint* ids);
    PFNGLGENFRAMEBUFFERS priv_fGenFramebuffers;
    typedef void (GLAPIENTRY * PFNGLGENRENDERBUFFERS) (GLsizei n, GLuint* ids);
    PFNGLGENRENDERBUFFERS priv_fGenRenderbuffers;

    typedef void (GLAPIENTRY * PFNGLDELETEPROGRAMPROC) (GLuint program);
    PFNGLDELETEPROGRAMPROC priv_fDeleteProgram;
    typedef void (GLAPIENTRY * PFNGLDELETESHADERPROC) (GLuint shader);
    PFNGLDELETESHADERPROC priv_fDeleteShader;
    typedef void (GLAPIENTRY * PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint* buffers);
    PFNGLDELETEBUFFERSPROC priv_fDeleteBuffers;
    typedef void (GLAPIENTRY * PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint* textures);
    PFNGLDELETETEXTURESPROC priv_fDeleteTextures;
    typedef void (GLAPIENTRY * PFNGLDELETEFRAMEBUFFERS) (GLsizei n, const GLuint* ids);
    PFNGLDELETEFRAMEBUFFERS priv_fDeleteFramebuffers;
    typedef void (GLAPIENTRY * PFNGLDELETERENDERBUFFERS) (GLsizei n, const GLuint* ids);
    PFNGLDELETERENDERBUFFERS priv_fDeleteRenderbuffers;

public:
#ifndef DEBUG
    GLuint GLAPIENTRY fCreateProgram() {
        return priv_fCreateProgram();
    }

    GLuint GLAPIENTRY fCreateShader(GLenum t) {
        return priv_fCreateShader(t);
    }

    void GLAPIENTRY fGenBuffers(GLsizei n, GLuint* names) {
        priv_fGenBuffers(n, names);
    }

    void GLAPIENTRY fGenTextures(GLsizei n, GLuint* names) {
        priv_fGenTextures(n, names);
    }

    void GLAPIENTRY fGenFramebuffers(GLsizei n, GLuint* names) {
        priv_fGenFramebuffers(n, names);
    }

    void GLAPIENTRY fGenRenderbuffers(GLsizei n, GLuint* names) {
        priv_fGenRenderbuffers(n, names);
    }

    void GLAPIENTRY fDeleteProgram(GLuint program) {
        priv_fDeleteProgram(program);
    }

    void GLAPIENTRY fDeleteShader(GLuint shader) {
        priv_fDeleteShader(shader);
    }

    void GLAPIENTRY fDeleteBuffers(GLsizei n, GLuint *names) {
        priv_fDeleteBuffers(n, names);
    }

    void GLAPIENTRY fDeleteTextures(GLsizei n, GLuint *names) {
        priv_fDeleteTextures(n, names);
    }

    void GLAPIENTRY fDeleteFramebuffers(GLsizei n, GLuint *names) {
        priv_fDeleteFramebuffers(n, names);
    }

    void GLAPIENTRY fDeleteRenderbuffers(GLsizei n, GLuint *names) {
        priv_fDeleteRenderbuffers(n, names);
    }
#else
    GLContext *TrackingContext() {
        GLContext *tip = this;
        while (tip->mSharedContext)
            tip = tip->mSharedContext;
        return tip;
    }

    GLuint GLAPIENTRY fCreateProgram() {
        GLuint ret = priv_fCreateProgram();
        TrackingContext()->CreatedProgram(this, ret);
        return ret;
    }

    GLuint GLAPIENTRY fCreateShader(GLenum t) {
        GLuint ret = priv_fCreateShader(t);
        TrackingContext()->CreatedShader(this, ret);
        return ret;
    }

    void GLAPIENTRY fGenBuffers(GLsizei n, GLuint* names) {
        priv_fGenBuffers(n, names);
        TrackingContext()->CreatedBuffers(this, n, names);
    }

    void GLAPIENTRY fGenTextures(GLsizei n, GLuint* names) {
        priv_fGenTextures(n, names);
        TrackingContext()->CreatedTextures(this, n, names);
    }

    void GLAPIENTRY fGenFramebuffers(GLsizei n, GLuint* names) {
        priv_fGenFramebuffers(n, names);
        TrackingContext()->CreatedFramebuffers(this, n, names);
    }

    void GLAPIENTRY fGenRenderbuffers(GLsizei n, GLuint* names) {
        priv_fGenRenderbuffers(n, names);
        TrackingContext()->CreatedRenderbuffers(this, n, names);
    }

    void GLAPIENTRY fDeleteProgram(GLuint program) {
        priv_fDeleteProgram(program);
        TrackingContext()->DeletedProgram(this, program);
    }

    void GLAPIENTRY fDeleteShader(GLuint shader) {
        priv_fDeleteShader(shader);
        TrackingContext()->DeletedShader(this, shader);
    }

    void GLAPIENTRY fDeleteBuffers(GLsizei n, GLuint *names) {
        priv_fDeleteBuffers(n, names);
        TrackingContext()->DeletedBuffers(this, n, names);
    }

    void GLAPIENTRY fDeleteTextures(GLsizei n, GLuint *names) {
        priv_fDeleteTextures(n, names);
        TrackingContext()->DeletedTextures(this, n, names);
    }

    void GLAPIENTRY fDeleteFramebuffers(GLsizei n, GLuint *names) {
        priv_fDeleteFramebuffers(n, names);
        TrackingContext()->DeletedFramebuffers(this, n, names);
    }

    void GLAPIENTRY fDeleteRenderbuffers(GLsizei n, GLuint *names) {
        priv_fDeleteRenderbuffers(n, names);
        TrackingContext()->DeletedRenderbuffers(this, n, names);
    }

    void THEBES_API CreatedProgram(GLContext *aOrigin, GLuint aName);
    void THEBES_API CreatedShader(GLContext *aOrigin, GLuint aName);
    void THEBES_API CreatedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API CreatedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API CreatedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API CreatedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API DeletedProgram(GLContext *aOrigin, GLuint aName);
    void THEBES_API DeletedShader(GLContext *aOrigin, GLuint aName);
    void THEBES_API DeletedBuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API DeletedTextures(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API DeletedFramebuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);
    void THEBES_API DeletedRenderbuffers(GLContext *aOrigin, GLsizei aCount, GLuint *aNames);

    void SharedContextDestroyed(GLContext *aChild);
    void ReportOutstandingNames();

    struct NamedResource {
        NamedResource()
            : origin(nsnull), name(0), originDeleted(PR_FALSE)
        { }

        NamedResource(GLContext *aOrigin, GLuint aName)
            : origin(aOrigin), name(aName), originDeleted(PR_FALSE)
        { }

        GLContext *origin;
        GLuint name;
        PRBool originDeleted;

        // for sorting
        bool operator<(const NamedResource& aOther) const {
            if (intptr_t(origin) < intptr_t(aOther.origin))
                return true;
            if (name < aOther.name)
                return true;
            return false;
        }
        bool operator==(const NamedResource& aOther) const {
            return origin == aOther.origin &&
                name == aOther.name &&
                originDeleted == aOther.originDeleted;
        }
    };

    nsTArray<NamedResource> mTrackedPrograms;
    nsTArray<NamedResource> mTrackedShaders;
    nsTArray<NamedResource> mTrackedTextures;
    nsTArray<NamedResource> mTrackedFramebuffers;
    nsTArray<NamedResource> mTrackedRenderbuffers;
    nsTArray<NamedResource> mTrackedBuffers;
#endif
    
};

inline void
GLDebugPrintError(GLContext* aCx, const char* const aFile, int aLine)
{
    GLenum err = aCx->fGetError();
    if (err) {
        printf_stderr("GL ERROR: 0x%04x at %s:%d\n", err, aFile, aLine);
    }
}

#ifdef DEBUG
#  define DEBUG_GL_ERROR_CHECK(cx) mozilla::gl::GLDebugPrintError(cx, __FILE__, __LINE__)
#else
#  define DEBUG_GL_ERROR_CHECK(cx) do { } while (0)
#endif

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

#endif /* GLCONTEXT_H_ */
