OpenGL Extensions

OpenGL Extensions

OpenGL Extensions


In this article I will talk about OpenGL extensions. Every extension is defined by an extension specification. I will explain how to read the extension specification so that you will know how to use the extensions. I will also show how you can check for the existence of extensions and how to initialize extensions in your own source code. I will show how you can use GLEW to query and use extensions with very little extra effort.
I assume the reader is familiar with the C++ programming language. If you want to know how to start programming in OpenGL, refer to my previous article titled Introduction to OpenGL.

Introduction

Extensions in OpenGL are the mechanism to introduction new features and techniques into the OpenGL programming API. The DirectX API however releases regular updates to the SDK which exposes new features and new technology to the graphics programmer. This requires the developer to download the latest DirectX SDK for every new release if you want to target the new features of the API. OpenGL on the other hand delivers new technology and features through extension specifications.

Vendors (like NVIDIA and AMD) must implement the extension specifications in their driver software that they deliver with their products. When you update your driver software, you will usually receive a new version of the OpenGL32.dll (on Windows) which will be loaded dynamically by your OpenGL applications. Your application can then query the DLL to find out what functions and features are supported. You must obtain pointers to functions that exist in the DLL in order to use the extension’s features.

In this article, I will explain how to read and understand the extension specifications so that you can use the extensions in your own applications.

Extension Specifications

All extensions must be registered with the Khronos working group. A listing of all of the accepted extensions can be found on the OpenGL extension registry: http://www.opengl.org/registry/. On the OpenGL registry, you will find all OpenGL core specification documents as well as the extension specification documentation for every extension.

The OpenGL extension specification defines several standard sections which are included in every specification. There are more sections in the extension specification than what I describe here. A few of the more interesting sections are:

  • Name: The formal name of the extension.
  • Name Strings: The name that defines the extension when querying for extension support using the appropriate query functions.
  • Number: The numerical identifier for the extension.
  • Dependencies: Lists of the OpenGL specification version that this extension was built against as well as any other extensions that this extension requires support for.
  • Overview: This section defines what the extension should do and how to use it.
  • New Procedures and Functions: All of the procedures and functions that are supported by this extension.
  • New Types: All the new types that are defined by this extension.
  • New Tokens: All of the new tokens that are defined by this extension.

Name

The Name section defines the formal name of the extension. The name must begin with a prefix which indicates the organization that is responsible for maintaining the extension. The prefix must be in all-caps. The prefix is followed by the “body” of the extension name. The prefix and the body are separated by an underscore “_” character and each word in the body are also separated by an underscore character.

An example of a valid extension name is:

PREFIX_extension_name

Their are 4 tiers that an extension can be in: single vendor, multiple vendor, ARB approved, and core. It is possible that an extension with the same name may appear in different tiers but there may be subtle differences between the two similar extensions that you should be aware of.

Single Vendor

An extension that is supported by a specific vendor is considered a vendor extension. A vendor extension is prefixed by the unique vendor code that defines the specific vendor.

The following table defines the the different vendor codes.

Extension Prefix Extension Vendor
NV NVIDIA Corporation.
NVX NVIDIA Corporation. Experimental extension.
AMD Advanced Micro Devices.
ATI ATI Technologies, Inc.
3DLABS 3DLABS, Inc.
SUN Sun Microsystems, Inc.
SGI Silicon Graphics, Inc.
SGIX Silicon Graphics, Inc. Experimental extension.
SGIS Silicon Graphics, Inc. Experimental extension.
INTEL Intel Corporation.
3DFX 3dfx Interactive.
IBM International Business Machines Corporation, or simply IBM.
MESA The Mesa 3D Graphics Library.
GREMEDY Graphic Remedy, Ltd.
OML Khronos Group, Inc. API: OpenML®
OES Khronos Group, Inc. API: OpenGL® ES
PGI Portland Group Inc.
I3D Intense3D, now 3DLABS Inc.
INGR Intergraph Corporation.
MTX Matrox Electronic Systems Ltd.

Some examples of extensions that are vendor specific are:

  • NV_fog_distance
  • NV_path_rendering
  • AMD_pinned_memory

