GL Feature Requirements

What is this?

With GL, it's possible to write one code base that targets a very wide range of platforms, which is done by dynamically querying the hardware's capabilities. However, it's hard to know what are all the different ways to access a feature and what needs to be checked before using a feature. This web page attempts to keep track of all these kinds of decisions to make it easy to write highly compatible code.

From a high level perspective, each GL feature has different requirements to be available based on:

  1. The windowing system interface. (eg: WGL for Windows, CGL for Mac OS X, GLX for X11, EGL for embedded systems)
  2. The API in use. (eg: OpenGL, OpenGL ES, WebGL)
  3. The version of the API in use. (eg: GL 4.3, GL ES 3.0, WebGL 2.0, etc.)
  4. The extensions available. (eg: GL_ARB_texture_non_power_of_two, GL_KHR_debug, etc.)

Furthermore, there exists quirks and bugs in GL implementations that will be documented here as they become known.

License

This is free and unencumbered software released into the public domain.

Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.

In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

For more information, please refer to http://unlicense.org/

Bugs

It's your responsibility to fact-check the information on this page if you're doing serious work. If you do find a mistake on this page, please help everybody by opening an issue or submitting a pull request on the GitHub repository for the website. You can also contact me by e-mail, which is available on my GitHub page.

Other resources

There are lots of other websites that can help with OpenGL compatibility. Consider checking these out:

Creating an OpenGL context

TODO

Dynamically querying an entry point

One great thing about OpenGL is its ability to dynamically query the features supported by the implementation in use, which makes it possible to target a wide variety of different OpenGL implementations in one code base. This is partly made possible through the concept of entry points, which act as channels of communication between your application and the GL driver. In practice, these entry points are simple C functions, and they are the bread and butter of any OpenGL application. For example, the commonly used function glDrawArrays is an entry point that allows you to tell the GL to draw some geometry.

The available entry points depend on the capabilities of the GL implementation made available to your application. Therefore, in order to write highly compatible applications, we should be only using entry points that we are certain are available, which can be done by following the guides in this very article. When you are certain that an entry point is available, you can query it through a xxxGetProcAddress function, which is slightly different between different platforms. These query functions always take a string as input (the name of the entry point) and return a pointer to a function that can be used as an entry point. Note that a GL context must be made current on the thread that is querying these entry points.

The xxxGetProcAddress functions typically return a type-less void pointer (in C: void*) which must be casted to a correct function pointer type in order to be called. There exist typedefs for these function pointer types in Khronos' OpenGL Registry (namely, in the glext.h, glcorearb.h, glxext.h, and wglext.h headers availble for download on that page.) Similar headers are available for OpenGL ES from the Khronos OpenGL ES Registry.

Example Code

For convenience for the rest of this article, this information will be used to define a GetProc function that makes it easy to query for entry points:

    // somewhere previous
    #ifdef _WIN32
        HMODULE hModuleOpenGL32 = LoadLibrary(TEXT("opengl32.dll"));
    #endif

    void* GetProc(const char* procname)
    {
    #ifdef _WIN32
        void* proc = wglGetProcAddress(procname);
        if (proc == NULL)
        {
            proc = GetProcAddress(hModuleOpenGL32, procname);
        }
        return proc;
    #elif defined(EMSCRIPTEN)
        return eglGetProcAddress(procname);
    #else
        // TODO: More platforms!
    #endif
    }
What follows is an example use of the above GetProc function:
    #include <GL/glcorearb.h>

    // somewhere later...
    PFNGLGETSTRINGPROC glGetString = GetProc("glGetString");
    const char* renderer = glGetString(GL_RENDERER);
    printf("GL_RENDERER: %s\n", renderer);
If this is all too ugly for you, you can use one of the many OpenGL loading libraries.

Determining the API in use and its version

Before doing almost anything else, you need to figure out what API you are using and what version it is. This is done using the output of glGetString(GL_VERSION), which is interpreted as follows:

Thus, it is necessary to check if the string begins with "WebGL" or "OpenGL ES" or neither in order to determine what API is currently in use. The version of the API in use can then be determined by parsing the major_number.minor_number from the string.

Example code

