Qt Weekly #21: Dynamic OpenGL implementation loading in Qt 5.4

In Qt 5.4 there is a new configure option on Windows: -opengl dynamic. This, in addition to -opengl desktop and -opengl es2, changes the OpenGL implementation (and implicitly the window system interface API) that Qt is built against. However, unlike the previously existing flags, dynamic is something different. Enabling it ensures that neither the Qt libraries nor the applications link directly to an OpenGL implementation, meaning that neither opengl32.lib nor libEGL.lib + libGLESv2.lib are passed to the linker. Instead, Qt will, upon application startup, choose the implementation to load.

Starting with the Qt 5.4 release candidate the previously ANGLE-only pre-built packages are changed to be dynamic builds. It is therefore time to take a closer look at this feature.

Up until Qt 5.4 the binary packages of Qt have been offered in two variants: desktop OpenGL (meaning opengl32.dll) and ANGLE. ANGLE provides an EGL and OpenGL ES 2.0 (soon 3.0) implementation by translating calls and shaders to Direct3D9 or 11. This is very handy for systems that have a lacking OpenGL implementation, for example due to not having the necessary graphics drivers or installed or due to using remote desktop. In addition, Direct3D 11 supports WARP, a software rasterizer.

The ability to work on a wider range of systems made ANGLE a promising prospect for providing OpenGL support for Qt and Qt Quick 2 on Windows, and so it became the default configuration setting. This is true in 5.4 too: configure defaults to ANGLE when no -opengl setting is specified.

However, the results were not always pretty. Offering multiple versions of the binaries causes confusion when getting started (which package to download?) and when deploying (which Qt libraries to ship with my application?). Relying exclusively on ANGLE is not an option in practice. Many desktop applications want OpenGL 3.x and 4.x features. On systems where a proper graphics driver is known to be available, the translation to Direct3D is unnecessary. Advanced modes of operation, like rendering from multiple threads have issues with ANGLE. Therefore the "desktop" OpenGL packages, meaning binaries linked against opengl32, had to be made available.

To eliminate the confusion, Qt 5.4 introduces the concept of dynamic OpenGL implementation selection. The binaries built in this mode are capable of operating with opengl32 (using WGL as the windowing system interface and regular OpenGL), ANGLE (using EGL and Open GL ES) or with a custom opengl32 variant (for example Mesa llvmpipe). As expected, Qt will perform some additional steps during application startup to decide which library to load. After that, all OpenGL calls (for example from Qt Quick) will be routed to this library, via QOpenGLFunctions.

The selection algorithm in Qt 5.4 is simple:

  1. Try opengl32 and check if OpenGL 2.0 functions are available.
  2. If this fails, try ANGLE.
  3. If initialization fails either due to missing ANGLE libraries or some other reason, try opengl32sw.dll. In practice this will be a software rasterizer based implementation. To make it easy to get started, a pre-built version of Mesa llvmpipe is bundled with the binary packages of Qt 5.4.

Pro tip: The debug output printed from the platform plugin can be very helpful to check which implementation gets loaded. To enable these prints, set the environment variable QT_LOGGING_RULES to qt.qpa.gl=true.

Why is this logic useful? Consider the following use cases:

  • When running on a modern system with the necessary graphics drivers installed, the first step will succeed so OpenGL is used normally.
  • When running the same application over remote desktop, the first step will fail and the fallback to ANGLE will be used instead. This functions better with remoting, so the application runs, just like locally.
  • Similarly, on a locked-down Windows 7 PC with no vendor-provided drivers installed, opengl32 will often turn out to be useless, providing only OpenGL 1.x. Direct3D and thus ANGLE may still be functional however.
  • In a virtual machine with limited or no accelerated graphics capabilities both step 1 & 2 may fail. In this case a build of Mesa llvmpipe can be utilized automatically, transparently to the application.

Applications that require desktop OpenGL and are not functional with OpenGL ES can use the QT_OPENGL environment variable or the Qt::AA_UseDesktopOpenGL application attribute before instantiating Q(Gui)Application. ANGLE can also be forced, if needed. For example, when playing video with Qt Multimedia, the best, accelerated path relies on Direct3D - ANGLE interop. Using a non-ANGLE implementation will still work, but possibly with reduced performance. Finally, in some scenarios (e.g. when targetting Windows XP) the robust solution is to deploy a software rasterizer (i.e. opengl32sw.dll) and force that with Qt::AA_UseSoftwareOpenGL in order to ensure a uniform experience across all the target systems. For the relevant settings, see the Windows-specific pages in the Qt documentation.

So how do you take this into use? It is easy: with Qt 5.4 the binary packages that previously were using ANGLE are now built with -opengl dynamic instead. This means that the "suffixless" packages that do not have opengl in the name (for instance, qt-enterprise-windows-x86-msvc2013_64-5.4.0.exe) are now capable of operating both with regular, desktop OpenGL, ANGLE and the software fallbacks, if needed. For the time being the "opengl" packages (built with -opengl desktop) are still being offered. However, these may disappear in Qt 5.5.

There is an important consequence of not linking directly to the OpenGL implementation: no OpenGL functions like glClear() are callable directly anymore. Applications that want to be dynamically switchable must always use QOpenGLFunctions and be prepared to function both on OpenGL and OpenGL ES 2.0. For Qt itself, this is not a problem. All the standard Qt modules, including Qt Quick 2 (qtdeclarative), have been updated to make sure they function as expected in all configurations. For examples, see the documentation and our earlier post about QOpenGLWidget.

Note that this dynamic mode of operation and in particular the software rasterizer fallback are targeted towards general GUI applications based on Qt Quick 2. Applications with heavy OpenGL graphics, for example visualization or games, are obviously not in scope here. Such applications are expected to continue to rely exclusively on modern OpenGL and dedicated graphics hardware. They should enforce a given implementation via QT_OPENGL or the application attributes, so they can continue to use direct OpenGL function calls and other features, like the versioned OpenGL function wrappers. Just add LIBS += -lopengl32 to the application's .pro file. The migration of an application that today uses and requires the desktop OpenGL builds of Qt is as simple as adding a line to its main() function and a line to the .pro project file. This is naturally not always an option, and this is the reason why Qt 5.4 continues to offer some of the desktop OpenGL packages. The future however lies in the dynamic builds, so applications are expected to gradually migrate to it.

Blog Topics: