Load OpenGL Functions

From OpenGL Wiki
Jump to navigation Jump to search

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[edit]

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.

Windows[edit]

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 context will work in the other. 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.

While the MSDN documentation says that wglGetProcAddress returns NULL on failure, some implementations will return other values. 1, 2, and 3 are used, as well as -1.

wglGetProcAddress will not return function pointers from any OpenGL functions that are directly exported by the OpenGL32.DLL itself. This means the old ones from OpenGL version 1.1. Fortunately those functions can be obtained by the Win32's GetProcAddress. On the other hand GetProcAddress will not work for the functions for which wglGetProcAddress works. So in order to get the address of any GL function one can try with wglGetProcAddress and if it fails, try again with the Win32's GetProcAddress:

void *GetAnyGLFuncAddress(const char *name)
{
  void *p = (void *)wglGetProcAddress(name);
  if(p == 0 ||
    (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
    (p == (void*)-1) )
  {
    HMODULE module = LoadLibraryA("opengl32.dll");
    p = (void *)GetProcAddress(module, name);
  }

  return p;
}

Linux and X-Windows[edit]

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.

MacOSX[edit]

GL functions on OSX have been weak linked since OSX 10.2; this means that you can call them directly and unimplemented extensions will resolve to NULL. Note that this means that you must parse the extension string to determine if a function is valid or not, or your program will crash. Apple recommends that programs which need getProcAddress functionality use NSSymbol to look up the function pointer directly. Sample code is provided in their documentation to accomplish this.

Function Prototypes[edit]

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 glxext.h and 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 (whose 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:

PFNGLUSEPROGRAMPROC glUseProgram;

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.

Function Retrieval[edit]

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.

In modern, post GL 3.0 code, the correct 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. Because glGetStringi is not a GL 1.1 function, you will need to load this function before using it on Windows machines.

This functionality does not allow you to directly search for a particular extension. 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.

Platform-specific Extensions[edit]

WGL and GLX themselves can be extended. With a few exceptions, WGL and GLX extensions are not advertised in the GL_EXTENSIONS string.

Windows[edit]

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 wglext.h.

Linux and X-Windows[edit]

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 glxext.h.