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.
Last updated: 2008-06-09