The three OpenGL APIs will be described using the following enum:

    enum GLApi
    {
        GLApi_OpenGL,
        GLApi_OpenGLES,
        GLApi_WebGL
    };

With this code, we can determine the API and its version:

    const char* version = glGetString(GL_VERSION);
    if (strncmp(version, "WebGL ", 6) == 0)
    {
        myApi = GLApi_WebGL;
        version += 6;
    } 
    else if (strncmp(version, "OpenGL ES ", 10) == 0) 
    {
        myApi = GLApi_OpenGLES;
        version += 10;
    } 
    else
    {
        myApi = GLApi_OpenGL;
    }
    
    sscanf(version, "%d.%d", &myMajorVersion, &myMinorVersion);
For convenience, this information is used to define a VersionCheck function that makes it easy to check for API versions:
    int VersionCheck(GLApi api, int major, int minor)
    {
        return myApi == api
               && (myMajorVersion > major ||
                  (myMajorVersion == major && myMinorVersion >= minor));
    }

Determining the available extensions

There are two ways to get the list of extensions depending on the version of the GL: glGetStringi(GL_EXTENSIONS, i) and glGetString(GL_EXTENSIONS). The first method allows you to iterate over individual extensions by their index, while the second method returns a big string that stores the names of the supported extensions separated by spaces. The first method is more convenient, but only available in more modern implementations. The second method is slightly inconvenient because it requires parsing the extension string.

It should be noted that WebGL has its own specialized functions for checking a specific extension or reading the list of extensions, which are getExtension and getSupportedExtensions, respectively. If you are using WebGL through emscripten, calls of glGetString(GL_EXTENSIONS) automatically get translated to those WebGL-specific calls.

Example code

What follows is a convenience function that checks whether an extension is supported or not.
    int ExtensionCheck(const char* extension)
    {
        if (VersionCheck(GLApi_OpenGL, 3, 0)
        ||  VersionCheck(GLApi_OpenGLES, 3, 0))
        {
            PFNGLGETINTEGERVPROC glGetIntegerv = GetProc("glGetIntegerv");
            PFNGLGETSTRINGIPROC glGetStringi = GetProc("glGetStringi");
            
            GLint numExtensions;
            glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
            for (GLint i = 0; i < numExtensions; i++)
            {
                const char* currExtension = glGetStringi(GL_EXTENSIONS, i);
                if (strcmp(extension, currExtension) == 0)
                {
                    return 1;
                }
            }
        }
        else if (VersionCheck(GLApi_OpenGL, 1, 0)
             ||  VersionCheck(GLApi_OpenGLES, 1, 0)
             ||  VersionCheck(GLApi_WebGL, 1, 0))
        {
            PFNGLGETSTRINGPROC glGetString = GetProc("glGetString");
            
            const char* extensions = glGetString(GL_EXTENSIONS);
            while (1)
            {
                const char* next = strchr(extensions, ' ');
                if ((next != NULL && strncmp(extension, extensions, next - extensions) == 0)
                ||  (next == NULL && strcmp(extension, extensions) == 0))
                {
                    return 1;
                }
                
                if (next == NULL) break;
                extensions = next + 1;
            }
        }
        
        return 0;
    }

GL_CLAMP_TO_EDGE

Did you know GL_CLAMP_TO_EDGE didn't always exist? Yeah, there was a point where pretty much everything was tiled, so people just used GL_REPEAT I suppose. It was originally exposed as GL_SGIS_texture_edge_clamp and quickly became a standard feature of GL implementations.

In the SGIS implementation, it was exposed as the constant GL_CLAMP_TO_EDGE_SGIS. Thankfully, this constant has the same value as the standard GL_CLAMP_TO_EDGE, so your code can just use GL_CLAMP_TO_EDGE without the SGIS prefix when this feature is supported.

Example code

    int clampToEdgeSupported = 0;
    if (VersionCheck(GLApi_OpenGL, 1, 2)
    ||  VersionCheck(GLApi_OpenGLES, 1, 0)
    ||  VersionCheck(GLApi_WebGL, 1, 0)
    || (VersionCheck(GLApi_OpenGL, 1, 0) && ExtensionCheck("GL_SGIS_texture_edge_clamp")))
    {
        clampToEdgeSupported = 1;
    }

