GLCheck.c

//
// File:        GLCheck.c
//              (Originally glCheck.c)
//
// Abstract:    glcheck allows developer to check the hardware capabilities of all GPU's
//              returning an array of records reflecting the attached hardware. This
//              list can be regenerated on Display Manager notifications to keep the
//              client update to on capabilities and setup changes.  This is provided as
//              sample to allow developers the freedom to check as few or as many
//              conditions and capabilities as they would like or add their own checks
//
// Version:     1.1 - Removed QD dependencies, list of extensions
//              1.0 - Original release.
//              
//
// Disclaimer:  IMPORTANT:  This Apple software is supplied to you by Apple Inc. ("Apple")
//              in consideration of your agreement to the following terms, and your use,
//              installation, modification or redistribution of this Apple software
//              constitutes acceptance of these terms.  If you do not agree with these
//              terms, please do not use, install, modify or redistribute this Apple
//              software.
//
//              In consideration of your agreement to abide by the following terms, and
//              subject to these terms, Apple grants you a personal, non - exclusive
//              license, under Apple's copyrights in this original Apple software ( the
//              "Apple Software" ), to use, reproduce, modify and redistribute the Apple
//              Software, with or without modifications, in source and / or binary forms;
//              provided that if you redistribute the Apple Software in its entirety and
//              without modifications, you must retain this notice and the following text
//              and disclaimers in all such redistributions of the Apple Software. Neither
//              the name, trademarks, service marks or logos of Apple Inc. may be used to
//              endorse or promote products derived from the Apple Software without specific
//              prior written permission from Apple.  Except as expressly stated in this
//              notice, no other rights or licenses, express or implied, are granted by
//              Apple herein, including but not limited to any patent rights that may be
//              infringed by your derivative works or by other works in which the Apple
//              Software may be incorporated.
//
//              The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
//              WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
//              WARRANTIES OF NON - INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A
//              PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION
//              ALONE OR IN COMBINATION WITH YOUR PRODUCTS.
//
//              IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
//              CONSEQUENTIAL DAMAGES ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//              SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//              INTERRUPTION ) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION
//              AND / OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER
//              UNDER THEORY OF CONTRACT, TORT ( INCLUDING NEGLIGENCE ), STRICT LIABILITY OR
//              OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Copyright ( C ) 2003-2007 Apple Inc. All Rights Reserved.
//
 
// see glcheck.h for more explanation on the use of CheckOpenGLCaps and it's associated data
 
 
#include "GLCheck.h"
 
#include <OpenGL/OpenGL.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
 
#include <string.h>
 
// -------------------------
 
// local CF dictionary routines
 
static long _getDictLong (CFDictionaryRef refDict, CFStringRef key)
{
  long int_value;
  CFNumberRef num_value = (CFNumberRef)CFDictionaryGetValue(refDict, key);
  if (!num_value) // if can't get a number for the dictionary
    return -1;  // fail
  // or if cant convert it
  if (!CFNumberGetValue(num_value, kCFNumberLongType, &int_value)) 
    return -1; // fail
  return int_value; // otherwise return the long value
}
 
static double _getDictDouble (CFDictionaryRef refDict, CFStringRef key)
{
  double double_value;
  CFNumberRef num_value = (CFNumberRef)CFDictionaryGetValue(refDict, key);
  if (!num_value) // if can't get a number for the dictionary
    return -1;  // fail
  // or if cant convert it
  if (!CFNumberGetValue(num_value, kCFNumberDoubleType, &double_value)) 
    return -1; // fail
  return double_value; // otherwise return the long value
}
 
// -------------------------
 
// this does a reasonable check to see if things have changed without being 
// too heavy weight; returns 1 if changed 0 if not
// checks num displays, displayID, displayMask, each display geometry and 
// renderer VRAM and ID
 
