The following is a complete program that loads DerelictGL:
import derelict.opengl.gl; void main() { DerelictGL.load(); // now you can call OpenGL functions }
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.
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) { ... } }
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 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.
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.
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 } }