There are three tenets to using extensions:
These are discussed below. Furthermore, one may also have to deal with different versions of OpenGL and the GLU and GLX libraries.We begin with a discussing of extension naming conventions.
GL_type_name
Where type is EXT or a vendor-specific identifier
such as SGI or IBM.
The EXT indentifier generally indicates that an extension has
been adopted by at least two vendors.
Vendors may also extend the type convention to indicate the
class of the extension. Silicon Graphics, for example, use
SGIS to indicate an
extension may only be available on particular systems
and SGIX to indicate that the extension is experimental.
name is a string of lowercase characters such
as polygon_offset.
Example extension names:
Note the naming schemes.
Any references to constants or functions defined by the extension must be
surrounded by
It is critical to properly test for extensions at compile time if you
want your application to be recompilable on different systems.
The former case dictates that our application should not have any
calls to extension functions which may be missing in the OpenGL library.
The later case dictates that we simply must not call a missing extension
function during execution.
To maximize portability we should obey the more stringent first case.
The solution is to query the dynamic library for extension functions and
call them via function pointers.
This is the recommended practice for Windows 95 and NT.
Operating systems which support dynamic libraries have an API for working
with dynamic libraries.
The only feature we need is the ability to get the address of function by
name.
Two examples follow.
For example, suppose we want to use the
For example, suppose we want to use the
The
Be aware that
Be careful when searching the extensions list!
The C library function
The following function can be used for reliable runtime extension testing:
A program written for OpenGL 1.0 which uses no extensions will work
with OpenGL 1.1 unchanged.
However, a program written for OpenGL 1.0 with extensions may require
some modifications to work with OpenGL 1.1.
If you want your program to compile and execute cleanly with either
OpenGL 1.0 or OpenGL 1.1 you will need to observe the following
guidelines.
For example:
Therefore, if you want to get a list of GLU extensions you'll need to
use something like this:
Version 1.1 of GLU only added the
Version 1.2 of GLU introduced a new polygon tessellator.
The new tessellator functions all begin with the prefix
Note that if the
Testing for the GLX version at runtime involves checking for a preprocessor
symbol such as
The GLX version can be determined at runtime by calling
Examples:
This is just one approach however.
For your application it may be appropriate to implement things differently.
When designing wrapper functions it's probably best to look at the big
picture and design simple, high-level wrappers rather than try to make
wrappers which directly corresponds to individual OpenGL functions.
A collection of wrappers like these may be put in a separate source
file and reused in many applications.
2. Naming conventions
OpenGL extension are named according to the convention:
If an extension defines any new GL_EXT_polygon_offset
GL_SGI_color_table
GL_SGIS_detail_texture
GL_MESA_window_pos
GLenum values they will be
suffixed with
the extension type. For example, the GL_EXT_blend_minmax
extension adds the following GLenum values:
If an extension defines any new API functions they will be suffixed with
the extension type as well. For example, the
GL_EXT_polygon_offset extension adds the function:
GL_FUNC_ADD_EXT
GL_MIN_EXT
GL_MAX_EXT
GL_BLEND_EQUATION_EXT
void glPolygonOffsetEXT( GLfloat factor, GLfloat bias )
3. An extension sampler
This section lists some OpenGL extensions with short descriptions. Many
extensions are implemented in groups. For example, the blending extensions
are interdependent and usually implemented together. See your OS/OpenGL
release notes and man pages for detailed descriptions.
Core extensions
Many of these extensions to OpenGL 1.0 have been incorporated into OpenGL 1.1.
glLogicOp
functionality to RGB blending
glPolygonOffsetEXT
function which
displaces the Z value of polygon fragments to facilitate drawing
cleanly outlined polygons
SGI-specific core extensions
GLX Extensions (see section 8)
SGI-specific GLX extensions
glCopyPixels can copy from one window
into another
Microsoft OpenGL Extensions
4. Compile-time extension testing
If an OpenGL extension is supported at compile-time the host's gl.h
file will define a preprocessor symbol named for that extension.
For example, the gl.h file will have
#define GL_EXT_texture3D 1
if the GL_EXT_texture3D extension is supported.
#ifdef/#endif.
For example:
#ifdef GL_EXT_texture3D
glTexImage3DEXT(GL_TEXTURE_3D_EXT, 0, format, w, h, d, border,
format, type, pixels);
#endif
Failure to test for extensions at compile time can result in compilation
and link errors such as Undefined symbol or
Undefined function.
5. Run-time extension testing
We must also test for OpenGL extensions at runtime.
There are two reasons for this:
4.1 Function Bindings
When using dynamic libraries, the binding of function calls to
library functions can
be either be done when the program is loaded into memory or when the
library function is called for the first time.
4.1.1 Microsoft Windows 95 and NT and SGI Cosmo OpenGL
The WGL interface to OpenGL has wglGetProcAddress.
It is used to get the address of OpenGL extension functions.
glArrayElementEXT
function in our OpenGL application.
We would use the following code:
/* Get pointer to the function, if it exists. */
PFNGLARRAYELEMENTEXTPROC glArrayElementPtr;
glArrayElementPtr = (PFNGLARRAYELEMENTEXTPROC)
wglGetProcAddress("glArrayElementEXT");
/* ... */
/* instead of glArrayElementEXT(i) we do this: */
if (glArrayElementPtr) {
(*glArrayElementPtr)(i);
}
4.1.2 IRIX
On SGI systems, the dlsym function is used to get the
address of a named function.
glArrayElementEXT
function in our OpenGL application.
We would use the following code:
#include <dlfcn.h>
typedef void (*glArrayElementProc)(int i);
void *libHandle;
glArrayElementProc glArrayElementPtr;
/* Open GL library and get pointer to the function, if it exists. */
libHandle = dlopen("libgl.so", RTLD_LAZY);
glArrayElementPtr = (glArrayElementProc)
dlsym(libHandle, "glArrayElementEXT");
/* ... */
/* instead of glArrayElementEXT(i) we do this: */
if (glArrayElementPTr) {
(*glArrayElementPtr)(i);
}
4.2 Renderer Testing
With GLX, OpenGL rendering can be directed to a non-local X server.
The target X server may not support the extension(s) your application uses.
To prevent problems we must query the renderer to determine which
extensions it supports.
glGetString(GL_EXTENSIONS) function returns a list of
extensions which are supported by the OpenGL renderer.
This list can be searched to determine if a specific extension is supported.
glGetString(GL_EXTENSIONS) must
be called after we've established an active OpenGL rendering context.
For example, we must call glXMakeCurrent or
wglMakeCurrent before calling glGetString.
The reason is that OpenGL extensions are dependant on the OpenGL renderer
and the renderer isn't bound until MakeCurrent is called.
strstr is not sufficient because
it may match a substring of the extension name you're testing for.
For example, if you're testing for the GL_EXT_texture
extension and glGetString(GL_EXTENSIONS) returns
"GL_EXT_texture3D" then simply using strstr
will incorrectly tell you that GL_EXT_texture is supported.
GLboolean CheckExtension( char *extName )
{
/*
** Search for extName in the extensions string. Use of strstr()
** is not sufficient because extension names can be prefixes of
** other extension names. Could use strtok() but the constant
** string returned by glGetString can be in read-only memory.
*/
char *p = (char *) glGetString(GL_EXTENSIONS);
char *end;
int extNameLen;
extNameLen = strlen(extName);
end = p + strlen(p);
while (p < end) {
int n = strcspn(p, " ");
if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) {
return GL_TRUE;
}
p += (n + 1);
}
return GL_FALSE;
}
6. OpenGL 1.1
Many extensions designed for OpenGL 1.0 have been incorporated into
OpenGL 1.1 as standard features.
Compile time
To detect whether a particular feature is available at compile time you will
need to use the C preprocessor to test for either an OpenGL 1.0 extension
name or test for the OpenGL 1.1 version symbol: GL_VERSION_1_1.
#if defined(GL_EXT_texture_object) || defined(GL_VERSION_1_1)
your code
#endif
Sometime in the future you may need
#if defined(GL_EXT_texture_object) || defined(GL_VERSION_1_1) || defined(GL_VERSION_1_2)
your code
#endif
Runtime
At runtime you must check if the renderer supports OpenGL 1.1 or the
1.0 extension:
/* After calling MakeCurrent()! */
char *version = (char*) glGetString(GL_VERSION);
GLboolean HaveTexObjExtension;
if (strncmp(version,"1.1",3)==0
|| CheckExtension("GL_EXT_texture_object")) {
HaveTexObjExtension = GL_TRUE;
}
else {
HaveTexObjExtension = GL_FALSE;
}
7. GLU extensions and versions
There have been several versions of the GLU (GL Utility) library and the
library may have extensions.
Again, for safety, the GLU version and extensions should be tested for at
compile-time and run-time if you need their specific features.
At this time, there are no known GLU extensions but a few different
GLU versions.
Compile-time testing
If a GLU extension is available at runtime the glu.h file will
define a preprocessor symbol with the prefix GLU_EXT_.
As with OpenGL extensions, there should be #ifdef/#endif
tests surrounding any references to functions or symbols unique to the
extension.
Run-time testing
GLU version 1.0 had no function to call at run-time to query the GLU
version or extensions list.
GLU version 1.1 added the gluGetString function which takes
two possible values: GLU_EXTENSIONS or GLU_VERSION.
char *extensions;
#ifdef GLU_VERSION_1_1
extensions = (char *) gluGetString(GLU_EXTENSIONS);
#else
extensions = "";
#endif
Be careful of accidently matching substrings while searching the string.
GLU versions
There have been several versions of the GLU library.
As shown above, you can test for the GLU version at compile-time
by checking for preprocessor symbols like GLU_VERSION_1_1
and GLU_VERSION_1_2.
At run-time you can determine the GLU version by calling
gluGetString(GLU_VERSION).
gluGetString function.
gluTess.
For more information about the changes in the GLU tesselator from version
1.0 to 1.1 see
http://www.digital.com:80/pub/doc/opengl/opengl_new_glu.html
GLU_VERSION_1_2 symbol is defined then
the GLU_VERSION_1_1 symbol is also defined.
One can expect this trend of backward compatibility to continue.
8. GLX extensions
The GLX interface offers extensions in a manner very similar to core OpenGL.
Again, extensions must be tested for both at compile-time and run-time.
If a GLX extension is not available there should be a fall-back strategy.
Compile-time testing
If a GLX extension is available at runtime the glx.h file will
define a corresponding preprocessor symbol.
For example, if the GLX_EXT_import_context extension is
available, then glx.h (or glxtokens.h) will contain
#define GLX_EXT_import_context 1
Run-time testing
After we've established a connection to an X server we can determine which
GLX extensions are available by calling
glXQueryExtensionsString(dpy, screen).
This function returns a list of supported GLX extensions separated by
white space.
Again, we have to be careful when searching the extensions list.
A function similar to CheckExtension should be used.
GLX version testing
There have been several versions of the GLX interface.
Version 1.0 was the first version.
Version 1.1 added the glXQueryExtensionsString,
glXQueryServerString and glXGetClientString functions.
Version 1.2 may include several of the 1.0 and 1.1 GLX extension features.
GLX_VERSION_1_1 or GLX_VERSION_1_2.
glXQueryVersion.
9. Fall-back scenarios
Your program should be prepared for the likely situation in which
a desired extension is not available. Depending on the nature of the
extension you may elect to limit functionality, fall-back to an equivalent
but slower implementation, or to simply abort.
Aborting when an extension isn't available is strongly discouraged.
In most cases users will prefer reduced performance/functionality
over complete failure.
At the very least, the user should be informed why an OpenGL application
can't operate if an extension isn't present.
GL_EXT_vertex_array extension
is not available then you should fall-back to the regular
glVertex/glColor/glNormal functions at the expense of
performance.
GL_EXT_texture3D extension is not available.
10. Putting it all Together
Dealing with OpenGL versions and extensions is a bit complicated.
This is an example, using texture objects, of how one may cope with the
complexity.
The basic idea is to write wrapper functions for extensions to hide the
complexity.
First, we need some function pointers:
typedef void (*glGenTexturesProc)( GLsizei n, GLuint *textures );
typedef void (*glDeleteTexturesProc)( GLsizei n, const GLuint *textures );
typedef void (*glBindTextureProc)( GLenum target, GLuint texture );
glGenTexturesProc glGenTexturesPtr = NULL;
glDeleteTexturesProc glDeleteTexturesPtr = NULL;
glBindTextureProc glBindTexturePtr = NULL;
Second, we'll call this function after we've made current our
OpenGL context:
void ExtensionSetup(void)
{
char *version = (char*) glGetString(GL_VERSION);
if (strncmp(version,"1.1",3)==0) {
/* OpenGL 1.1 */
glGenTexturesPtr = LookupFunction("glGenTextures");
glDeleteTexturesPtr = LookupFunction("glDeleteTextures");
glBindTexturePtr = LookupFunction("glBindTexture");
}
else {
/* OpenGL 1.0 */
int haveTextureObjects = CheckExtension("GL_EXT_texture_object");
if (haveTextureObjects) {
glGenTexturesPtr = LookupFunction("glGenTexturesEXT");
glDeleteTexturesPtr = LookupFunction("glDeleteTexturesEXT");
glBindTexturePtr = LookupFunction("glBindTextureEXT");
}
else {
glGenTexturesPtr = NULL;
glDeleteTexturesPtr = NULL;
glBindTexturePtr = NULL;
}
}
}
The LookupFunction function finds function pointers in dynamic
libraries.
It may be implemented with something like this:
void *LookupFunction(const char *funcName)
{
#if defined(__WIN32__)
return wglGetProcAddress(funcName);
#elif defined(IRIX)
void *libHandle = dlopen("libgl.so", RTLD_LAZY);
void *func = dlsym(libHandle, funcName);
dlclose(libHandle);
return func;
#else
/* other OSes... */
#endif
}
Third, we'll implement wrapper functions for texture object functionality.
/*
* Allocate a set of texture objects.
*/
void myGenTextures( GLsizei n, GLuint *textures )
{
if (glGenTexturesPtr) {
/* Call OpenGL 1.1 or 1.0 extension function */
(*glGenTexturesPtr)( n, textures );
}
else {
/* fallback code: use display lists */
GLuint first;
first = glGenLists( n );
if (first>0) {
GLuint i;
for (i=0; i < n; i++) {
textures[i] = first+i;
}
}
}
}
/*
* Begin definition of a texture object.
*/
void myBeginTexture( GLenum target, GLuint texture )
{
if (glBindTexturePtr) {
(*glBindTexturePtr)( target, texture );
}
else {
/* fallback code: use display lists */
glNewList( texture, GL_COMPILE );
}
}
/*
* End definition of a texture object.
*/
void myEndTexture( GLenum target )
{
if (glBindTexturePtr) {
(*glBindTexturePtr)( target, 0 );
}
else {
/* fallback code: use display lists */
glEndList();
}
}
/*
* Bind (use) a texture object.
*/
void myBindTexture( GLenum target, GLuint texture )
{
if (glBindTexturePtr) {
(*glBindTexturePtr)( target, texture );
}
else {
/* fallback code: use display lists */
glCallList( texture );
}
}
Finally, in our application we'll use the myGenTextures,
myBeginTexture, myEndTexture, myBindTexture functions to
actually use texture objects.
11. References
Other sources of information about OpenGL extensions can be found at:
Last edited on July 9, 1997 by Brian Paul.