GL_ARB_vertex_buffer_object

Vertex buffers are a pretty awesome feature. They allow you to allocate GPU memory and upload data to them up front, so you don't need to constantly re-submit your geometry every time you want to draw. Vertex buffers are so useful that they became widely available in GL pretty early on. However, reading back from buffers and mapping buffers are areas where OpenGL ES and WebGL fall behind.

Example code

    PFNGLBINDBUFFERPROC glBindBuffer = NULL;
    PFNGLDELETEBUFFERSPROC glDeleteBuffers = NULL;
    PFNGLGENBUFFERSPROC glGenBuffers = NULL;
    PFNGLISBUFFERPROC glIsBuffer = NULL;
    PFNGLBUFFERDATAPROC glBufferData = NULL;
    PFNGLBUFFERSUBDATAPROC glBufferSubData = NULL;
    PFNGLGETBUFFERSUBDATAPROC glGetBufferSubData = NULL;
    PFNGLMAPBUFFERPROC glMapBuffer = NULL;
    PFNGLUNMAPBUFFERPROC glUnmapBuffer = NULL;
    PFNGLGETBUFFERPARAMETERIVPROC glGetBufferParameteriv = NULL;
    PFNGLGETBUFFERPOINTERVPROC glGetBufferPointerv = NULL;
    if (VersionCheck(GLApi_OpenGL, 1, 5)
    ||  VersionCheck(GLApi_OpenGLES, 1, 1)
    ||  VersionCheck(GLApi_WebGL, 1, 0))
    {
        glBindBuffer = GetProc("glBindBuffer");
        glDeleteBuffers = GetProc("glDeleteBuffers");
        glGenBuffers = GetProc("glGenBuffers");
        glIsBuffer = GetProc("glIsBuffer");
        glBufferData = GetProc("glBufferData");
        glBufferSubData = GetProc("glBufferSubData");
        
        if (VersionCheck(GLApi_OpenGL, 1, 5))
        {
            glGetBufferSubData = GetProc("glGetBufferSubData");
            glMapBuffer = GetProc("glMapBuffer");
            glUnmapBuffer = GetProc("glUnmapBuffer");
            glGetBufferPointerv = GetProc("glGetBufferPointerv");
        }
        else if (VersionCheck(GLApi_OpenGLES, 1, 1)
             &&  ExtensionCheck("GL_OES_mapbuffer"))
        {
            glMapBuffer = GetProc("glMapBufferOES");
            glUnmapBuffer = GetProc("glUnmapBufferOES");
            glGetBufferPointerv = GetProc("glGetBufferPointervOES");
        }
        
        glGetBufferParameteriv = GetProc("glGetBufferParameteriv");
    }
    else if (VersionCheck(GLApi_OpenGL, 1, 0)
         &&  ExtensionCheck("GL_ARB_vertex_buffer_object"))
    {
        glBindBuffer = GetProc("glBindBufferARB");
        glDeleteBuffers = GetProc("glDeleteBuffersARB");
        glGenBuffers = GetProc("glGenBuffersARB");
        glIsBuffer = GetProc("glIsBufferARB");
        glBufferData = GetProc("glBufferDataARB");
        glBufferSubData = GetProc("glBufferSubDataARB");
        glGetBufferSubData = GetProc("glGetBufferSubDataARB");
        glMapBuffer = GetProc("glMapBufferARB");
        glUnmapBuffer = GetProc("glUnmapBufferARB");
        glGetBufferPointerv = GetProc("glGetBufferPointervARB");
    }

GL_ARB_map_buffer_range

TODO

GL_ARB_vertex_array_object

Storing the setup of your vertex data in a vertex array object is a commonly available extension on new-ish implementations. The interface for manipulating these vertex array objects is confusing because it uses so much global state, so I recommend taking the time to read the actual GL specs to really understand what state is being stored in a VAO and how to manipulate this state. (Protip: There are state tables at the end of GL specification documents.)

Sometimes using VAOs to store vertex array state is optional (and you can use a default global VAO instead), but some modern implementations require you to have a VAO bound if you want to make any draw calls. If you don't like this restriction, then just generate a single VAO and bind it at the start of your program!