Multiple Vendor

When multiple vendors want to jointly implement an extension, the EXT prefix is used to identify the extension.

Some examples of multi-vendor extensions are:

  • EXT_texture3D
  • EXT_histogram
  • EXT_convolution

ARB Approved

The OpenGL Architecture Review Board can also promote vendor or multi-vendor extensions into the generally accepted category of extensions. In this case, the extension will receive the ARB prefix. Extensions that get promoted to the ARB accepted extension will generally become core extensions in the next release of the OpenGL specification.

Some examples of ARB approved extensions are:

  • ARB_vertex_buffer_object
  • ARB_pixel_buffer_object
  • ARB_framebuffer_object

Core

In most cases, the ARB approved extensions will be promoted into the core API in the next OpenGL specification. In this case, the functions, types, and tokens that are part of the extension specification will be integrated into the OpenGL core specification and the ARB suffix will be dropped from the symbol’s names. It is a requirement that the ARB extension mechanism still be supported for ARB extensions that have been promoted to the core API so you can still query the extension support using the ARB extension strings and continue to use the ARB suffix on the functions, types, and tokens.

For example, the ARB_vertex_buffer_object extension has been integrated into the core specification in OpenGL 1.5. So using the extension specification, you can still query for the existence of the GL_ARB_vertex_buffer_object extension and you will use the glGenBufferARB, glBindBufferARB, glBufferDataARB, etc… functions but if the graphics adapter is guaranteed to support the OpenGL 1.5 specification, you can also query for the core versions of these functions (glGenBuffer, glBindBuffer, glBufferData, etc…) without the ARB suffix.

As far as I can tell, the only way to check if an extension has become part of the core specification is to read the core specification for version of OpenGL you want to target. In the final appendices of the OpenGL core specification documentation, you will find lists of extensions that have been promoted to the core in each OpenGL release.

Name Strings

Some extensions can apply to several API’s. The extension strings append a prefix to the extension name that also qualifies the API the extension is used in. The name strings is also the string that you will use to query for the existence of the extension in the specific API.

The following table lists the extension prefixes that are used to identify the API that the extension applies to:

Extension API Query Function Prefix
GL and ES glGetString GL_
GLU gluGetString GLU_
GLX glXQueryExtensionsString GLX_
EGL eglQueryString EGL_
WGL glGetString or wglGetExtensionsStringEXT WGL_

For example, the ARB_vertex_buffer_object extension defines both GL_ARB_vertex_buffer_object and the GLX_ARB_vertex_buffer_object name strings.

Using the name string to query for extension support will be shown in this article.

Number

Each extension is also assigned a unique extension number. The extension number is primarily used for documentation purposes but it is also a way to indicate order of dependencies. If an extension is dependent on another extension in the specification, the dependent extension will have a lower number than the depending extension.

Knowing the extension number will also let you quickly find the extension specification in the extension registry as extensions are listed in numerical order.

As an example, the GL_ARB_vertex_buffer_object extension is number 28.

Dependencies

The dependencies section lists the oldest version of the OpenGL (or OpenGL ES) specification for which the extension can be implemented against. In addition to the version of the specification, the extension specification must document the other extensions that this extension relies on.

Overview

Probably the most significant section of the extension specification is the overview. This section must describe what the extension does as well as how to use the extension. It may also explain what problems this extension tries to resolve and the rationale behind the inception of the extension.

New Procedures and Functions

This section will list all of the new functions that are exported by this extension. Each new function must be suffixed by either the vendor code (NV, ATI, AMD), the multi-vendor extension code (EXT), or the Architecture Review Board code (ARB). For example, the ARB_vertex_buffer_object extension exports the following functions:

  • glBindBufferARB
  • glDeleteBuffersARB
  • glGenBuffersARB
  • glIsBufferARB
  • glBufferDataARB
  • glBufferSubDataARB
  • glGetBufferSubDataARB
  • glMapBufferARB, glxMapBufferARB
  • glUnmapBufferARB
  • glGetBufferParameterivARB
  • glGetBufferPointervARB

When the extension gets moved to the core, the ARB suffix is removed from the function names.

