One of the benefits of using OpenGL is that it is extensible. An extension is typically introduced by one or more vendors and then later is accepted by the OpenGL Architecture Review Board (ARB). Some extensions are promoted from a vendor-specific extension to a common one while others become part of the core OpenGL API. Extensions allow OpenGL to embrace innovation, but they also have implications for how you verify that the OpenGL functionality you want to use is available.
Because extensions can be introduced at the vendor level, more than one extension can provide the same basic functionality. There might also be an ARB extension that has functionality similar to that of a vendor-specific extension. As particular functionality becomes widely adopted, it can be moved into the core OpenGL API by the ARB. As a result, functionality that you want to use could be included as an extension, as part of the core API, or both. For example, the ability to combine texture environments is supported through the GL_ARB_texture_env_combine
and the GL_EXT_texture_env_combine
extensions. It's also part of the core OpenGL version 1.3 API. Although each has similar functionality, they use a different syntax. What this means is that you may need to check in several places (core OpenGL API and extension strings) to determine whether a specific renderer supports functionality that you want to use.
Detecting Functionality
Guidelines for Code That Checks for Functionality
See Also
OpenGL has two types of commands—those that are part of the core API and those that are part of an extension to OpenGL. Your application first needs to check for the version of the core OpenGL API and then check for the available extensions. Keep in mind that OpenGL functionality is available on a per-renderer basis. Not all renderers support all the available functionality. For example, a software renderer might not support fog effects even though fog effects are available in an OpenGL extension installed on the current system. For this reason, it's important that you check for particular functionality on a per-renderer basis.
Regardless of which extension you are checking for, the approach is the same. You need to call the OpenGL function glGetString
twice. The first time pass the GL_VERSION
constant. The function returns a string that specifies the version of OpenGL. The second time, pass the GL_EXTENSIONS
constant. The function returns a pointer to an extension name string. The extension name string is a space-delimited list of the OpenGL extensions that are supported by the current renderer. This string can be rather long, so make sure that you don't allocate a fixed-length string for the return value of the glGetString
function. That is, do not use the function strcpy
; use a pointer and evaluate the string in place.
Pass the extension name string to the function gluCheckExtension
along with the name of the extension you want to check for. The gluCheckExtension
function returns a Boolean value that indicates whether or not the extension is available for the current renderer.
If an extension becomes part of the core OpenGL API, OpenGL continues to export the name strings of the promoted extensions. It also continues to support the previous versions of any extension that has been exported in earlier versions of Mac OS X. The fact that extensions are not typically removed guarantees that the methodology you use today to check for a feature will work in all future versions of Mac OS X.
OpenGL has a tremendous amount of functionality, as you can see by looking at the extensions listed in “OpenGL Functionality by Version.” You need to call gluCheckExtension
for each extension you want to check, and you need to check each extension for each renderer. Checking for functionality, although fairly straightforward, involves writing a large chunk of code. The best way to check for OpenGL functionality is to implement a capability-checking function that you call when your program starts up, and then any time a display configuration changes. Listing 5-1 shows a code excerpt that checks for a few extensions. (Note that it is not a standalone function.) A detailed explanation for each line of code appears following the listing.
You can extend this example to make a comprehensive functionality-checking routine for your application. For more details, see the GLCheck.c
file in the Cocoa OpenGL sample application.
Listing 5-1 Checking for OpenGL functionality
GLint maxRectTextureSize; |
GLint myMaxTextureUnits; |
GLint myMaxTextureSize; |
const GLubyte * strVersion; |
const GLubyte * strExt; |
float myGLVersion; |
GLboolean isVAO, isTexLOD, isColorTable, isFence, isShade, |
isTextureRectangle; |
strVersion = glGetString (GL_VERSION); // 1 |
sscanf((char *)strVersion, "%f", &myGLVersion); |
strExt = glGetString (GL_EXTENSIONS); // 2 |
glGetIntegerv(GL_MAX_TEXTURE_UNITS, &myMaxTextureUnits); // 3 |
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &myMaxTextureSize); // 4 |
isVAO = |
gluCheckExtension ((const GLubyte*)"GL_APPLE_vertex_array_object",strExt); // 5 |
isFence = gluCheckExtension ((const GLubyte*)"GL_APPLE_fence", strExt); // 6 |
isShade = |
gluCheckExtension ((const GLubyte*)"GL_ARB_shading_language_100", strExt); // 7 |
isColorTable = |
gluCheckExtension ((const GLubyte*)"GL_SGI_color_table", strExt) || |
gluCheckExtension ((const GLubyte*)"GL_ARB_imaging", strExt); // 8 |
isTexLOD = |
gluCheckExtension ((const GLubyte*)"GL_SGIS_texture_lod", strExt) || |
(myGLVersion >= 1.2); // 9 |
isTextureRectangle = gluCheckExtension ((const GLubyte*) |
"GL_EXT_texture_rectangle", strExt); |
if (isTextureRectangle) |
glGetIntegerv (GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT, &maxRectTextureSize); |
else |
maxRectTextureSize = 0; // 10 |
Here what the code does:
Gets a string that specifies the version of OpenGL.
Gets the extension name string.
Calls the OpenGL function glGetIntegerv
to get the value of the attribute passed to it which, in this case, is the maximum number of texture units.
Gets the maximum texture size.
Checks whether vertex array objects are supported.
Checks for the Apple fence extension.
Checks for support for version 1.0 of the OpenGL shading language.
Checks for RGBA-format color lookup table support. In this case, the code needs to check for the vendor-specific string and for the ARB string. If either is present, the functionality is supported.
Checks for an extension related to the texture level of detail parameter (LOD). In this case, the code needs to check for the vendor-specific string and for the OpenGL version. If either the vendor string is present or the OpenGL version is greater than or equal to 1.2, the functionality is supported.
Gets the OpenGL limit for rectangle textures. For some extensions, such as the rectangle texture extension, it may not be enough to check whether the functionality is supported. You may also need to check the limits. You can use glGetIntegerv
and related functions (glGetBooleanv
, glGetDoublev
, glGetFloatv
) to obtain a variety of parameter values.
Keep in mind that you must check functionality on a per-renderer basis. The code in Listing 5-2 shows one way to query the current renderer. It uses the CGL API, which can be called from Cocoa or Carbon applications. In reality, you need to iterate over all displays and all renderers for each display to get a true picture of the OpenGL functionality available on a particular system. You also need to update the your functionality "snapshot" each time the list of displays or display configuration changes.
Listing 5-2 Setting up a valid rendering context to get renderer functionality information
#include <OpenGL/OpenGL.h> |
#include <ApplicationServices/ApplicationServices.h> |
CGDirectDisplayID display = CGMainDisplayID (); // 1 |
CGOpenGLDisplayMask myDisplayMask = |
CGDisplayIDToOpenGLDisplayMask (display); // 2 |
{ // Check capabilities of display represented by display mask |
CGLPixelFormatAttribute attribs[] = {kCGLPFADisplayMask, |
myDisplayMask, |
NULL}; // 3 |
CGLPixelFormatObj pixelFormat = NULL; |
long numPixelFormats = 0; |
CGLContextObj myCGLContext = 0; |
CGLContextObj curr_ctx = CGLGetCurrentContext (); // 4 |
CGLChoosePixelFormat (attribs, &pixelFormat, &numPixelFormats); // 5 |
if (pixelFormat) { |
CGLCreateContext (pixelFormat, NULL, &myCGLContext); // 6 |
CGLDestroyPixelFormat (pixelFormat); // 7 |
CGLSetCurrentContext (myCGLContext); // 8 |
if (myCGLContext) { |
// Check for capabilities and functionality here |
} |
} |
CGLDestroyContext (myCGLContext); // 9 |
CGLSetCurrentContext (curr_ctx); // 10 |
} |
Here's what the code does:
Gets the display ID of the main display.
Maps a display ID to an OpenGL mask.
Fills a pixel format attributes array with the display mask attribute and the mask value.
Saves the current context so that it can be restored later.
Gets the pixel format object for the display. The numPixelFormats
parameter specifies how many pixel formats are listed in the pixel format object.
Creates a context based on the first pixel format in the list supplied by the pixel format object. Only one renderer will be associated with this context.
In your application, you would need to iterate through all pixel formats for this display.
Destroys the pixel format object when it is no longer needed.
Sets the current context to the newly created, single-renderer context. Now you are ready to check for the functionality supported by the current renderer. See Listing 5-1 for an example of functionality checking code.
Destroys the context because it is no longer needed.
Restores the previously saved context as the current context, thus ensuring no intrusion upon the user.
The guidelines in this section will ensure that your functionality checking code is thorough yet efficient. See “Detecting Functionality” for specific details on implementing these guidelines.
Don't rely on what's in a header file. A command declaration in a header file does not ensure that a feature is supported by the current renderer. Neither does linking against a stub library that exports a function.
Make sure that a renderer is attached to a valid rendering context before you check the functionality of that renderer.
Check the API version or the extension name string for the current renderer before you issue OpenGL commands.
Check only once per renderer. After you've determined that the current renderer supports an OpenGL command, you don't need to check for that functionality again for that renderer.
Ensure that your code supports a feature, whether the feature is part of the core OpenGL API or is an extension. Keep in mind that different constants and command names are often used for functionality that is both part of the core API and an extension.
Enable only those OpenGL features that are tested. Enabling untested features can lead to application failures.
OpenGL extension information:
The OpenGL Extensions Registry at http://www.opengl.org/registry/.
OpenGL Extensions Guide provides a list of extensions and availability according to OpenGL version, Mac OS X version, and renderer.
Many OpenGL sample code projects (ADC Reference Library) contain code to check for OpenGL functionality. For example, see the glCheck.c
and glCheck.h
files in the Cocoa OpenGL sample application or in the GLCarbonCGLFullScreen sample application.
© 2004, 2008 Apple Inc. All Rights Reserved. (Last updated: 2008-06-09)