Note that all the functions for actually manipulating VAO state come from other extensions. VAOs have become a bit of a chimera because of this.

Example code

    PFNGLBINDVERTEXARRAYPROC glBindVertexArray = NULL;
    PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = NULL;
    PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = NULL;
    PFNGLISVERTEXARRAYPROC glIsVertexArray = NULL;
    if (VersionCheck(GLApi_OpenGL, 3, 0)
    ||  VersionCheck(GLApi_OpenGLES, 3, 0)
    ||  VersionCheck(GLApi_WebGL, 2, 0)
    || (VersionCheck(GLApi_OpenGL, 1, 0) && ExtensionCheck("GL_ARB_vertex_array_object")))
    {
        // note: interestingly, GL_ARB_vertex_array_object entry points aren't defined with the ARB suffix.
        glBindVertexArray = GetProc("glBindVertexArray");
        glDeleteVertexArrays = GetProc("glDeleteVertexArrays");
        glGenVertexArrays = GetProc("glGenVertexArrays");
        glIsVertexArray = GetProc("glIsVertexArray");
    }
    else if (VersionCheck(GLApi_OpenGL, 1, 0)
         && ExtensionCheck("GL_APPLE_vertex_array_object"))
    {
        glBindVertexArray = GetProc("glBindVertexArrayAPPLE");
        glDeleteVertexArrays = GetProc("glDeleteVertexArraysAPPLE");
        glGenVertexArrays = GetProc("glGenVertexArraysAPPLE");
        glIsVertexArray = GetProc("glIsVertexArrayAPPLE");
    }
    else if ((VersionCheck(GLApi_OpenGLES, 1, 0) && ExtensionCheck("GL_OES_vertex_array_object"))
         ||  (VersionCheck(GLApi_WebGL, 1, 0) && ExtensionCheck("OES_vertex_array_object")))
    {
        glBindVertexArray = GetProc("glBindVertexArrayOES");
        glDeleteVertexArrays = GetProc("glDeleteVertexArraysOES");
        glGenVertexArrays = GetProc("glGenVertexArraysOES");
        glIsVertexArray = GetProc("glIsVertexArrayOES");
    }

GL_ARB_instanced_arrays

This extension made it possible to use share a vertex attribute between multiple consecutive instanced draws.

Example code

    PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = NULL;
    if (VersionCheck(GLApi_OpenGL, 3, 3)
    ||  VersionCheck(GLApi_OpenGLES, 3, 0)
    ||  VersionCheck(GLApi_WebGL, 2, 0))
    {
        glVertexAttribDivisor = GetProc("glVertexAttribDivisor");
    }
    else if (VersionCheck(GLApi_OpenGL, 1, 1)
         &&  ExtensionCheck("GL_ARB_instanced_arrays"))
    {
        glVertexAttribDivisor = GetProc("glVertexAttribDivisorARB");
    }
    else if (VersionCheck(GLApi_OpenGLES, 2, 0))
    {
        if (ExtensionCheck("GL_ANGLE_instanced_arrays"))
        {
            glVertexAttribDivisor = GetProc("glVertexAttribDivisorANGLE");
        }
        else if (ExtensionCheck("GL_NV_instanced_arrays"))
        {
            glVertexAttribDivisor = GetProc("glVertexAttribDivisorNV");
        }
        else if (ExtensionCheck("GL_EXT_instanced_arrays"))
        {
            glVertexAttribDivisor = GetProc("glVertexAttribDivisorEXT");
        }
    }
    else if (VersionCheck(GLApi_WebGL, 1, 0)
         &&  ExtensionCheck("ANGLE_instanced_arrays"))
    {
        glVertexAttribDivisor = GetProc("glVertexAttribDivisorANGLE");
    }

GL_ARB_vertex_attrib_binding

At some point, somebody thought it was a good idea to separate the format of attributes and the buffers they are bound to. This is arguably a good idea, but it makes a lot of state in VAOs weirdly redundant. (Namely, the "pointer" set from glVertexAttribPointer now becomes an arbitrary userdata pointer which you can set to whatever you want, since it now only has use in compatibility mode.) Take the time to really read and understand the specs if you want to use this feature.