unsigned char HaveOpenGLCapsChanged (GLCaps aDisplayCaps[], 
                                     CGDisplayCount dspyCnt)
{
  CGDisplayCount maxDisplays = 32;
  CGDirectDisplayID activeDspys[32];
  CGDisplayErr theError;
  short i;
  CGDisplayCount newDspyCnt = 0;
  
  if (NULL == aDisplayCaps) return 1;
 
  theError = CGGetActiveDisplayList(maxDisplays, activeDspys, &newDspyCnt);
  // if theError getting list mark as changed
  if (theError) return 1; 
  // if number of displays not equal
  if (dspyCnt != newDspyCnt) return 1;
  
  for (i = 0; i < dspyCnt; i++) {
    // get device ids
    if (aDisplayCaps[i].cgDisplayID != activeDspys[i]) return 1;
    if (aDisplayCaps[i].cglDisplayMask !=  
        CGDisplayIDToOpenGLDisplayMask(activeDspys[i])) return 1;
 
    // get current geometry
    {
      CGRect displayRect = CGDisplayBounds (activeDspys[i]);
      // get mode dictionary
      CFDictionaryRef dispMode = CGDisplayCurrentMode (activeDspys[i]);
      // check for all geometry matches 
      if (aDisplayCaps[i].deviceWidth != (long) displayRect.size.width)
        return 1;   
      if (aDisplayCaps[i].deviceHeight != (long) displayRect.size.height) 
        return 1;   
      if (aDisplayCaps[i].deviceOriginX != (long) displayRect.origin.x) 
        return 1;   
      if (aDisplayCaps[i].deviceOriginY != (long) displayRect.origin.y) 
        return 1;   
      if (aDisplayCaps[i].deviceDepth != 
                 (short) _getDictLong (dispMode,  kCGDisplayBitsPerPixel)) 
        return 1;    
      if (aDisplayCaps[i].deviceRefresh != 
          (short)(_getDictDouble (dispMode, kCGDisplayRefreshRate) + 0.5)) 
        return 1; // round to GLint
    }
 
    // get renderer info based on gDevice
    {
      CGLRendererInfoObj info;
      long j, numRenderers = 0, rv = 0;
      CGLError theErr = 0;
      long deviceVRAM; // video memory in bytes
      unsigned long rendererID; // renderer ID
      
      theErr = CGLQueryRendererInfo (aDisplayCaps[i].cglDisplayMask, 
                                  &info, &numRenderers);
      if(0 == theErr) {
        CGLDescribeRenderer (info, 0, kCGLRPRendererCount, &numRenderers);
        for (j = 0; j < numRenderers; j++) {
          // find accelerated renderer (assume only one)
          CGLDescribeRenderer (info, j, kCGLRPAccelerated, &rv); 
          if (true == rv) { // if accelerated
            // what is the renderer ID
            CGLDescribeRenderer (info, j, kCGLRPRendererID, (long *)&rendererID); 
            if (rendererID != aDisplayCaps[i].rendererID) // check match
              return 1;
            // what is the VRAM
            CGLDescribeRenderer (info, j, kCGLRPVideoMemory, &deviceVRAM); 
            if (deviceVRAM != aDisplayCaps[i].deviceVRAM) // check match
              return 1;
            break; // done
          }
        }
      }
      CGLDestroyRendererInfo (info);
    }
  }
  return 0;
}
 
// -------------------------
 
// This will walk all active displays and gather information about their
// hardware renderer 
 
// An array length (maxDisplays) and array of GLCaps are passed in. Up to
// maxDisplays of the array are filled in with the displays meeting the
// specified criteria.  The actual number of displays filled in is returned
// in dspyCnt.  Calling this function with maxDisplays of 0 will just
// return the number of displays in dspyCnt.
 
// Developers should note this is NOT an exhaustive list of all the
// capabilities one could query, nor a required set of capabilities,
// feel free to add or subtract queries as you find helpful for your 
// application/use.
 
// one note on mirrored displays... if the display configuration is 
// changed it is possible (and likely) that the current active display
// in a mirrored configuration (as identified by the OpenGL Display Mask)
// will change if the mirrored display is removed.  
// This is due to the preference of selection the external display as 
// the active display.  This may affect full screen apps which should 
// always detect display configuration changes and respond accordingly.
 