New Types

In some cases, an extension may introduce new types. The specification must provide enough information to define the new types using common C-language binding definitions.

For example, a new type with the name GLhandleARB might be defined in the specification like this:

New Types

    typedef unsigned int GLhandleARB;

New Tokens

Tokens are the constant enumeration values that are used in procedures and functions where a constant type or a bitfield pattern is expected. These types are usually used in places where a GLenum or GLint values are expected.

New tokes are usually separated in the extension specification based on the procedures and parameters that accept them.

For example, you may find the following in the ARB_vertex_buffer_object extension specification:

New Tokens

    Accepted by the <pname> parameter of GetBooleanv, GetIntegerv,GetFloatv, and GetDoublev:
        ARRAY_BUFFER_BINDING_ARB                     0x8894
        ELEMENT_ARRAY_BUFFER_BINDING_ARB             0x8895
        VERTEX_ARRAY_BUFFER_BINDING_ARB              0x8896
        ...

Querying Extensions

Prior to OpenGL 3.0 the method for querying for supported extensions required the use of the glGetString method passing GL_EXTENSIONS as the only parameter. The function will return a pointer to a GLubyte array (which can be cast to const char* type so it can be treated like a string). The string returned by this function is a space-delimited list of all supported extensions. It is the responsibility of the application programmer to correctly parse this string to determine if the requested extension is available.

However, this method seemed to cause some confusion especially when the extension being requested is the sub-string of another extension. For example, if one was querying for the GL_EXT_pixel_transform extension, an improperly formatted query may return a false positive if the GL_EXT_pixel_transform_color_table is listed even if GL_EXT_pixel_transform is not.

As of OpenGL 3.0, the accepted method for querying for supported extensions is the new glGetStringi method. Since this method was introduced in OpenGL 3.0 it will not be immediatly available and you must retrieve a pointer to the glGetStringi method from the vendor supplied OpenGL library.

Extension Declarations

Extension declarations are separated into three different header files depending on the API. Since these header files are updated for every new extension that is introduced, you will not find them in the default include paths together with the “gl.h” header file. You must download these files manually from the OpenGL registry and include them in your project’s include paths.

  • glext.h: The core and ARB extension functions, types, and tokens. These extensions are supported on all platforms.
  • glxext.h: Extension functions, types, and tokens specific to the X-windows platform.
  • wglext.h: Extension functions, types, and tokens specific to the Windows platform.

The glGetStringi method declaration is in the glext.h header file.

Function Definition

We must first define a pointer to a function that will be used to store the entry-point to the glGetStringi function that is exported from the library.

#include <GL/glext.h>

PFNGLGETSTRINGIPROC glGetStringi = NULL;

The PFNGLGETSTRINGIPROC symbol is a typedef of a function pointer that has the signature of the glGetStringi method. The glGetStringi variable will be used to store the pointer to the function defined in the OpenGL library.

Get the Function Pointer

To retrieve the pointer to the function, we will use the wglGetProcAddress method on the Windows platform, or the glXGetProcAddress on the X-windows platform.

To get the pointer to the “glGetStringi” method, you would do this on the Windows platform:

    glGetStringi = (PFNGLGETSTRINGIPROC)wglGetProcAddress("glGetStringi");

or this on the X-windows platform:

    glGetStringi = (PFNGLGETSTRINGIPROC)glXGetProcAddress("glGetStringi");

If the requested function is not exported from the OpenGL library supplied by your hardware vendor, then a NULL pointer is returned by these functions.

Query the Supported Extensions

We can define a method that will return true if a particular extension is supported on the current system, or false if it is not. Let’s see how we might do this.

First, we’ll define a function that can be used to split the extension string into a list of extension tokens.

// check to see if a particular character is in our delimiter string
inline bool IsDelimeter( const char c, const std::string& delimeters )
{
    return delimeters.find_first_of(c) != std::string::npos;
}

