|
IntroductionOpenGL commands can be divided into two areas, core API and extensions. OpenGL is designed with this in mind and even if a specific command is not in the base specification, one can use a supported extension to access the desired capability. In contrast, other 3D APIs may include a larger group of functions in their core but this is no guarantee of GPU support. Using those features requires additional hardware support checking, lest one end up with undesirable performance for commands which do not have hardware support on the target system. OpenGL guarantees that, if an extension is supported in the OpenGL extensions string or via the core API for a particular renderer, then the full functionality of that particular extension is provided. When looking at what functionality is present in a specific implementation, an application should first look at the core API version for the current renderer, then look to the extensions string.. Core OpenGL APIThe OpenGL API intrinsically supports certain core functionality based on the API version. The definitive reference for a particular version is the OpenGL API specification, which can be found at the opengl.org web site's OpenGL specifications page. Command documentation can also be found in Mac OS X "man" pages and in the book "OpenGL Reference Manual". Note: Within the "gl.h" header file constant definitions for each OpenGL version which the header supports can be found. These will look like Finding the OpenGL API VersionThe OpenGL API version number can be determined using Note: This is not always true, see Finding a Valid Renderer for Functionality Checking section below for more details. The version string is returned in a specific format, with the major and minor version information first followed by vendor specific information (see OpenGL specification section 6.1.11). In practice, the required space between these fields may not be present prior to Mac OS X 10.1, so developers should check for the initial numeric followed by a period and second numeric. Listing 1 shows read and pares the version string, in this case checking for the OpenGL 1.2 or later API version. This code assumes that a context is established and attached to a drawable on the renderer in question (this requirement holds true for all #include <OpenGL/gl.h> // get version string pointer enum { kShortVersionLength = 32 }; // more than enough to hold the version const GLubyte * strVersion = glGetString(GL_VERSION); // get version string // get just the non-vendor specific part of version string GLubyte strShortVersion [kShortVersionLength]; short i = 0; bool fOpenGL12plus = false; while ((((strVersion[i] <= '9') && (strVersion[i] >= '0')) || (strVersion[i] == '.')) && (i < kShortVersionLength)) { // get only basic version info (until first space or not 1-9 or .) strShortVersion [i] = strVersion[i]; i++; } strShortVersion [i] = 0; //truncate string // if we are not version 1.0 or 1.1 then must be 1.2 or later if (!strstr ((const char *) strShortVersion, "1.0") && !strstr ((const char *) strShortVersion, "1.1")) fOpenGL12plus = true; Once the major and minor version is determined one can look for extended functionality. OpenGL provides an extension mechanism for developers to access functionality beyond which the base specification, through extensions. A little background on the extension process is helpful in understanding the design and functionality and overlap of extensions. OpenGL ExtensionsAs the OpenGL API is extended new functionality is normally provided through extensions. These can be vendor specific, multi-vendor, or Architecture Review Board (ARB) approved. The source for information about extensions is again the opengl.org web site's OpenGL extensions page with specific extension descriptions at SGI's OpenGL Extensions Registry. All Mac OS X extensions are detailed in our OpenGL extensions guide. Normally, extensions tend to start life as vendor specific or multi-vendor and then move to multi-vendor or ARB acceptance and possible inclusion in the core specification. This is not required but is just a by product of innovation and forward progress with a cross platform API. Because of this adoption path, more than one extension may cover the same basic functionality and renderers may support one or more of the extensions of interest. There may be an ARB extension with the same or similar functionality as a vendor specific extension and, as this functionality becomes more ubiquitous, it can be moved into the core OpenGL API by the OpenGL ARB. As noted previously, developers should check for the functionality desired both as part of the core API and as an extension, including checking for all extensions that cover the functionality desired. An example of this is the texture environment combiner operations, which can be supported either through the Lastly, while there are different methods which need to be checked to verify certain functionality, OpenGL should continue to export the name strings of promoted extensions in the extensions string, and continue to support the previous versions of extensions which have been exported in early versions of Mac OS X. Thus no extension should ever be removed from the extensions string. This guarantees developers are able check for a feature using a current methodology and reliably continue to use the same methodology in all future versions of Mac OS X. Checking for OpenGL ExtensionsThe extension name strings consists of extension names supported by that particular renderer separated by spaces. The extensions string can be long and one should not copy it to a limited length temporary string but rather, check the string for the extension name in place. The name string for an extension is not the extensions name but the string indicated by the "Name Strings" field of the extension description. Listing 2 shows an example of how to check the extensions string for functionality which maybe in one or more extensions or a version of the core specification, using the GLU convenience function gluCheckExtension. Note, this listing builds on code presented in Listing 1. bool fTexEnvCombine = false; // get extensions string const GLubyte * strExtension = glGetString (GL_EXTENSIONS); if (gluCheckExtension ("GL_ARB_texture_env_combine", strExtension) || gluCheckExtension ("GL_EXT_texture_env_combine", strExtension)) fTexEnvCombine = true; // also check for API support (short version string from above) // if we are not version 1.0, 1.1 or 1.2 then must be 1.3 or later if (!strstr ((const char *) strShortVersion, "1.0") && !strstr ((const char *) strShortVersion, "1.1") && !strstr ((const char *) strShortVersion, "1.2")) fTexEnvCombine = true; To conclude, only after successfully checking the API version or the extensions string for the particular current renderer should an application use the desired command. Once the command is found to be supported in the core of OpenGL or in an extension for the renderer in use, no further work is required. Just include the OpenGL framework, "gl.h" and the "glext.h" header files and call the command as needed (no use of function pointers is required for Mac OS X OpenGL but they could be used depending on what OS versions and OpenGL features are supported by any application, see QA1188: GetProcAdress and OpenGL Entry Points for more information on function pointers). Note: Developers should use caution in enabling untested OpenGL features in their code. History shows when an application automatically enables an untested code path based on OpenGL features which become available after an application is released (such as via a software update) there is high likelihood of application failures. This is not to say allowing for new features is not desireable, just that developers should use care and test thoroughly. IMPORTANT: Having just the command declaration in a header file or even having a function export by a stub library to link against is not enough to ensure the feature is supported by the current renderer. One must ensure the particular renderer in use supports the command. Additionally, ensure the application is coded to support the feature whether it is found in core functionality or an extension, as there can be different constants or command names. Lastly, be careful to use core constants with core command syntax and extension constants with extension commands syntax when applicable. OpenGL Core Functionality Changes per API VersionSince the core OpenGL functionality has changed as different API versions have been adopted, it is convenient to known what basic functionality was added with each API version. The following list shows the changes in core functionality in various OpenGL API versions. Also indicated is the extension from which the functionality is derived. One should consult the actual specification for detailed information on the functionality and how it is implemented, especially since there are likely differences between the implementation presented in the core API and extension on which it is based. Additional detail on the core API changes can be found in the 1.4 OpenGL specification appendix D through F. OpenGL 1.1OpenGL 1.1 adds:
OpenGL 1.2OpenGL 1.2 adds:
OpenGL 1.2.1OpenGL 1.2.1 introduced ARB extensions with no specific core API changes. OpenGL 1.3OpenGL 1.3 adds:
OpenGL 1.4OpenGL 1.4 adds:
OpenGL 1.5OpenGL 1.5 adds:
The functionality above is required to be present if the renderer reports a particular version no matter what extensions are supported. Again, it is worth noting that renderers may or may not support the extensions which the API functionality is based. For example if a renderer reports version 1.3, it may or may not support the Finding a Valid Renderer for Functionality CheckingAs discussed previously, checking functionality requires a renderer attached to a drawable for OpenGL commands to return valid results. When no drawable is attached OpenGL commands are effectively "NO-OPs" thus return invalid results. There are a couple different techniques to ensure a valid rendering environment. First, one can build a pixel format and context then attach this to a valid drawable for the OS API in use, such as a window, view or screen. Since renderers can change with enclosing display changes, one should also ensure the drawable used is correctly positioned on the display of interest (or iterate across the set of displays of interest). While, there is no requirement to make the drawable visible, which allows this action to take place without disturbing the user, it would be desirable to have a method that does not involve creation of a drawable and which could be used by any OS API (CGL, NSOpenGL, or AGL). Listing 3 shows a technique using CGL, which is available to any Mach-O application, to determine the capabilities of a renderer based on the display, even if that application is using NSOpenGL or AGL vice CGL for its interface to OpenGL. This code does not create a drawable but instead uses the OpenGL display mask in the pixel format to limit the pixel format and thus the context, to single display and equivalently, a single renderer. Once this context is created and set current, normal OpenGL calls will be valid for the contexts single renderer. One could use this code, if desired, to loop over available displays and establish capability information for all displays, thus all renderers, without creating a drawable nor being intrusive to the user. #include <OpenGL/OpenGL.h> #include <ApplicationServices/ApplicationServices.h> CGDirectDisplayID display = CGMainDisplayID (); CGOpenGLDisplayMask cglDisplayMask = CGDisplayIDToOpenGLDisplayMask (display); { // check capabilities of display represented by display mask CGLPixelFormatAttribute attribs[] = {kCGLPFADisplayMask, cglDisplayMask, NULL}; CGLPixelFormatObj pixelFormat = NULL; long numPixelFormats = 0; CGLContextObj cglContext = 0; CGLContextObj curr_ctx = CGLGetCurrentContext (); CGLChoosePixelFormat (attribs, &pixelFormat, &numPixelFormats); if (pixelFormat) { CGLCreateContext (pixelFormat, NULL, &cglContext); CGLDestroyPixelFormat (pixelFormat); if (cglContext) { CGLSetCurrentContext (cglContext); // test functionality here CGLDestroyContext (cglContext); } } CGLSetCurrentContext (curr_ctx); // reset current CGL context } Now that we have code to check the OpenGL core version, extension string and set up a valid renderer on any display, we can extend this to create an all inclusive OpenGL functionality checking example which developers can use as is or extend to meet their individual needs. The next two sections cover this example. Detecting Functionality with glCheckglCheck is a set of routines which can be used by OpenGL clients to easily get the OpenGL feature set for all attached displays. The sample code is factored by display as this is a logical division of hardware capabilities. Even though two displays maybe driven by a single hardware device, no display will be driven by more than one hardware device. The glCheck code can be set up prior to using OpenGL and then updated for display re-configuration events. Once initialized, clients can directly access the capability structure to determine if the current OpenGL context has a certain capability. The sample code itself contains flags for all features present at the time of this writing, but clients are free to remove features they are using if they like. This can save some memory but since the feature checking code is called extremely infrequently it is not required for performance purposes. It is important to note that the code encompasses checking for both the core OpenGL version and extension string so clients are freed to just check a simple lightweight flag for a specific feature. glCheck provides a complete and lightweight runtime check which replaces heavy weight runtime calls to The API is very simple and can be called from both Cocoa and Carbon clients. Using the API consists of four steps: initializing the structures and possibly notification routines, updating the structures on display configuration changes, checking for feature or capabilities as needed at runtime, and finally freeing the structures and removing any notification routines on exit. Note: Clients may not need to check for a specific feature in some cases. For example if one knows that a specific renderer needs to be handled specially then a lightweight check for just the renderer identification of the current context can be enough to decide which code path to utilize. It is still important to use care with this technique as a specific renderer's feature set may change and when the client really needs a feature they should really check for that feature. Initialization for both Cocoa and Carbon is accomplished with the code shown in Listing 4. This code establishes global variables to store a list of structures, number of active displays and a universal procedure pointer for an update notification routine if used. Applications are free to store this information as they please, globals are used for simplicity in this example implementation. #include <stdlib.h> #include <Carbon/Carbon.h> #include <ApplicationServices/ApplicationServices.h> #include "glCheck.h" // header code in Listing 8 // configuration info globals GLCaps * gDisplayCaps = NULL; // array of GLCaps CGDisplayCount gNumDisplays = 0; // number of displays // related DM change notification // (required for display config change notifications) DMExtendedNotificationUPP gConfigEDMUPP = NULL; static void getCurrentCaps (void) { if (gDisplayCaps && HaveOpenGLCapsChanged (gDisplayCaps, gNumDisplays)) { // if changed free (gDisplayCaps); // set up to build new ones gDisplayCaps = NULL; } if (!gDisplayCaps) { // if we do not have caps // will just update number of displays CheckOpenGLCaps (0, NULL, &gNumDisplays); gDisplayCaps = (GLCaps*) malloc (sizeof (GLCaps) * gNumDisplays); CheckOpenGLCaps (gNumDisplays, gDisplayCaps, &gNumDisplays); } } While the #include <Cocoa/Cocoa.h> #include <Carbon/Carbon.h> // for display manager functionality #pragma mark ---- Display Manager Event Handling ---- // if notification procedures are used... // update our GL configuration info based on display change notification void handleConfigDMEvent (void *userData, short msg, void *notifyData) { if (kDMNotifyEvent == msg) { // post change notifications only getCurrentCaps (); // from Listing 4 } } // --------------------------------- // called for window resizes, moves and display changes // (resize, depth and display config change) // not required if an application is using display change notifications - (void) update { [super update]; // do any application update stuff here... if (![self inLiveResize]) { // if not doing live resize // this call checks to see if the current config changed in a // reasonably lightweight way to prevent expensive allocations getCurrentCaps (); // from Listing 4 } } // --------------------------------- - (void) awakeFromNib { // do app initialization stuff here... // initialize OpenGL capabilities // (this could be anytime prior to deciding on OpenGL code paths) // get current GL capabilities for all displays getCurrentCaps (); // from Listing 4 // configure display change notification (if desired) gConfigEDMUPP = NewDMExtendedNotificationUPP (handleConfigDMEvent); DMRegisterExtendedNotifyProc (gConfigEDMUPP, NULL, NULL, &psn); } #include <Carbon/Carbon.h> // for display manager functionality #pragma mark ---- Display Manager Event Handling ---- // update our GL configuration info based on display change notification void handleConfigDMEvent (void *userData, short msg, void *notifyData) { if (kDMNotifyEvent == msg) { // post change notifications only getCurrentCaps (); // from Listing 4 } } // --------------------------------- main () { // do client initialization stuff... // initialize OpenGL capabilities // get current GL capabilities for all displays getCurrentCaps (); // from Listing 4 // configure display change notification gConfigEDMUPP = NewDMExtendedNotificationUPP (handleConfigDMEvent); DMRegisterExtendedNotifyProc (gConfigEDMUPP, NULL, NULL, &psn); // client run loop... // client exit stuff... } Checking for specific capabilities is lightweight and fairly simple to accomplish. This processed is designed to be used as needed at application runtime during frame generation. Listing 7 and Listing 8 show Cocoa and Carbon capability checking, specifically in this case for the Note: Using renderer identification to determine renderer capabilities is not fool proof. There can be cases where two identical renderers exist on a system, such as having the same type of AGP and PCI video cards. This will only be an issue when looking at video memory as all other capabilities will always be the same for both cards. If two of the same type of renderers do exist on a system and they have different amounts of video memory then the following technique will yield in-determinant results and may select either renderer depending on the ordering of the display based list. There is no simple way to avoid this matching issue and this is an extremely rare case that will only occur with multiple video cards which are of the exact same GPU type but which have different amounts of video memory. In all other cases finding the current renderer identification will yield correct and completely deterministic results. Given the rareness of this issue, this example does not do any work to avoid these in-determinant cases. Once an application has the current renderer identification they can traverse the array of capabilities, find the structure of interest by matching the renderer and then directly look up the feature desired. This array index can then be used directly for all OpenGL feature determination until one of two events occur, either a context update is needed (see Technical Q&A QA1209, 'Updating OpenGL Contexts' for information on when contexts updates are needed) or a display configuration change occurs which may change the configuration array. Study Listing 7 and Listing 8 for specific details of this example implementation. #include <Cocoa/Cocoa.h> // assumes initialization code from Listing 4 { // this finds the correct renderer via context virtual screen // and pixel format renderer list then checks the capabilities list // for texture rectangle capability (for example) it assumes gDisplayCaps // and gNumDisplays are defined and initialized BOOL hasCap = NO; long renderer; // match renderer using pixel format and context virtual screen [[self pixelFormat] getValues:&renderer forAttribute:NSOpenGLPFARendererID forVirtualScreen: [[self openGLContext] currentVirtualScreen]]; for (i = 0; i < gNumDisplays; i++) { if ((renderer == gDisplayCaps[i].rendererID) && // if we match gDisplayCaps[i].fTexRect) { // and have the capability hasCap = YES; break; } } } #include <agl/agl.h> // assumes initialization code from Listing 4 { // this finds the correct renderer via context virtual screen // and pixel format renderer list then checks the capabilities list // for texture rectangle capability (for example) it assumes gDisplayCaps // and gNumDisplays are defined and initialized Boolean hasCap = false; GLint renderer = 0; // match renderer using pixel format and context virtual screen AGLPixelFormat pf = aglPixFmt; // first PF GLint pfVS = 0, currVS = aglGetVirtualScreen (aglContext); // current VS aglDescribePixelFormat (pf, AGL_VIRTUAL_SCREEN, &pfVS); // get VS for PF while ((pfVS != currVS) && pf) { // while PF's and the VS's do not match pf = aglNextPixelFormat (pf); // get next PF aglDescribePixelFormat (pf, AGL_VIRTUAL_SCREEN, &pfVS); // get VS } if (pf) // if we matched VS (we should) aglDescribePixelFormat(pf, AGL_RENDERER_ID, &renderer); // get rend for (i = 0; i < gNumDisplays; i++) { if ((renderer == gDisplayCaps[i].rendererID) && // if we match DisplayCaps[i]. fTexRect) { // and have the capability hasCap = YES; break; } } } Finally, Listing 9 shows the common code which should be executed on exit to free memory allocated and dispose any callbacks we have installed. The listing assumes the same global variables used in the previous code examples. #include <stdlib.h> #include <Carbon/Carbon.h> // assumes initialization code from Listing 4 // if notification installed, remove display change notification if (gConfigEDMUPP) { // dispose UPP for DM notifications DisposeDMExtendedNotificationUPP (gConfigEDMUPP); gConfigEDMUPP = NULL; } // free memory for display capabilities records if (gDisplayCaps) { free (gDisplayCaps); gDisplayCaps = NULL; } glCheck Source CodeThe glcheck code example in Listing 10 and Listing 11 shows an example implementation of the capability checking discussed in this technical note. This code sample has a header file that defines a simple structure with feature based flags that show what capabilities a particular renderer possesses is detailed in Listing 10. // 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 #include <Carbon/Carbon.h> #include <ApplicationServices/ApplicationServices.h> typedef struct { // developers can add capabilities as required CGDirectDisplayID cgDisplayID; // CG display ID (main identifier) unsigned long displayID; // QD display ID GDHandle hGDevice; // graphics device handle CGOpenGLDisplayMask cglDisplayMask; // CGL display mask // current (at time of look up) device geometry long deviceWidth; // pixel width long deviceHeight; // pixel width long deviceOriginX; // left location of device (relative to main device) long deviceOriginY; // upper location of device (relative to main device) short deviceDepth; // pixel depth in bits short deviceRefresh; // integer refresh rate in Hz // Renderer info long deviceVRAM; // video memory in bytes long deviceTextureRAM; // uses current mode (geometry, pixel depth, etc.) unsigned long rendererID; // renderer ID char strRendererName [256]; // name of hardware renderer char strRendererVendor [256]; // name of hardware renderer vendor char strRendererVersion [256]; // string rep of hardware renderer version bool fullScreenCapable; // does device support full screen // can add more device specs as you want // Renderer Caps long textureUnits; // standard gl path max number of texture units long maxTextureSize; // maximum 1D and 2D texture size supported long max3DTextureSize; // maximum 3D texture size supported long maxCubeMapTextureSize; // maximum cube map texture size supported long maxRectTextureSize; // maximum rectangular texture size supported // OpenGL version support unsigned short glVersion; // bcd gl version (ie. 1.4 is 0x0140) // Functionality bool fSpecularVector; // GL_APPLE_specular_vector bool fTransformHint; // GL_APPLE_transform_hint bool fPackedPixels; // GL_APPLE_packed_pixels or 1.2+ bool fClientStorage; // GL_APPLE_client_storage bool fYCbCr; // GL_APPLE_ycbcr_422 (YUV texturing) bool fTextureRange; // GL_APPLE_texture_range (AGP texturing) bool fFence; // GL_APPLE_fence bool fVAR; // GL_APPLE_vertex_array_range bool fVAO; // GL_APPLE_vertex_array_object bool fElementArray; // GL_APPLE_element_array bool fVPEvals; // GL_APPLE_vertex_program_evaluators bool fFloatPixels; // GL_APPLE_float_pixels bool fFlushRenderer; // GL_APPLE_flush_render bool fPixelBuffer; // GL_APPLE_pixel_buffer bool fImaging; // GL_ARB_imaging (not required in 1.2+) bool fTransposeMatrix; // GL_ARB_transpose_matrix or 1.3+ bool fMultitexture; // GL_ARB_multitexture or 1.3+ bool fTexEnvAdd; // GL_ARB_texture_env_add, GL_EXT_texture_env_add or 1.3+ bool fTexEnvCombine; // GL_ARB_texture_env_combine or 1.3+ bool fTexEnvDot3; // GL_ARB_texture_env_dot3 or 1.3+ bool fTexEnvCrossbar; // GL_ARB_texture_env_crossbar or 1.4+ bool fTexCubeMap; // GL_ARB_texture_cube_map or 1.3+ bool fTexCompress; // GL_ARB_texture_compression or 1.3+ bool fMultisample; // GL_ARB_multisample or 1.3+ (Anti-aliasing) bool fTexBorderClamp; // GL_ARB_texture_border_clamp or 1.3+ bool fPointParam; // GL_ARB_point_parameters or 1.4+ bool fVertexProg; // GL_ARB_vertex_program bool fFragmentProg; // GL_ARB_fragment_program bool fTexMirrorRepeat; // GL_ARB_texture_mirrored_repeat or 1.4+ bool fDepthTex; // GL_ARB_depth_texture or 1.4+ bool fShadow; // GL_ARB_shadow or 1.4+ bool fShadowAmbient; // GL_ARB_shadow_ambient bool fVertexBlend; // GL_ARB_vertex_blend bool fWindowPos; // GL_ARB_window_pos or 1.4+ bool fTex3D; // GL_EXT_texture3D or 1.2+ bool fClipVolHint; // GL_EXT_clip_volume_hint bool fRescaleNorm; // GL_EXT_rescale_normal or 1.2+ bool fBlendColor; // GL_EXT_blend_color or GL_ARB_imaging bool fBlendMinMax; // GL_EXT_blend_minmax or GL_ARB_imaging bool fBlendSub; // GL_EXT_blend_subtract or GL_ARB_imaging bool fCVA; // GL_EXT_compiled_vertex_array bool fTexLODBias; // GL_EXT_texture_lod_bias or 1.4+ bool fABGR; // GL_EXT_abgr bool fBGRA; // GL_EXT_bgra or 1.2+ bool fTexFilterAniso; // GL_EXT_texture_filter_anisotropic bool fPaletteTex; // GL_EXT_paletted_texture bool fShareTexPalette; // GL_EXT_shared_texture_palette bool fSecColor; // GL_EXT_secondary_color or 1.4+ bool fTexCompressS3TC; // GL_EXT_texture_compression_s3tc bool fTexRect; // GL_EXT_texture_rectangle bool fFogCoord; // GL_EXT_fog_coord bool fDrawRangeElements; // GL_EXT_draw_range_elements bool fStencilWrap; // GL_EXT_stencil_wrap or 1.4+ bool fBlendFuncSep; // GL_EXT_blend_func_separate or 1.4+ bool fMultiDrawArrays; // GL_EXT_multi_draw_arrays or 1.4+ bool fShadowFunc; // GL_EXT_shadow_funcs bool fStencil2Side; // GL_EXT_stencil_two_side bool fColorSubtable; // GL_EXT_color_subtable or GL_ARB_imaging bool fConvolution; // GL_EXT_convolution or GL_ARB_imaging bool fHistogram; // GL_EXT_histogram or GL_ARB_imaging bool fColorTable; // GL_SGI_color_table or GL_ARB_imaging bool fColorMatrix; // GL_SGI_color_matrix bool fTexEdgeClamp; // GL_SGIS_texture_edge_clamp or 1.2+ bool fGenMipmap; // GL_SGIS_generate_mipmap or 1.4+ bool fTexLOD; // GL_SGIS_texture_lod or 1.2+ bool fPointCull; // GL_ATI_point_cull_mode bool fTexMirrorOnce; // GL_ATI_texture_mirror_once bool fPNtriangles; // GL_ATI_pn_triangles or GL_ATIX_pn_triangles bool fTextFragShader; // GL_ATI_text_fragment_shader bool fBlendEqSep; // GL_ATI_blend_equation_separate bool fBlendWeightMinMax; // GL_ATI_blend_weighted_minmax bool fCombine3; // GL_ATI_texture_env_combine3 bool fSepStencil; // GL_ATI_separate_stencil bool fArrayRevComps4Byte; // GL_ATI_array_rev_comps_in_4_bytes bool fPointSprite; // GL_NV_point_sprite bool fRegCombiners; // GL_NV_register_combiners bool fRegCombiners2; // GL_NV_register_combiners2 bool fTexEnvCombine4; // GL_NV_texture_env_combine4 bool fBlendSquare; // GL_NV_blend_square or 1.4+ bool fFogDist; // GL_NV_fog_distance bool fMultisampleFilterHint; // GL_NV_multisample_filter_hint bool fTexGenReflect; // GL_NV_texgen_reflection bool fTexShader; // GL_NV_texture_shader bool fTexShader2; // GL_NV_texture_shader2 bool fTexShader3; // GL_NV_texture_shader3 bool fDepthClamp; // GL_NV_depth_clamp bool fLightMaxExp; // GL_NV_light_max_exponent bool fConvBorderModes; // GL_HP_convolution_border_modes or GL_ARB_imaging bool fRasterPosClip; // GL_IBM_rasterpos_clip } GLCaps; // 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); // 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 maxDisplays, GLCaps * aDisplayCaps, CGDisplayCount * dspyCnt); The source code, as shown in Listing 11, has a number of sections. In the first section, displays are enumerated and iterated on resulting in an a table with a capabilities structure for each display. The next section information is gathered about each display and renderer in turn. First basic CoreGraphics and Carbon display information to ease use by either Cocoa or Carbon applications. Next, the Carbon device information is used to retrieve renderer full screen and memory limits. Finally we use our CGL code to create a rendering context and retrieve specific OpenGL info for all supported functionality and some example limits. Developers are free to reduce or expand this checking as required for their specific application. Lastly, as covered previously, #include "glCheck.h" #include <OpenGL/OpenGL.h> #include <AGL/agl.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 error; short i; CGDisplayCount newDspyCnt = 0; if (NULL == aDisplayCaps) return 1; error = CGGetActiveDisplayList(maxDisplays, activeDspys, &newDspyCnt); // if error getting list mark as changed if (error) 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 err = 0; long deviceVRAM; // video memory in bytes unsigned long rendererID; // renderer ID err = CGLQueryRendererInfo (aDisplayCaps[i].cglDisplayMask, &info, &numRenderers); if(0 == err) { 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, &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 err; short i; short size = sizeof (GLCaps); // no devices *dCnt = 0; if (maxDspys == 0) { // find number of displays *dCnt = 0; err = CGGetActiveDisplayList (32, dspys, dCnt); if (err) // err 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; err = CGGetActiveDisplayList(maxDspys, dspys, dCnt); if (err) return; // err 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); } { // find gDevice device by bounds GDHandle hGD; for (hGD = GetDeviceList (); hGD; hGD = GetNextDevice (hGD)) { if (!TestDeviceAttribute (hGD, screenDevice) || !TestDeviceAttribute (hGD, screenActive)) continue; // if postion and sizes match if (((*hGD)->gdRect.top == dCaps[i].deviceOriginY) && ((*hGD)->gdRect.left == dCaps[i].deviceOriginX) && (((*hGD)->gdRect.bottom - (*hGD)->gdRect.top) == dCaps[i].deviceHeight) && (((*hGD)->gdRect.right - (*hGD)->gdRect.left) == dCaps[i].deviceWidth)) { dCaps[i].hGDevice = hGD; break; } } if (dCaps[i].hGDevice == NULL) return; // err if (noErr != DMGetDisplayIDByGDevice (dCaps[i].hGDevice, &dCaps[i].displayID, false)) dCaps[i].displayID = 0; // err getting display ID } { // get renderer info based on gDevice CGLRendererInfoObj info; long j, numRenderers = 0, rv = 0; CGLError err = 0; err = CGLQueryRendererInfo (dCaps[i].cglDisplayMask, &info, &numRenderers); if(0 == err) { 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, NULL }; 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, strRend, 255); strVend = glGetString (GL_VENDOR); strncpy (dCaps[i].strRendererVendor, strVend, 255); strVers = glGetString (GL_VERSION); strncpy (dCaps[i].strRendererVersion, 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 ("GL_APPLE_specular_vector", strExt); dCaps[i].fTransformHint = gluCheckExtension ("GL_APPLE_transform_hint", strExt); dCaps[i].fPackedPixels = gluCheckExtension ("GL_APPLE_packed_pixels", strExt) || gluCheckExtension ("GL_APPLE_packed_pixel", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fClientStorage = gluCheckExtension ("GL_APPLE_client_storage", strExt); dCaps[i].fYCbCr = gluCheckExtension ("GL_APPLE_ycbcr_422", strExt); dCaps[i].fTextureRange = gluCheckExtension ("GL_APPLE_texture_range", strExt); dCaps[i].fFence = gluCheckExtension ("GL_APPLE_fence", strExt); dCaps[i].fVAR = gluCheckExtension ("GL_APPLE_vertex_array_range", strExt); dCaps[i].fVAO = gluCheckExtension ("GL_APPLE_vertex_array_object", strExt); dCaps[i].fElementArray = gluCheckExtension ("GL_APPLE_element_array", strExt); dCaps[i].fVPEvals = gluCheckExtension("GL_APPLE_vertex_program_evaluators",strExt); dCaps[i].fFloatPixels = gluCheckExtension ("GL_APPLE_float_pixels", strExt); dCaps[i].fFlushRenderer = gluCheckExtension ("GL_APPLE_flush_render", strExt); dCaps[i].fPixelBuffer = gluCheckExtension ("GL_APPLE_pixel_buffer", strExt); dCaps[i].fImaging = gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fTransposeMatrix = gluCheckExtension ("GL_ARB_transpose_matrix", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fMultitexture = gluCheckExtension ("GL_ARB_multitexture", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexEnvAdd = gluCheckExtension ("GL_ARB_texture_env_add", strExt) || gluCheckExtension ("GL_EXT_texture_env_add", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexEnvCombine = gluCheckExtension ("GL_ARB_texture_env_combine", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexEnvDot3 = gluCheckExtension ("GL_ARB_texture_env_dot3", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexEnvCrossbar = gluCheckExtension ("GL_ARB_texture_env_crossbar", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fTexCubeMap = gluCheckExtension ("GL_ARB_texture_cube_map", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexCompress = gluCheckExtension ("GL_ARB_texture_compression", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fMultisample = gluCheckExtension ("GL_ARB_multisample", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fTexBorderClamp = gluCheckExtension ("GL_ARB_texture_border_clamp", strExt) || (dCaps[i].glVersion >= 0x0130); dCaps[i].fPointParam = gluCheckExtension ("GL_ARB_point_parameters", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fVertexProg = gluCheckExtension ("GL_ARB_vertex_program", strExt); dCaps[i].fFragmentProg = gluCheckExtension ("GL_ARB_fragment_program", strExt); dCaps[i].fTexMirrorRepeat = gluCheckExtension ("GL_ARB_texture_mirrored_repeat", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fDepthTex = gluCheckExtension ("GL_ARB_depth_texture", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fShadow = gluCheckExtension ("GL_ARB_shadow", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fShadowAmbient = gluCheckExtension ("GL_ARB_shadow_ambient", strExt); dCaps[i].fVertexBlend = gluCheckExtension ("GL_ARB_vertex_blend", strExt); dCaps[i].fWindowPos = gluCheckExtension ("GL_ARB_window_pos", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fTex3D = gluCheckExtension ("GL_EXT_texture3D", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fClipVolHint = gluCheckExtension ("GL_EXT_clip_volume_hint", strExt); dCaps[i].fRescaleNorm = gluCheckExtension ("GL_EXT_rescale_normal", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fBlendColor = gluCheckExtension ("GL_EXT_blend_color", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fBlendMinMax = gluCheckExtension ("GL_EXT_blend_minmax", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fBlendSub = gluCheckExtension ("GL_EXT_blend_subtract", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fCVA = gluCheckExtension ("GL_EXT_compiled_vertex_array", strExt); dCaps[i].fTexLODBias = gluCheckExtension ("GL_EXT_texture_lod_bias", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fABGR = gluCheckExtension ("GL_EXT_abgr", strExt); dCaps[i].fBGRA = gluCheckExtension ("GL_EXT_bgra", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fTexFilterAniso = gluCheckExtension ("GL_EXT_texture_filter_anisotropic",strExt); dCaps[i].fPaletteTex = gluCheckExtension ("GL_EXT_paletted_texture", strExt); dCaps[i].fShareTexPalette = gluCheckExtension ("GL_EXT_shared_texture_palette", strExt); dCaps[i].fSecColor = gluCheckExtension ("GL_EXT_secondary_color", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fTexCompressS3TC = gluCheckExtension ("GL_EXT_texture_compression_s3tc", strExt); dCaps[i].fTexRect = gluCheckExtension ("GL_EXT_texture_rectangle", strExt); dCaps[i].fFogCoord = gluCheckExtension ("GL_EXT_fog_coord", strExt); dCaps[i].fDrawRangeElements = gluCheckExtension ("GL_EXT_draw_range_elements", strExt); dCaps[i].fStencilWrap = gluCheckExtension ("GL_EXT_stencil_wrap", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fBlendFuncSep = gluCheckExtension ("GL_EXT_blend_func_separate", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fMultiDrawArrays = gluCheckExtension ("GL_EXT_multi_draw_arrays", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fShadowFunc = gluCheckExtension ("GL_EXT_shadow_funcs", strExt); dCaps[i].fStencil2Side = gluCheckExtension ("GL_EXT_stencil_two_side", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fColorSubtable = gluCheckExtension ("GL_EXT_color_subtable", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fConvolution = gluCheckExtension ("GL_EXT_convolution", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fHistogram = gluCheckExtension ("GL_EXT_histogram", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fColorTable = gluCheckExtension ("GL_SGI_color_table", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fColorMatrix = gluCheckExtension ("GL_SGI_color_matrix", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); dCaps[i].fTexEdgeClamp = gluCheckExtension ("GL_SGIS_texture_edge_clamp", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fGenMipmap = gluCheckExtension ("GL_SGIS_generate_mipmap", strExt); dCaps[i].fTexLOD = gluCheckExtension ("GL_SGIS_texture_lod", strExt) || (dCaps[i].glVersion >= 0x0120); dCaps[i].fPointCull = gluCheckExtension ("GL_ATI_point_cull_mode", strExt); dCaps[i].fTexMirrorOnce = gluCheckExtension ("GL_ATI_texture_mirror_once", strExt); dCaps[i].fPNtriangles = gluCheckExtension ("GL_ATI_pn_triangles", strExt) || gluCheckExtension ("GL_ATIX_pn_triangles", strExt); dCaps[i].fTextFragShader = gluCheckExtension ("GL_ATI_text_fragment_shader", strExt); dCaps[i].fBlendEqSep = gluCheckExtension ("GL_ATI_blend_equation_separate", strExt); dCaps[i].fBlendWeightMinMax = gluCheckExtension ("GL_ATI_blend_weighted_minmax", strExt); dCaps[i].fCombine3 = gluCheckExtension ("GL_ATI_texture_env_combine3", strExt); dCaps[i].fSepStencil = gluCheckExtension ("GL_ATI_separate_stencil", strExt); dCaps[i].fArrayRevComps4Byte = gluCheckExtension ("GL_ATI_array_rev_comps_in_4_bytes",strExt); dCaps[i].fPointSprite = gluCheckExtension ("GL_NV_point_sprite", strExt); dCaps[i].fRegCombiners = gluCheckExtension ("GL_NV_register_combiners", strExt); dCaps[i].fRegCombiners2 = gluCheckExtension ("GL_NV_register_combiners2", strExt); dCaps[i].fTexEnvCombine4 = gluCheckExtension ("GL_NV_texture_env_combine4", strExt); dCaps[i].fBlendSquare = gluCheckExtension ("GL_NV_blend_square", strExt) || (dCaps[i].glVersion >= 0x0140); dCaps[i].fFogDist = gluCheckExtension ("GL_NV_fog_distance", strExt); dCaps[i].fMultisampleFilterHint = gluCheckExtension ("GL_NV_multisample_filter_hint", strExt); dCaps[i].fTexGenReflect = gluCheckExtension ("GL_NV_texgen_reflection", strExt); dCaps[i].fTexShader = gluCheckExtension ("GL_NV_texture_shader", strExt); dCaps[i].fTexShader2 = gluCheckExtension ("GL_NV_texture_shader2", strExt); dCaps[i].fTexShader3 = gluCheckExtension ("GL_NV_texture_shader3", strExt); dCaps[i].fDepthClamp = gluCheckExtension ("GL_NV_depth_clamp", strExt); dCaps[i].fLightMaxExp = gluCheckExtension ("GL_NV_light_max_exponent", strExt); dCaps[i].fRasterPosClip = gluCheckExtension ("GL_IBM_rasterpos_clip", strExt); dCaps[i].fConvBorderModes = gluCheckExtension ("GL_HP_convolution_border_modes", strExt) || gluCheckExtension ("GL_ARB_imaging", strExt); 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 } } } This concludes the discussion of OpenGL functionality. Understanding the relation of core to extended commands is key to easily navigating and using OpenGL on all supported platforms. This Technical Note discussed core and extended functionality and how to determine if a certain feature is supported. Furthermore, creating valid rendering contexts was discussed in the context of Mac OS X OpenGL interface API's. Lastly, a concrete example of how to fully check for available system renderers and their functionality is provided. Document Revision History
Posted: 2003-12-29 |
|