Example code

    PFNGLBINDVERTEXBUFFERPROC glBindVertexBuffer = NULL;
    PFNGLVERTEXATTRIBFORMATPROC glVertexAttribFormat = NULL;
    PFNGLVERTEXATTRIBBINDINGPROC glVertexAttribBinding = NULL;
    PFNGLVERTEXBINDINGDIVISORPROC glVertexBindingDivisor = NULL;
    if (VersionCheck(GLApi_OpenGL, 4, 3)
    || (VersionCheck(GLApi_OpenGL, 4, 2) && ExtensionCheck("GL_ARB_vertex_attrib_binding"))
    ||  VersionCheck(GLApi_OpenGLES, 3, 1))
    {
        glBindVertexBuffer = GetProc("glBindVertexBuffer");
        glVertexAttribFormat = GetProc("glVertexAttribFormat");
        glVertexAttribBinding = GetProc("glVertexAttribBinding");
        glVertexBindingDivisor = GetProc("glVertexBindingDivisor");
    }

glBindVertexBuffers

This function allows you to make multiple glBindVertexBuffer calls at the time time.

Example code

    PFNGLBINDVERTEXBUFFERSPROC glBindVertexBuffers = NULL;
    if (VersionCheck(GLApi_OpenGL, 4, 4))
    {
        glBindVertexBuffers = GetProc("glBindVertexBuffers");
    }

GL_KHR_debug

This extension adds lots of debug functionality to GL, which is usually only available in debug contexts. Subsets of it are available if you support GL_[ARB|AMD]_debug_output. It should be noted that the AMD version of this extension uses a different signature for the debug callback. (In general, you should have a separate code path if you're using the AMD extension for debugging.) Also, GL_KHR_debug exposes significantly more features than the plain GL_[ARB|AMD]_debug_output extensions.

Example code

    PFNGLDEBUGMESSAGEENABLEAMDPROC glDebugMessageEnableAMD = NULL;
    PFNGLDEBUGMESSAGEINSERTAMDPROC glDebugMessageInsertAMD = NULL;
    PFNGLDEBUGMESSAGECALLBACKAMDPROC glDebugMessageCallbackAMD = NULL;
    PFNGLGETDEBUGMESSAGELOGAMDPROC glGetDebugMessageLogAMD = NULL;
    PFNGLDEBUGMESSAGECONTROLPROC glDebugMessageControl = NULL;
    PFNGLDEBUGMESSAGEINSERTPROC glDebugMessageInsert = NULL;
    PFNGLDEBUGMESSAGECALLBACKPROC glDebugMessageCallback = NULL;
    PFNGLGETDEBUGMESSAGELOGPROC glGetDebugMessageLog = NULL;
    PFNGLGETPOINTERVPROC glGetPointerv = NULL;
    PFNGLPUSHDEBUGGROUPPROC glPushDebugGroup = NULL;
    PFNGLPOPDEBUGGROUPPROC glPopDebugGroup = NULL;
    PFNGLOBJECTLABELPROC glObjectLabel = NULL;
    PFNGLGETOBJECTLABELPROC glGetObjectLabel = NULL;
    PFNGLOBJECTPTRLABELPROC glObjectPtrLabel = NULL;
    PFNGLGETOBJECTPTRLABEL glGetObjectPtrLabel = NULL;
    if (VersionCheck(GLApi_OpenGL, 4, 3)
    || (VersionCheck(GLApi_OpenGL, 1, 1) && ExtensionCheck("GL_KHR_debug")))
    {
        glDebugMessageControl = GetProc("glDebugMessageControl");
        glDebugMessageInsert = GetProc("glDebugMessageInsert");
        glDebugMessageCallback = GetProc("glDebugMessageCallback");
        glGetDebugMessageLog = GetProc("glGetDebugMessageLog");
        glGetPointerv = GetProc("glGetPointerv");
        glPushDebugGroup = GetProc("glPushDebugGroup");
        glPopDebugGroup = GetProc("glPopDebugGroup");
        glObjectLabel = GetProc("glObjectLabel");
        glGetObjectLabel = GetProc("glGetObjectLabel");
        glObjectPtrLabel = GetProc("glObjectPtrLabel");
        glGetObjectPtrLabel = GetProc("glGetObjectPtrLabel");
    }
    else if (VersionCheck(GLApi_OpenGL, 1, 1)
         &&  ExtensionCheck("GL_ARB_debug_output"))
    {
        glDebugMessageControl = GetProc("glDebugMessageControlARB");
        glDebugMessageInsert = GetProc("glDebugMessageInsertARB");
        glDebugMessageCallback = GetProc("glDebugMessageCallbackARB");
        glGetDebugMessageLog = GetProc("glGetDebugMessageLogARB");
        glGetPointerv = GetProc("glGetPointerv");
    }
    else if (VersionCheck(GLApi_OpenGL, 1, 1)
         &&  ExtensionCheck("GL_AMD_debug_output"))
    {
        glDebugMessageEnableAMD = GetProc("glDebugMessageEnableAMD");
        glDebugMessageInsertAMD = GetProc("glDebugMessageInsertAMD");
        glDebugMessageCallbackAMD = GetProc("glDebugMessageCallbackAMD");
        glGetDebugMessageLogAMD = GetProc("glDebugMessageLogAMD");
    }
    else if (VersionCheck(GLApi_OpenGLES, 1, 0)
         &&  ExtensionCheck("GL_KHR_debug"))
    {
        glDebugMessageControl = GetProc("glDebugMessageControlKHR");
        glDebugMessageInsert = GetProc("glDebugMessageInsertKHR");
        glDebugMessageCallback = GetProc("glDebugMessageCallbackKHR");
        glGetDebugMessageLog = GetProc("glGetDebugMessageLogKHR");
        glGetPointerv = GetProc("glGetPointervKHR");
        glPushDebugGroup = GetProc("glPushDebugGroupKHR");
        glPopDebugGroup = GetProc("glPopDebugGroupKHR");
        glObjectLabel = GetProc("glObjectLabelKHR");
        glGetObjectLabel = GetProc("glGetObjectLabelKHR");
        glObjectPtrLabel = GetProc("glObjectPtrLabelKHR");
        glGetObjectPtrLabel = GetProc("glGetObjectPtrLabelKHR");
    }

GL_ARB_texture_non_power_of_two

Not all OpenGL implementations can use NPOT textures freely: Some can't use them at all, some can use them but not repeat or mipmap them, while more modern implementations allow all operations on NPOT textures.

Example code

This is pretty much adapted right out of Aras' awesome blog post on this topic, which you should read if you want more information about NPOT texture support.

    // See Aras' blog post to understand what these classifications mean.
    // Can do convenient stuff, like "if (npotSupport >= NPOTSupport_Limited)"
    enum NPOTSupport
    {
        NPOTSupport_None,
        NPOTSupport_Limited,
        NPOTSupport_Full
    };
    
    NPOTSupport npotSupport = NPOTSupport_None;
    if (VersionCheck(GLApi_OpenGL, 3, 0)
    ||  VersionCheck(GLApi_OpenGLES, 3, 0)
    ||  VersionCheck(GLApi_WebGL, 2, 0))
    {
        npotSupport = NPOTSupport_Full;
    }
    else if (VersionCheck(GLApi_OpenGL, 2, 0)
         || (VersionCheck(GLApi_OpenGL, 1, 0) && ExtensionCheck("GL_ARB_texture_non_power_of_two")))
    {
        // TODO: Attempt to find if a DX10+ level GPU is running to use full NPOT support.
        // See Aras' blog post for some heuristics.
        npotSupport = NPOTSupport_Limited;
    }
    else if (VersionCheck(GLApi_OpenGLES, 1, 0))
    {
        if (ExtensionCheck("GL_ARB_texture_non_power_of_two")
        ||  ExtensionCheck("GL_OES_texture_npot"))
        {
            npotSupport = NPOTSupport_Full;
        }
        else if (ExtensionCheck("GL_APPLE_texture_2D_limited_npot")
             ||  ExtensionCheck("GL_IMG_texture_npot"))
        {
            npotSupport = NPOTSupport_Limited;
        }
    }
    else if (VersionCheck(GLApi_WebGL, 1, 0))
    {
        npotSupport = NPOTSupport_Limited;
    }