DerelictGL



Introduction

DerelictGL is a D binding to the OpenGL library. Currently, DerelictGL exposes all core OpenGL functions up to version 2.1. However, in order to use features available in OpenGL versions greater than 1.1, you must load them separately after creating an OpenGL context. See below.

Using

  1. Always make sure the DerelictGL source modules are available on your import path.
  2. In modules that make use of DerelictGL, you will need to import the derelict.opengl.gl module.
  3. Before calling any OpenGL functions, you need to make a call to DerelictGL.load(). This will load the shared library and all core OpenGL 1.0 and 1.1 functions.

The following is a complete program that loads DerelictGL:

import derelict.opengl.gl;

void main()
{
    DerelictGL.load();

    // now you can call OpenGL functions
}
Return values and parameters of type GLubyte*, when intended to represent a string, have been declared in DerelictGL as char* instead.

As with other Derelict packages, DerelictGL will throw an exception if an error occurs while loading the shared library. For more information on Derelict exceptions, see the documentation for Loading/Unloading Shared Libraries.

Finally, the method DerelictGL.unload() is provided for convenience. In normal practice you do not need to call this function, as Derelict will unload the library automatically when the app exits. You generally should only use this function if you need to unload DerelictGL while the application is running.

Loading OpenGL Versions 1.2 and Later

OpenGL poses a few issues beyond other libraries that Derelict binds to and therefore requires special-case handling. One problem is the variety of graphics cards and OpenGL versions available on end user systems. If you are developing an OpenGL application for general public consumption, there is no way to guarantee which version of the API will be available on any user's system until the application is actually run on that system.

Another issue is presented by Windows operating systems. The default opengl32.dll on Windows systems prior to Vista only provides exports for OpenGL 1.1. It has not been updated since it was first realeased on Windows 95. Furthermore, this DLL is a (rather poor) software implementation. Vista ships with the software implementation, but also includes an OpenGL DLL that supports up to core OpenGL 1.4 and is implemented on top of Direct3D. This DLL provides hardware acceleration, but all of the calls first go through a translation layer to convert them to D3D calls. The system can load the implementation provided by the graphics card driver, but there is no way to know which implementation the application is actually using, or which version a graphics card driver supports, until the context is created.

Derelict's dynamic loading mechanism helps to work around the first issue. If not for the Windows problem, loading OpenGL version 1.2 and later could be accomplished via the single call to DerelictGL.load. However, in order to maintain a consistent interface across all platforms, DerelictGL includes some additional methods and constants to load and manage different OpenGL versions.

GLVersion
This enum defines all OpenGL versions currently loadable by Derelict. At the time of this writing, these are the values GLVersion defines:

void DerelictGL.loadVersions(GLVersion minVersion)
This method will attempt to load a specific verision of OpenGL. If any version of OpenGL up to and including the version requested fails to load successfully, then this method throws a SharedLibProcLoadException.

Before attempting to load any functions, two checks are made. The first check is to determine if an OpenGL context has been made current. If not, this method will throw an Exception/span>. The method next determines if a check has been made internally for the latest available version supported by the driver. If not, that check is made. Only then are the functions loaded.

When this function returns successfully, a call to DerelictGL.availableVersion will return either a value greater than or equal to the version you requested to load. If an exception is thrown, you can still check the highest version loaded, which will be lower than that which you requested.

GLVersion DerelictGL.availableVersion()
When this method is called, it first checks to determine if the available version has already been set. If not, it will query the driver for the highest version supported and then will attempt to load each supported version. A successful load will result in a return value that indicates the highest loaded version. A failure will result in an Exception if no context is current, or a SharedLibProcLoadException if any function failed to load. Successive calls will always return the latest loaded version as long as a context is valid.

char[] DerelictGL.versionString(GLVersion glv)
Returns the string form of the given version. The format of the string is "Version X.X".

bool DerelictGL.hasValidContext()
Returns true if a context has been made current. This is used internally before attempting any loads that require an active context, but is also publicly available. You may find this useful if you have an application that manages multiple OpenGL contexts.

Examples
Generally, if you want to load a specific version of OpenGL, you should use DerelictGL.loadVersions. Otherwise, you can use DerelictGL.availableVersion to load the latest version available. Following are examples of both:

// Example 1: Requesting a specific version.
import derelict.opengl.gl;
import derelict.util.exception;

void main()
{
    // load OpenGL 1.1
    DerelictGL.load();

    // attempt to load every version up to OpenGL 1.5
    try
    {
        DerelictGL.loadVersions(GLVersion.Version15);
    }
    catch(SharedLibProcLoadException slple)
    {
        // Here, you can check which is the highest version that actually loaded.

        /* Do Something Here */
    }
}
// Example 2: Loading the highest available version.
import derelict.opengl.gl;
import derelict.util.exception;

