Loading OpenGL functions

I already wrote about using OpenGL 3.3 with Qt applications, using new style shaders and helper classes for handling shader programs and buffers. But there is one more important thing to do before we can start writing OpenGL 3.3 applications with Qt. The problem is that usually functions and constants from OpenGL 3.3 won't be available even if we have the appropriate libraries and drivers. That's simply how OpenGL works and we have to work around this limitation.

The qgl.h header, which is used by all other headers from the QtOpenGL module, includes <GL/gl.h> (or its equivalent, depending on the platform). However, on Windows this standard header is always compatible with version 1.1 of OpenGL (even if you have the latest Platform SDK), and on systems using recent versions of MESA (including most Linuxes) it's compatible with version 1.3. To have all the new symbols from version 3.3, you need to include <GL/glext.h>, but it also doesn't help much. First, this header is not available on Windows. Second, it only defines typedefs for function pointers that you have to retrieve by yourself using a platform-specific function, because they are not directly exported by the OpenGL library like in case of most other APIs. And even if they were, they may not be available on some platforms, depending on the actual version and available extensions, and you may still want your code to work without some of them.

There are some existing libraries that attempt to solve this problem by automatically loading those functions behind the scenes. The most popular ones are GLEW and GL Load (which is a part of the Unofficial GL SDK). They are cool but both are relatively huge (well over 2 MB of header files and source code) for a simple task of loading a few dozens of functions. They include a bunch of extensions which are not part of the OpenGL 3.3 core profile. They are also meant to completely replace <GL/gl.h>, and although they work with Qt, it's not an elegant solution.

Qt itself also has a rather funny approach to this problem. All classes that require 2.0+ functionality (shaders, buffers, etc.) use an internal header, qglextensions_p.h. It works in a somewhat similar way to those libraries. It defines the function pointer types and constants and then defines macros which replace canonical function names with appropriate entries in an internal structure which is stored in the QGLContext. Obviously we cannot rely on it because it's internal, and besides it only defines a small set of functions and constants which are directly used by Qt.

There is also a public class QGLFunctions which is part of the API, though it's not internally used by Qt. It takes a completely different approach and instead of using macros, it's a class with methods of the same name as canonical OpenGL functions. The recommended way to use it is to inherit this class in each class that needs to use those functions. It seems like a bit of WTF to me. Even worse, it only covers OpenGL/ES 2.0 which is fine for embedded applications, but not enough for a desktop application targeting OpenGL 3.3.

As you can probably guess I came up with a custom solution. The idea is that it only needs to add symbols not already defined in <GL/gl.h>, assuming that it's compatible with at least OpenGL 1.1. It also only covers the OpenGL 3.3 core profile without any additional extensions or features removed from the core profile (though those defined by <GL/gl.h> will still be available). It consists of a header file which is basically a slightly stripped version of gl3.h from the official OpenGL Registry. I basically removed everything pre-1.2 and post-3.3 and some other unnecessary stuff. Another header defines a structure holding all function pointers and all the necessary macro definitions, and a single source file contains code that initializes this structure using a QGLContext, which takes care of retrieving function pointers in a cross-platform way.

The size of all three files is a mere 120 kilobytes. Some day I may publish them as a separate mini-libary, but for now you can find them in the SVN repository of Descend.

Filed under: Blog