void CheckOpenGLCaps (CGDisplayCount maxDspys, 
                      GLCaps dCaps[], 
                      CGDisplayCount * dCnt)
{
  CGLContextObj curr_ctx = 0;
  CGDirectDisplayID dspys[32];
  CGDisplayErr theErr;
  short i;
  short size = sizeof (GLCaps);
  
  // no devices
  *dCnt = 0;
  
  if (maxDspys == 0) { // find number of displays
    *dCnt = 0;
    theErr = CGGetActiveDisplayList (32, dspys, dCnt);
    if (theErr) // theErr getting list
      *dCnt = 0; // 0 displays since can't correctly find any
    // zero list to ensure the routines are used correctly
    memset (dspys, 0, sizeof (CGDirectDisplayID) * *dCnt);
    return; // return dCnt
  }
  if (NULL == dCaps) return;
 
  theErr = CGGetActiveDisplayList(maxDspys, dspys, dCnt);
  if (theErr) return; // theErr getting list
  if (0 == *dCnt) return; // no displays
  
  memset (dCaps, 0, size * *dCnt); // zero memory
  
  for (i = 0; i < *dCnt; i++) {
    // get device ids
    dCaps[i].cgDisplayID = dspys[i];
    dCaps[i].cglDisplayMask = CGDisplayIDToOpenGLDisplayMask(dspys[i]);
    
    { // get current geometry
      CGRect displayRect = CGDisplayBounds (dspys[i]);
      // get mode dictionary
      CFDictionaryRef dispMode = CGDisplayCurrentMode (dspys[i]); 
      dCaps[i].deviceWidth = (long) displayRect.size.width;   
      dCaps[i].deviceHeight = (long) displayRect.size.height;   
      dCaps[i].deviceOriginX = (long) displayRect.origin.x;   
      dCaps[i].deviceOriginY = (long) displayRect.origin.y;   
      dCaps[i].deviceDepth = (short) _getDictLong (dispMode,  
                                              kCGDisplayBitsPerPixel);    
      dCaps[i].deviceRefresh = (short) (_getDictDouble (dispMode,
                                        kCGDisplayRefreshRate) + 0.5); 
    }    
 
    { // get renderer info based on gDevice
      CGLRendererInfoObj info;
      long j, numRenderers = 0, rv = 0;
      CGLError theErr2 = 0;
      
      theErr2 = CGLQueryRendererInfo (dCaps[i].cglDisplayMask, 
                                  &info, 
                                  &numRenderers);
      if(0 == theErr2) {
        CGLDescribeRenderer (info, 0, kCGLRPRendererCount, &numRenderers);
        for (j = 0; j < numRenderers; j++) {
          // find accelerated renderer (assume only one)
          CGLDescribeRenderer (info, j, kCGLRPAccelerated, &rv); 
          if (true == rv) { // if accelerated
            // what is the renderer ID
            CGLDescribeRenderer (info, j, kCGLRPRendererID,
                                 &dCaps[i].rendererID);
            // can we do full screen?
            CGLDescribeRenderer (info, j, kCGLRPFullScreen, &rv); 
            dCaps[i].fullScreenCapable = (bool) rv;
            // what is the VRAM?
            CGLDescribeRenderer (info, j, kCGLRPVideoMemory,
                                 &dCaps[i].deviceVRAM);
            // what is the current texture memory? 
            CGLDescribeRenderer (info, j, kCGLRPTextureMemory,
                                 &dCaps[i].deviceTextureRAM);
            break; // done
          }
        }
      }
      CGLDestroyRendererInfo (info);
    }
 
    { // build context and context specific info
      CGLPixelFormatAttribute attribs[] = { kCGLPFADisplayMask,
                                            dCaps[i].cglDisplayMask, 
                                            (CGLPixelFormatAttribute)0 };
      CGLPixelFormatObj pixelFormat = NULL;
      long numPixelFormats = 0;
      CGLContextObj cglContext;
      
      curr_ctx = CGLGetCurrentContext (); // get current CGL context
      CGLChoosePixelFormat (attribs, &pixelFormat, &numPixelFormats);
      if (pixelFormat) {
        CGLCreateContext(pixelFormat, NULL, &cglContext);
        CGLDestroyPixelFormat (pixelFormat);
        CGLSetCurrentContext (cglContext);
        if (cglContext) {
          const GLubyte * strExt;
          const GLubyte * strRend;
          const GLubyte * strVers;
          const GLubyte * strVend;
 
          // get renderer strings
          strRend = glGetString (GL_RENDERER);
          strncpy (dCaps[i].strRendererName, (char *) strRend, 255);
          strVend = glGetString (GL_VENDOR);
          strncpy (dCaps[i].strRendererVendor, (char *) strVend, 255);
          strVers = glGetString (GL_VERSION);
          strncpy (dCaps[i].strRendererVersion, (char *) strVers, 255);
          { // get BCD version
            short j = 0;
            short shiftVal = 8;
            while (((strVers[j] <= '9') && (strVers[j] >= '0')) ||
                                            (strVers[j] == '.')) { 
            // get only basic version info (until first non-digit or non-.)
              if ((strVers[j] <= '9') && (strVers[j] >= '0')) {
                dCaps[i].glVersion += (strVers[j] - '0') << shiftVal;
                shiftVal -= 4;
              }
              j++;
            }
          }
          strExt = glGetString (GL_EXTENSIONS);
 
          // get caps
          glGetIntegerv (GL_MAX_TEXTURE_UNITS, 
                         &dCaps[i].textureUnits);
          glGetIntegerv (GL_MAX_TEXTURE_SIZE,
                         &dCaps[i].maxTextureSize); 
          glGetIntegerv (GL_MAX_3D_TEXTURE_SIZE, 
                         &dCaps[i].max3DTextureSize);
          glGetIntegerv (GL_MAX_CUBE_MAP_TEXTURE_SIZE, 
                         &dCaps[i].maxCubeMapTextureSize);
 
          // get functionality info
          dCaps[i].fSpecularVector = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_specular_vector", strExt);
          dCaps[i].fTransformHint = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_transform_hint", strExt);
          dCaps[i].fPackedPixels = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixels", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_packed_pixel", strExt)  || 
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fClientStorage = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_client_storage", strExt);
          dCaps[i].fYCbCr = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_ycbcr_422", strExt);
          dCaps[i].fTextureRange = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_texture_range", strExt);
          dCaps[i].fFence = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_fence", strExt);
          dCaps[i].fVAR = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_vertex_array_range", strExt);
          dCaps[i].fVAO = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_vertex_array_object", strExt);
          dCaps[i].fElementArray = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_element_array", strExt);
          dCaps[i].fVPEvals = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_vertex_program_evaluators",strExt);
          dCaps[i].fFloatPixels = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_float_pixels", strExt);
          dCaps[i].fFlushRenderer = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_flush_render", strExt);
          dCaps[i].fPixelBuffer = 
              gluCheckExtension ((const GLubyte *) "GL_APPLE_pixel_buffer", strExt);
          dCaps[i].fImaging = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fTransposeMatrix = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_transpose_matrix", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fMultitexture = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_multitexture", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexEnvAdd = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_env_add", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_env_add", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexEnvCombine = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_env_combine", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexEnvDot3 = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_env_dot3", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexEnvCrossbar = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_env_crossbar", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fTexCubeMap = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_cube_map", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexCompress = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_compression", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fMultisample = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_multisample", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fTexBorderClamp = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_border_clamp", strExt) ||
              (dCaps[i].glVersion >= 0x0130);
          dCaps[i].fPointParam = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_point_parameters", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fVertexProg = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_vertex_program", strExt);
          dCaps[i].fFragmentProg = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_fragment_program", strExt);
          dCaps[i].fTexMirrorRepeat = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_mirrored_repeat", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fDepthTex = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_depth_texture", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fShadow = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_shadow", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fShadowAmbient = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_shadow_ambient", strExt);
          dCaps[i].fVertexBlend = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_vertex_blend", strExt);
          dCaps[i].fWindowPos = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_window_pos", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fTex3D = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture3D", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fClipVolHint = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_clip_volume_hint", strExt);
          dCaps[i].fRescaleNorm = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_rescale_normal", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fBlendColor = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_blend_color", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fBlendMinMax = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_blend_minmax", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fBlendSub = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_blend_subtract", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fCVA = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_compiled_vertex_array", strExt);
          dCaps[i].fTexLODBias = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_lod_bias", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fABGR = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_abgr", strExt);
          dCaps[i].fBGRA = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_bgra", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fTexFilterAniso = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_filter_anisotropic",strExt);
          dCaps[i].fPaletteTex = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_paletted_texture", strExt);
          dCaps[i].fShareTexPalette = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_shared_texture_palette", strExt);
          dCaps[i].fSecColor = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_secondary_color", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fTexCompressS3TC = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_compression_s3tc", strExt);
          dCaps[i].fTexRect = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_rectangle", strExt);
          dCaps[i].fFogCoord = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_fog_coord", strExt);
          dCaps[i].fDrawRangeElements = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_draw_range_elements", strExt);
          dCaps[i].fStencilWrap = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_stencil_wrap", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fBlendFuncSep = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_blend_func_separate", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fMultiDrawArrays = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_multi_draw_arrays", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fShadowFunc = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_shadow_funcs", strExt);
          dCaps[i].fStencil2Side = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_stencil_two_side", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fColorSubtable = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_color_subtable", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fConvolution = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_convolution", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fHistogram = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_histogram", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fColorTable = 
              gluCheckExtension ((const GLubyte *) "GL_SGI_color_table", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fColorMatrix = 
              gluCheckExtension ((const GLubyte *) "GL_SGI_color_matrix", strExt) || 
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fTexEdgeClamp = 
              gluCheckExtension ((const GLubyte *) "GL_SGIS_texture_edge_clamp", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fGenMipmap = 
              gluCheckExtension ((const GLubyte *) "GL_SGIS_generate_mipmap", strExt);
          dCaps[i].fTexLOD = 
              gluCheckExtension ((const GLubyte *) "GL_SGIS_texture_lod", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fPointCull = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_point_cull_mode", strExt);
          dCaps[i].fTexMirrorOnce = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_texture_mirror_once", strExt);
          dCaps[i].fPNtriangles = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_pn_triangles", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_ATIX_pn_triangles", strExt);
          dCaps[i].fTextFragShader = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_text_fragment_shader", strExt);
          dCaps[i].fATIBlendEqSep = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_blend_equation_separate", strExt);
          dCaps[i].fBlendWeightMinMax = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_blend_weighted_minmax", strExt);
          dCaps[i].fCombine3 = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_texture_env_combine3", strExt);
          dCaps[i].fSepStencil = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_separate_stencil", strExt);
          dCaps[i].fArrayRevComps4Byte = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_array_rev_comps_in_4_bytes",strExt);
          dCaps[i].fNVPointSprite = 
              gluCheckExtension ((const GLubyte *) "GL_NV_point_sprite", strExt);
          dCaps[i].fRegCombiners = 
              gluCheckExtension ((const GLubyte *) "GL_NV_register_combiners", strExt);
          dCaps[i].fRegCombiners2 = 
              gluCheckExtension ((const GLubyte *) "GL_NV_register_combiners2", strExt);
          dCaps[i].fTexEnvCombine4 = 
              gluCheckExtension ((const GLubyte *) "GL_NV_texture_env_combine4", strExt);
          dCaps[i].fBlendSquare = 
              gluCheckExtension ((const GLubyte *) "GL_NV_blend_square", strExt) ||
              (dCaps[i].glVersion >= 0x0140);
          dCaps[i].fFogDist = 
              gluCheckExtension ((const GLubyte *) "GL_NV_fog_distance", strExt);
          dCaps[i].fMultisampleFilterHint = 
              gluCheckExtension ((const GLubyte *) "GL_NV_multisample_filter_hint", strExt);
          dCaps[i].fTexGenReflect = 
              gluCheckExtension ((const GLubyte *) "GL_NV_texgen_reflection", strExt);
          dCaps[i].fTexShader = 
              gluCheckExtension ((const GLubyte *) "GL_NV_texture_shader", strExt);
          dCaps[i].fTexShader2 = 
              gluCheckExtension ((const GLubyte *) "GL_NV_texture_shader2", strExt);
          dCaps[i].fTexShader3 = 
              gluCheckExtension ((const GLubyte *) "GL_NV_texture_shader3", strExt);
          dCaps[i].fDepthClamp = 
              gluCheckExtension ((const GLubyte *) "GL_NV_depth_clamp", strExt);
          dCaps[i].fLightMaxExp = 
              gluCheckExtension ((const GLubyte *) "GL_NV_light_max_exponent", strExt);
          dCaps[i].fRasterPosClip = 
              gluCheckExtension ((const GLubyte *) "GL_IBM_rasterpos_clip", strExt);
          dCaps[i].fConvBorderModes = 
              gluCheckExtension ((const GLubyte *) "GL_HP_convolution_border_modes", strExt) ||
              gluCheckExtension ((const GLubyte *) "GL_ARB_imaging", strExt);
          dCaps[i].fAuxDeptStencil =
              gluCheckExtension ((const GLubyte *) "GL_APPLE_aux_depth_stencil", strExt);
          dCaps[i].fFlushBufferRange =
              gluCheckExtension ((const GLubyte *) "GL_APPLE_flush_buffer_range", strExt);
          dCaps[i].fObjectPurgeable =
              gluCheckExtension ((const GLubyte *) "GL_APPLE_object_purgeable", strExt);
          dCaps[i].fDrawBuffers =
              gluCheckExtension ((const GLubyte *) "GL_ARB_draw_buffers", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fFragmentProgShadow =
              gluCheckExtension ((const GLubyte *) "GL_ARB_fragment_program_shadow", strExt);
          dCaps[i].fFragmentShader = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_fragment_shader", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fHalfFloatPixel = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_half_float_pixel", strExt);
          dCaps[i].fOcclusionQuery = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_occlusion_query", strExt) ||
              (dCaps[i].glVersion >= 0x0150);
          dCaps[i].fPBO = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_pixel_buffer_object", strExt) ||
              (dCaps[i].glVersion >= 0x0210);
          dCaps[i].fPointSprite =           
              gluCheckExtension ((const GLubyte *) "GL_ARB_point_sprite", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fShaderObjects = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_shader_objects", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fShaderTextureLOD = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_shader_texture_lod", strExt);
          dCaps[i].fShadingLanguage100 =
              gluCheckExtension ((const GLubyte *) "GL_ARB_shading_language_100", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fTexFloat =
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_float", strExt);
          dCaps[i].fTexNPOT = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_texture_non_power_of_two", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fVBO = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_vertex_buffer_object", strExt) ||
              (dCaps[i].glVersion >= 0x0150);
          dCaps[i].fVertexShader = 
              gluCheckExtension ((const GLubyte *) "GL_ARB_vertex_shader", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fTexComp3dc = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_texture_compression_3dc", strExt);
          dCaps[i].fTexATIfloat = 
              gluCheckExtension ((const GLubyte *) "GL_ATI_texture_float", strExt);
          dCaps[i].fBlendEqSep = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_blend_equation_separate", strExt) ||
              (dCaps[i].glVersion >= 0x0200);
          dCaps[i].fDepthBounds = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_depth_bounds_test", strExt);
          dCaps[i].fFBOblit =
              gluCheckExtension ((const GLubyte *) "GL_EXT_framebuffer_blit", strExt);
          dCaps[i].fFBO = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_framebuffer_object", strExt);
          dCaps[i].fGeometryShader4 = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_geometry_shader4", strExt);
          dCaps[i].fGPUProgParams = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_gpu_program_parameters", strExt);
          dCaps[i].fGPUShader4 = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_gpu_shader4", strExt);
          dCaps[i].fDepthStencil = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_packed_depth_stencil", strExt);
          dCaps[i].fSepSpecColor = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_separate_specular_color", strExt) ||
              (dCaps[i].glVersion >= 0x0120);
          dCaps[i].fTexCompDXT1 =
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_compression_dxt1", strExt);
          dCaps[i].fTexMirrorClamp = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_mirror_clamp", strExt);
          dCaps[i].fTexSRGB = 
              gluCheckExtension ((const GLubyte *) "GL_EXT_texture_sRGB", strExt) ||
              (dCaps[i].glVersion >= 0x0210);         
          
          if (dCaps[i].fTexRect) // only check if extension supported
                glGetIntegerv (GL_MAX_RECTANGLE_TEXTURE_SIZE_EXT,
                               &dCaps[i].maxRectTextureSize);
            else
                dCaps[i].maxRectTextureSize = 0;
            
          CGLDestroyContext (cglContext);
        }
      }
      CGLSetCurrentContext (curr_ctx); // reset current CGL context
    }
  }
}