void main()
{
    // load OpenGL 1.1
    DerelictGL.load();

    // attempt to load the higest GL version supported by the driver
    GLVersion glv;
    try
    {
        glv = DerelictGL.getAvailableVersion();
    }
    catch(SharedLibProcLoadException slple)
    {
        // You might want to abort here if you are worried about a corrupt
        // shared library. Otherwise, you'll want to determine what was
        // actually loaded.
        glv = DerelictGL.getAvailableVersion();
    }

    switch(glv)
    {
        ...
    }

}
It should be noted that Derelict's selective loading mechanism may be used when loading versions later than 1.2. However, it is not recommended you do so unless there is a very compelling reason.

Loading OpenGL Extensions

As an added convenience, DerelictGL includes support for the easy detection and loading of several OpenGL extensions. In the derelict.opengl.extension package are several subpackages. Each subpackage contains loaders for a group of extensions. For example, loaders for GL_EXT extensions are found in derelict.opengl.extension.ext, loaders for GL_ARB extensions in derelict.opengl.extension.arb, loaders for derelict.opengl.extension.nv, and so on.

Naming Conventions

It's important to understand the difference between extension names and name strings. A name string is an extension name prefixed with GL_ (for core OpenGL extensions), or a platform-specific prefix (such as WGL_ on Windows and GLX_ on Linux) for platform-specific extensions. For example, ARB_shadow is an extension name and GL_ARB_shadow is a name string.

In DerelictGL, each supported extension has its own loader. The name of the loader's module is identical to the extension name, minus the prefix. The prefix is the name of the package in which the module can be found. For example, the loader for the extension ARB_shadow_objects resides in the module derelict.opengl.extension.arb.shadow_objects.

The one exception to this rule is the extension EXT_422_pixels. In D, module names cannot begin with a number. For this extension, the module is derelict.opengl.extension.ext.four22_pixels. However, the name of the loader for this extension follows the convention outlined below.

The name of each loader is a modified form of the extension name. Essentially, each lowercase word separated by an underscore is capitalized and the underscores removed, resulting in a camel-case name. Here are some examples:

Take special notice of EXTBgra. It's tempting to write EXTBGRA instead, but in any case where acronyms are part of an extension name, the name of the loader will only capitalize the first letter of the acronym.

Loading Extensions

On some operating systems, it may be possible to load extensions without first creating an OpenGL context. Unfortunately, on Windows, this is not the case. As such, in order to maintain a consistent interface DerelictGL requires that a valid OpenGL context exist before extensions can be loaded. Any attempt to load extensions when no context has been created will cause an exception to be thrown.

Once DerelictGL has been loaded and a context created, you can load all extensions used by your application with one method call: DerelictGL.loadExtensions. Extensions not used by your application will not be loaded. Internally, DerelictGL uses module constructors to track which extensions need to be loaded. So all you need do is to import the extension loader modules you intend you use. There is no requirement that you import them in the same module in which you load them -- you can import them anywhere in your application:

// render.d
module myapp.render;

import derelict.opengl.extension.arb.point_sprite;
import derelict.opengl.extension.nv.texture_rectangle;

...

// init.d
module myapp.init

void loadOpenGL()
{
	// load DerelictGL
	DerelictGL.load();
	
	// create the OpenGL context here (via SDL, Win32, or some other API)
	...
	
	// load all of the extensions you use (in this case ARB_point_sprite and NV_texture_rectangle)
	DerelictGL.loadExtensions();
}

This is a fine compromise between the two techniques often used in the C world: loading each extension individually, or using a third-party library that loads all available extensions.

The load method of each extension loader is publicly exposed, so you can call it directly if you prefer. It's much simpler, however, to use the loadExtensions method of DerelictGL instead.

Using Extensions

Because all extensions used by the application are loaded at once, and because failure to load an extension rarely indicates that an application should terminate, no exceptions are thrown when an extension is not present or fails to load. It would have been possible to use Derelict's selective loading mechanism with extension loading, but the decision was made not to do so simply because it is not an exceptional circumstance for an extension to be unavailable on a user's machine. In fact, it is often expected that some extensions will not be present. Therefore, before attempting to use an extension it is important to check whether or not the extension is available.

Alternate code paths are the norm in OpenGL programming. Typically, developers determine a mimimal OpenGL version and/or extension set to support. If the minimum is not available on the user's machine, the application aborts. Many OpenGL applications also support more advanced features if they are available. For example, some advanced lighting technniques can be implemented with GLSL shaders, but when GLSL is unavailable the application can fall back to a different technique using the fixed-function pipeline for a degraded effect. Some applications may provide for three or four different ways of achieving a single effect.

With this in mind, each extension loader in Derelict exposes the following method: bool isEnabled(). You can call this method to determine if an extension is available and take appropriate action if it is not. Here is an example:

import derelict.extension.arb.vertex_shader;

void doSomethingWithVertexShaderExtension
{
	if(ARBVertexShader.isEnabled())
	{
		// use extension here
	}
	else
	{
		// use fallback, exit program, or whatever you need to do
	}
}

Dependencies

DerelictUtil