Load OpenGL Functions
Loading OpenGL Functions is an important task for initializing OpenGL after creating an OpenGL context. You are strongly advised to use an OpenGL Loading Library instead of a manual process. However, if you want to know how it works manually, read on.
Platform Specific Function Retrieval
Querying the function pointers requires using a function not defined by the OpenGL API. It is a platform-specific function, and the different versions have different semantics.
The function of interest here is wglGetProcAddress. This function takes an ASCII string that exactly matches (case-sensitive) the function name in question. The functions can be OpenGL functions or platform-specific WGL functions.
This function only works in the presence of a valid OpenGL context. Indeed, the function pointers it returns are themselves context-specific. The Windows documentation for this function states that the functions returned may work with another context, depending on the vendor of that context and that context's pixel format.
In practice, if two contexts come from the same vendor and refer to the same GPU, then the function pointers pulled from one will work in another. This is important when creating an OpenGL context in Windows, as you need to create a dummy context to get WGL extension functions to create the real one.
wglGetProcAddress will return functions for extension functions or OpenGL core functions... except for any core functions from version 1.1 or below. You are expected to get those from the Windows <gl.h> file. This makes implementing a cross-platform function loader tricky.
While the MSDN documentation says that it returns NULL on failure, some implementations will return other values. 1, 2, and 3 are used, as well as -1.
Linux and X-Windows
The function glXGetProcAddress (or glXGetProcAddressARB; it is not entirely clear as to which should be used) retrieves function pointers on X-Windows-based systems. This function takes an ASCII string that exactly matches (case-sensitive) the function name in question. The functions can be OpenGL functions or platform-specific GLX functions.
This function can operate without an OpenGL context, though the functions it returns obviously can't. This means that functions are not associated with a context in any way.
This also means that a non-NULL return value does not mean that the function is supported by any particular context. So it is more difficult to go "fishing" for entrypoints on GLX; use the standard OpenGL methods to query for available extensions.
glXGetProcAddress will return valid function pointers for OpenGL extensions and all core versions.
These all depend on getting function pointers. We have seen the functions used to query them; we must also have a place to store them.
Function pointers in C have a type, which includes the return type of the function and the types of its parameter list. This allows C to call into the memory address returned by our function retrieval code. If the actual function signature used by the pointer is different from the one we store the pointer value into, then there will be all kinds of problems.
The OpenGL Registry provides access to a header containing function pointer definitions for every extension and every core function for version 1.2 and above (since 1.1 and below are provided by Windows'
gl.h). This header is called
glext.h. The headers
wglext.h are also provided, containing extensions for GLX and WGL. These headers are kept reasonably up to date with OpenGL releases.
If you are not going to use an OpenGL loading library (and you are still advised to do so), then you must either use one of these headers or generate one yourself. The source of these headers are a number of .spec files (who's format seems obvious, but is never really specified anywhere) also on the OpenGL Registry page.
The ext.h headers do not define actual function pointers themselves; they define the typedefs for function pointers. Let us take glUseProgram as an example. The ext.h file has a typedef as follows:
typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program);
The name of the typedef is PFNGLUSEPROGRAMPROC: Pointer to the FunctioN glUseProgram, which is a PROCedure. The
APIENTRYP is part of a bit of macro magic needed to make everything work.
The use of the typedef allows us to more easily define a function pointer of the appropriate type in our code:
This is not defined by the header; we have to define it ourselves. Most OpenGL loading libraries do not require that you define them yourself.
Once we have an actual function pointer and our function retrieval function, we can get the function pointer in question. However, a question arises: should we be getting this function at all?
If the function is a core OpenGL function, then we need to check the OpenGL version. For OpenGL versions before 3.0, this was done by calling glGetString(GL_VERSION), which returned a string that needed to be parsed. In GL versions 3.0 or greater, we can use glGetIntegerv(GL_MAJOR_VERSION) and GL_MINOR_VERSION to get the major and minor versions. You can know whether you have a 3.0 or greater context if you specifically ask for one at context creation time and actually get it.
Either way, we can compare the version to the expected version for various groups of functions, and then load them or not as needed.
If the function is an extension, we need to check to see if the extension is supported. In OpenGL versions before 3.0, this was done by calling glGetString(GL_EXTENSIONS). This returned a large string of space-separated extension names. This was abandoned, deprecated in 3.0 and removed in 3.1, because it frequently caused parsing problems for users. Some users also tried to use
strcpy to copy the string to a fixed-size buffer, which could easily cause a buffer overrun if the string had too many extensions.
So nowadays, the way to query which extensions are supported is to use glGetIntegerv(GL_NUM_EXTENSIONS) to get the number of extensions, and then use glGetStringi(GL_EXTENSIONS, i), where i is a number between 0 and the number of extensions - 1.
Note that you cannot really search for a particular extension this way. If you need to check for a particular extension, then you need to iterate through the list once and record which extensions are there into some data structure that you can query.
Once you know a function pointer is available, it can be retrieved easily:
//In a header somewhere. #include <glext.h> PFNGLUSEPROGRAMPROC glUseProgram; //In an initialization routine glUseProgram = (PFNGLUSEPROGRAMPROC)wglGetProcAddress("glUseProgram");
Or glXGetProcAddress, as needed. You will need to make the appropriate context the current context for this to work on Windows.
WGL and GLX themselves can be extended. With a few exceptions, WGL and GLX extensions are not advertised in the GL_EXTENSIONS string.
To query WGL extensions, you must first create an OpenGL context. Yes, that seems backwards, but that's how it is. To get the list of WGL extensions, you must create a context, make it current, and call wglGetProcAddress("wglGetExtensionsStringARB"). If this function does not return a valid pointer (see above for how to check), then the list of WGL extension strings is not available. If this pointer is invalid, then the OpenGL extension string may contain some WGL extensions. However, this is very unlikely, as this is an old and widely-implemented extension.
This function returns a string like glGetString(GL_EXTENSIONS): a space-separated list of extensions. So make sure to parse it correctly and don't copy it into a fixed-length buffer.
WGL function retrieval does require an active, current context. However, the use of WGL functions do not. Therefore, you can destroy the context after querying all of the WGL extension functions.
Function typedefs for WGL extensions are available in
Linux and X-Windows
To query GLX extensions, you must first get a list of extensions. To do this, use glXQueryExtensionsString. This function takes a GLX display and a screen number; it does not require an active OpenGL context. It returns a string like glGetString(GL_EXTENSIONS): a space-separated list of extensions. So make sure to parse it correctly and don't copy it into a fixed-length buffer.
From there, parse the string to get the list of GLX extensions, and load them as normal.
Function typedefs for GLX extensions are available in