Using Extensions in OpenGL
All of the examples shown in this book so far have relied on the core functionality of OpenGL. However, one of OpenGL’s greatest strengths is that it can be extended and enhanced by hardware manufacturers, operating system vendors, and even publishers of tools and debuggers. Extensions can have many different effects on OpenGL functionality.
An extension is any addition to a core version of OpenGL. Extensions are listed in the OpenGL extension registry5 on the OpenGL Web site. These extensions are written as a list of differences from a particular version of the OpenGL specification, and note what that version of OpenGL is. That means the text of the extensions describes how the core OpenGL specification must be changed if the extension is supported. However, popular and generally useful extensions are normally “promoted” into the core versions of OpenGL; thus, if you are running the latest and greatest version of OpenGL, there might not be that many extensions that are interesting but not part of the core profile. A complete list of the extensions that were promoted to each version of OpenGL and a brief synopsis of what they do is included in Appendix C, “OpenGL Features and Versions.”
There are three major classifications of extensions: vendor, EXT, and ARB. Vendor extensions are written and implemented on one vendor’s hardware. Initials representing the specific vendor are usually part of the extension name—“AMD” for Advanced Micro Devices or “NV” for NVIDIA, for example. It is possible that more than one vendor might support a specific vendor extension, especially if it becomes widely accepted. EXT extensions are written together by two or more vendors. They often start their lives as vendor-specific extensions, but if another vendor is interested in implementing the extension, perhaps with minor changes, it may collaborate with the original authors to produce an EXT version. ARB extensions are an official part of OpenGL because they are approved by the OpenGL governing body, the Architecture Review Board (ARB). These extensions are often supported by most or all major hardware vendors and may also have started out as vendor or EXT extensions.
This extension process may sound confusing at first. Hundreds of extensions currently are available! But new versions of OpenGL are often constructed from extensions programmers have found useful. In this way each extension gets its time in the sun. The ones that shine can be promoted to core; the ones that are less useful are not considered. This “natural selection” process helps to ensure only the most useful and important new features make it into a core version of OpenGL.
A useful tool to determine which extensions are supported in your computer’s OpenGL implementation is Realtech VR’s OpenGL Extensions Viewer. It is freely available from the Realtech VR Web site (see Figure 3.6).
Figure 3.6: Realtech VR’s OpenGL Extensions Viewer
Enhancing OpenGL with Extensions
Before using any extensions, you must make sure that they’re supported by the OpenGL implementation that your application is running on. To find out which extensions OpenGL supports, there are two functions that you can use. First, to determine the number of supported extensions, you can call glGetIntegerv() with the GL_NUM_EXTENSIONS parameter. Next, you can find out the name of each of the supported extensions by calling
const GLubyte* glGetStringi(GLenum name, GLuint index);
You should pass GL_EXTENSIONS as the name parameter, and a value between 0 and 1 less than the number of supported extensions in index. The function returns the name of the extension as a string. To see if a specific extension is supported, you can simply query the number of extensions, and then loop through each supported extension and compare its name to the one you’re looking for. The book’s source code comes with a simple function that does this for you. sb7IsExtensionSupported() has the prototype
int sb7IsExtensionSupported(const char * extname);
This function is declared in the <sb7ext.h> header, takes the name of an extension, and returns non-zero if it is supported by the current OpenGL context and zero if it is not. Your application should always check for support for extensions you wish to use before using them.
Extensions generally add to OpenGL in some combination of four different ways:
- They can make things legal that weren’t before, by simply removing restrictions from the OpenGL specification.
- They can add tokens or extend the range of values that can be passed as parameters to existing functions.
- They can extend GLSL to add functionality, built-in functions, variables, or data types.
- They can add entirely new functions to OpenGL itself.
In the first case, where things that once were considered errors no longer are, your application doesn’t need to do anything besides start using the newly allowed behavior (once you have determined that the extension is supported, of course). Likewise, for the second case, you can just start using the new token values in the relevant functions, presuming that you have their values. The values of the tokens are in the extension specifications, so you can look them up there if they are not included in your system’s header files.
To enable use of extensions in GLSL, you must first include a line at the beginning of shaders that use them to tell the compiler that you’re going to need their features. For example, to enable the hypothetical GL_ABC_foobar_feature extension in GLSL, include the following in the beginning of your shader:
#extension GL_ABC_foobar_feature : enable
This tells the compiler that you intend to use the extension in your shader. If the compiler knows about the extension, it will let you compile the shader, even if the underlying hardware doesn’t support the feature. If this is the case, the compiler should issue a warning if it sees that the extension is actually being used. Typically, extensions to GLSL will add preprocessor tokens to indicate their presence. For example, GL_ABC_foobar_feature will implicitly include
#define GL_ABC_foobar_feature 1
This means that you could write code such as
#if GL_ABC_foobar_feature // Use functions from the foobar extension #else // Emulate or otherwise work around the missing functionality #endif
This allows you to conditionally compile or execute functionality that is part of an extension that may or may not be supported by the underlying OpenGL implementation. If your shader absolutely requires support for an extension and will not work at all without it, you can instead include this more assertive code:
#extension GL_ABC_foobar_feature : require
If the OpenGL implementation does not support the GL_ABC_foobar_feature extension, then it will fail to compile the shader and report an error on the line including the #extension directive. In effect, GLSL extensions are opt-in features, and applications must6 tell compilers up front which extensions they intend to use.
Next we come to extensions that introduce new functions to OpenGL. On most platforms, you don’t have direct access to the OpenGL driver and extension functions don’t just magically appear as available to your applications to call. Rather, you must ask the OpenGL driver for a function pointer that represents the function you want to call. Function pointers are generally declared in two parts; the first is the definition of the function pointer type, and the second is the function pointer variable itself. Consider this code as an example:
typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); PFNGLDRAWTRANSFORMFEEDBACKPROC glDrawTransformFeedback = NULL;
This declares the PFNGLDRAWTRANSFORMFEEDBACKPROC type as a pointer to a function taking GLenum and GLuint parameters. Next, it declares the glDrawTransformFeedback variable as an instance of this type. In fact, on many platforms, the declaration of the glDrawTransformFeedback() function is actually just like this. This seems pretty complicated, but fortunately the following header files include declarations of all of the function prototypes, function pointer types, and token values introduced by all registered OpenGL extensions:
#include <glext.h> #include <glxext.h> #include <wglext.h>
These files can be found at the OpenGL extension registry Web site. The glext.h header contains both standard OpenGL extensions and many vendor-specific OpenGL extensions, the wglext.h header contains a number of extensions that are Windows specific, and the glxext.h header contains definitions that are X specific (X is the windowing system used on Linux and many other UNIX derivatives and implementations).
The method for querying the address of extension functions is actually platform specific. The book’s application framework wraps up these intricacies into a handy function that is declared in the <sb7ext.h> header file. The function sb7GetProcAddress() has this prototype:
void * sb7GetProcAddress(const char * funcname);
Here, funcname is the name of the extension function that you wish to use. The return value is the address of the function, if it’s supported, and NULL otherwise. Even if OpenGL returns a valid function pointer for a function that’s part of the extension you want to use, that doesn’t mean the extension is present. Sometimes the same function is part of more than one extension, and sometimes vendors ship drivers with partial implementations of extensions present. Always check for support for extensions using the official mechanisms or the sb7IsExtensionSupported() function.