// Tokenize a string into a vector of tokens
inline std::vector<std::string> TokenizeString( const std::string& str, const std::string& delimiters = " \t\n\r" )
{
    std::vector<std::string> tokens;
    std::string token;
    const char* c_string = str.c_str();

    while ( *c_string != '\0' )
    {
        token.clear();
        while ( *c_string != '\0' && !IsDelimeter(*c_string, delimiters) )
        {
            token.push_back( *c_string );
            c_string++;
        }

        if ( token.length() > 0 )
        {
            tokens.push_back( token );
        }

        // Eat-up delimeters
        while ( *c_string != '\0' && IsDelimeter(*c_string, delimiters) ) c_string++;
    }

    return tokens;
}

The IsDelimeter function simply checks to see if the character c is one of the delimiters in the delimeter string.

The TokenizeString function will split the string str into a list of tokens. The delimiters in the original string will be discarded by this function.

With the TokenizeString utility function, we can query the extension string from OpenGL and split all the extensions into a list of tokens to check for the existence of a particular extension.

bool IsSupported( const std::string& extension )
{
    std::vector<std::string> extensions;

    // Fall-back to the pre-3.0 method for querying extensions.
    if ( glGetStringi == NULL )
    {
        std::string extensionString = (const char*)glGetString( GL_EXTENSIONS );
        extensions = TokenizeString( extensionString, " " );
    }
    else
    {
        // Use the post-3.0 method for querying extensions
        GLint numExtensions = 0;
        glGetIntegerv( GL_NUM_EXTENSIONS, &numExtensions );
        for( int i = 0; i < numExtensions; ++i )
        {
            std::string extensionString = (const char*)glGetStringi( GL_EXTENSIONS, i );
            extensions.push_back(extensionString);
        }
    }
    
    return std::find(extensions.begin(), extensions.end(), extension ) != extensions.end();
}

The IsSupported method accepts the named string of an extension you want to query for. This must be the named string as it appears in the “Name Strings” section of the extension specification.

This function provides a fall-back method if the glGetStringi method was not exported from the OpenGL library. In this case, the glGetString method is used to query the space-delimited string of supported extensions. The TokenizeString method (not shown here) will split this string into a vector of extension name strings.

If the glGetStringi is not NULL then we can use the new method for querying the extension strings. In this case, we must first query the number of supported extensions using the glGetIntegerv method and the GL_NUM_EXTENSIONS token (this token is define in the glext.h header file).

We then loop from 0 to (GL_NUM_EXTENSIONS – 1) querying the extension names strings using the glGetStringi method.

Once we have a vector containing all of the extension name strings, we can perform a linear search over the vector to determine if our requested extension is present. If it is, then the extension is supported and we can safely request pointers to all of the functions and procedures defined in the extension specification.

Using GLEW

The method shown in the previous section seems very tedious and complicated. There must be a way to avoid creating all of this boiler-plate code over again in every new OpenGL demo you want to create? Introducing GLEW!

GLEW is the OpenGL Extension Wrangler Library. GLEW is a cross-platform, open-source, C/C++ extension loading library.

Installing GLEW

On the GLEW download page, there are several options for obtaining GLEW:

  • Source: You can download the source code as a ZIP or TGZ file if you want to build GLEW into your own projects. If you on Windows you will probably want the ZIP file and if you are using a Unix/Linux you probably want the TGZ file.
  • Binaries: GLEW provides pre-built Windows 32-bit and 64-bit binaries. You should be aware that even if you are running on a 64-bit operating system, you will want to use the binaries that apply to the platform you are targeting when you compile your application. If your application targets the Win32 platform, you will want the 32-bit pre-compiled version of GLEW. If your application targets the x64 platform, then you will want the 64-bit pre-compiled version of GLEW.

When you install GLEW, I don’t recommend you follow the instructions on the GLEW webpage to install GLEW into the default include paths for Visual Studio unless you don’t plan on distributing your application. If however you want to share your source code for your OpenGL demo, you will want to make sure that whoever you are sharing your demo with can also access the same version of GLEW that you used to create the demo without requiring them to download GLEW too. To do that, you will want to distribute GLEW together with your project’s source files. I recommend you unzip the GLEW source files and binaries to a folder that is accessible relative to your project files. For example, I usually create a sub-directory called “externals” at the same location as my Visual Studio solution file where I extract all of the 3rd-party include files, source code, and binaries that are needed by all of the projects in the solution.

With GLEW extracted to a folder relative to your project, you need to add the GLEW “include” folder in your project’s “Additional Include Paths” and the GLEW “lib” path to your project’s “Additional Library Directories”. When you include search paths in these project properties, you should always use paths that are defined relative to your project’s folder. This way the settings and the file structure can be easily distributed.

If you are linking against the static library, you also need to specify glew32s.lib in the “Additional Dependencies” option and if you plan on loading the glew32.dll file at runtime, then you should link against the glew32.lib file.

Including GLEW Headers

GLEW can either be statically linked to your application or dynamically linked using the glew32.dll library. I prefer to use static linking for GLEW.

To use the static library you must define the GLEW_STATIC macro in your code before you include the glew.h header file.

#define GLEW_STATIC
#include <gl/glew.h>

This is the only header file that you need to include to get access to all of the extensions that are supported by GLEW. The GLEW header will also include the “gl.h” and “glu.h” header files, so you don’t need to include those header files explicitly when you are using GLEW.

Initialize GLEW

GLEW can be initialized only after an OpenGL context has been created. If you are using GLUT or freeGLUT, this only happens after the window has been created with the glutCreateWindow function.

To initialize GLEW, you simply use the glewInit function.

if ( glewInit() != GLEW_OK )
{
    std::cout << "Failed to initilalize GLEW!" << std::endl;
    exit(-1);
}

If glewInit returns GLEW_OK then initialization succeeded and you have access to all of the OpenGL core functionality and extensions.

You can start using the extension functionality right away, but it is usually a good idea to check for the extension support before using it. You may have support for all of the extensions you plan to use in your demo, but someone else running your demo may not.

Query Supported Extensions

GLEW defines a set of variables that can be used directly in your code to query for OpenGL version support or extension support. To check if a particular version of OpenGL is supported, you can use the following code:

if (GLEW_VERSION_1_3)
{
  // Core profile OpenGL 1.3 is supported.
}

Using this same mechanism, you can also check for a particular extension:

if (GLEW_ARB_vertex_program)
{
  // The ARB_vertex_program extension is supported.
}

These may look like constant macro definitions that are resolved at compile-time, but in fact they are actually look-up functions that are resolved at run-time.

You can also check for support of multiple extensions and core profile versions using the glewIsSupported function. This function takes a space-delimited string of extensions to check for and returns true if all of the extensions are supported.

if (glewIsSupported("GL_VERSION_1_4  GL_ARB_point_sprite"))
{
  // OpenGL 1.4 core profile and point sprite extension supported.
}

Using the glewIsSupported function we supply the extension name strings as they appear in the extension specification (without the GLEW prefix).

If everything is okay, you can simply use any of the functions, types, and tokens defined in the specification. No further work is required to get access to the function pointers defined by each extension, GLEW does that part for you.

Using the Extensions

With GLEW initialized and support for the required extensions have been confirmed, you can simply use any of the functions, types and tokens that are defined in the extension specification.

For example, to use the glGenBuffersARB function that is defined in the ARB_vertex_buffer_object extension, you would simply invoke it:

GLuint vboID = 0;
glGenBuffersARB( 1, &vboID );

Conclusion

In this article I discussed how to read the extension specification. I also showed how you can get access to the functions and procedures defined by the extension specification.

Using GLEW we’ve seen how we can simplify the process of checking for extension support and getting access to the procedures and functions that are exported by the extensions.

In a later article, I will show how you can use the ARB_vertex_buffer_object extension to enable storing vertex information in GPU memory for faster rendering.

References

Beginning OpenGL Game Programming - Second Edition (2009)


Benstead, Luke with Astle, D. and Hawkins, K. (2009). Beginning OpenGL Game Programming. 2nd. ed. Boston, MA: Course Technology.
OpenGL® Registry: http://www.opengl.org/registry/
The OpenGL Extension Wrangler: http://glew.sourceforge.net/
OpenGL Extensions (wiki): http://www.opengl.org/wiki/OpenGL_Extensions

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>