November 10, 2016

Alpha Versions of DerelictUtil/SDL2/GL3/FI

For a little while now I’ve been working locally on some major updates to a few of the Derelict packages. I’ve decided to go ahead and tag some alpha releases for DUB of what I’ve got so far in order to make sure it works for others besides me. With the exception of one package, I’ve been using them locally without any problems. I’ll summarize the changes here for anyone who wants to try them out.

DerelictUtil version 3.0.0-alpha.1

This is the spark that ignited the changes in the other packages. Because it’s binary-incompatible with the existing 2.x versions, every Derelict package will have to be updated to use it in order for them all to live happily in the same application. This includes the “unofficial” Derelict packages out there that are built on DerelictUtil. Once all of the DerelictOrg packages are updated, other packages will not work with the latest versions unless they upgrade to DerelictUtil 3.0.0 as well.

Here are the major changes:

  • The xtypes and wintypes modules are gone.
  • The system module has the new manifest constants Derelict_Arch_32 and Derelict_Arch_64.
  • SharedLibLoader.bindFunc is now public.
  • A new public function, bindFunc_stdcall, has been added to SharedLibLoader. Some bindings, like DerelictFI, need this on Windows and had implemented it locally. Now it can be used by any package that needs it.
  • @nogc, @property and nothrow have been added to SharedLib and SharedLibLoader where appropriate.
  • __FILE__ and __LINE__ were added to the DerelictException constructors. This was also added to 2.0 and released under the 2.1 tag for anyone that wants it now, but since I’m in the middle of upgrading everything to 3.0, I didn’t bother upgrading the existing packages to 2.1.

If you maintain a Derelict binding, I recommend you start testing this out with version=~>3.0.0-alpha.1 in a new branch to make sure nothing is broken. I expect to drop the alpha suffix (and any future beta suffix) by the beginning of the new year.

DerelictSDL2 3.0.0-alpha.1

In addition to depending on the new DerelictUtil version, this version of DerelictSDL2 adds support for SDL_gpu (specifically, the API as it exists as of commit 18f04250f00c7c389460c7478851cea6b358a868), along with the option to use static instead of dynamic bindings for all of the SDL2_* libraries. To make any specific binding static, use versions with the syntax PackageName_Static in your dub configurations (or compiler command lines). For example, the following uses DerelictSDLImage as a static binding, but any of the other DerelictSDL2 bindings will be dynamic:

dependency "derelict-sdl2" version="~>3.0.0-alpha.1"
versions="DerelictSDL2Image_Static"

This means you will need to configure your build to link with the SDL2_image static library or either the import library on Windows, or the shared object on other systems. You would still use the load method for the other SDL2 bindings. The version DerelictSDL2_Static will enable the static version of all of the DerelictSDL2 bindings, as will the version Derelict_Static, which will also enable static versions of any Derelict package that supports it.

Alternatively, a subconfiguration can be specified in the dub file.

dependency "derelict-sdl2" version="~>3.0.0-alpha.1"
subConfiguration "derelict-sdl2" "derelict-sdl2-static"

This is the same as specifying the version DerelictSDL2_Static, with the addition that it excludes the dynamic-related modules from the build (though there’s still a compile-time dependency on DerelictUtil). The default configuration is derelict-sdl2-dynamic.

Note that DerelictSDL2ttf has been renamed to DerelictSDL2TTF, but the former is still around as an alias.

DerelictGLFW3 4.0.0-alpha.1

There are no major additions or changes to DerelictGLFW3, other than the dependency on the new DerelictUtil. I was actually going to brand this as version 3.2. However, there has been some confusion about DerelictGLFW3 versions and GLFW3 versions, given that the latter is currently on version 3.2 as well. Since I’m already bumping up the major versions of the other packages, doing so here helps to eliminate that source of confusion.

DerelictGLFW3 3.1 already supported a static configuration, derelict-glfw3-static. In this release, I’ve changed the expected version tag from DerelictGLFW3Static to DerelictGLFW3_Static for consistency. I’ve also added Derelict_Static as a synonym for DerelictGLFW3_Static. Using the subconfiguration instead of the version will exclude the dynamic binding modules from compilation and eliminate the dependency on DerelictUtil.

DerelictGL3 2.0.0-alpha.1

Major, major, breaking changes here. Of course it uses the new DerelictUtil, but the package has also been completely refactored. The first thing to note is that all of the modules now live in the derelict.opengl package (which should now be imported instead of the gl module), rather than derelict.opengl3. And what used to be the gl3 module is now simply gl. The following code does almost what the equivalent with the old package and module name does today.

import derelict.opengl;
void initGraphics() {
    // Create an OpenGL context
    ...
    // Now load OpenGL as usual
    DerelictGL3.load();
    DerelictGL3.reload();
}

The call to reload  will still, by default, load the highest version of OpenGL available in the current context. The difference is that, now, no extensions are loaded by default. In order for extensions to be loaded, you have to mix them in somewhere. The easiest way to do that is to create a module specifically for that purpose. Something like this:

module mypackage.mygl;
public import derelict.opengl;
public import derelict.opengl.extensions.arb_b,
              derelict.opengl.extensions.arb_c;
mixin(arbBindlessTexture); // GL_ARB_bindless_texture
mixin(arbComputeShader);   // GL_ARB_compute_shader

This will ensure that the extensions are declared in your new module and loaded when you call reload. Some extensions, like GL_ARB_compute_shader, have the same function and type names in the extension specification as they do in core OpenGL. In those cases, if the context supports the version where the extension is part of the core, then it will not be loaded as an extension. So with these specific extensions, you can test their availability with the following lines:

import mypackage.mygl;
// It's best to set some variables at start up, so you aren't
// calling isExtensionLoaded every time you need it. This is
// a departure from the current version of DerelictGL3, which
// provided such flags for you.
bool haveBindlessTexture;
bool haveComputeShader;

void setExtensionFlags() {
    // GL_ARB_bindless_texture is a full-on extension with ARB
    // suffixes on the function names, so you can simply check
    // if it has been loaded
    haveBindlessTexture = 
        DerelictGL3.isExtensionLoaded(ARB_bindless_texture);

    // GL_ARB_compute_shader may be loaded as an extension or as
    // part of OpenGL 4.3. It's necessary to check for both unless
    // you know you will never have a 4.3 context.
    haveComputeShader = 
        DerelictGL3.loadedVersion >= GLVersion.gl43 || 
        DerelictGL3.isExtensionSupported(ARB_compute_shader);
}

This requires detailed knowledge about which extensions share declarations with a core version and which don’t. That information can be found online, and it’s also easy to see in the comments of the arb_*.d modules, but eventually I’ll have a list of everything in the online docs.

It’s also possible now to specify a maximum version of OpenGL to support at compile-time. This will only include the type, constant and function declarations up to and including the version you specify. The default includes everything up to the highest supported version (which is currently 4.5). To take advantage of this, you have to mixin a template with the desired version number, like so:

module mypackage.mygl;
public import derelict.opengl;
mixin glFreeFuncs!(GLVersion.gl33);

This example will configure DerelictGL3 to only include declarations up to and including OpenGL 3.3. Extensions can still be mixed in as in the previous example (note again, this is a template mixin, where as the extensions are string mixins — I’ve settled on using the template mixins where compile-time parameters are needed and the string mixins elsewhere). Finally, the version DerelictGL3_CustomFreeFuncs needs to be specified in your dub configuration (or compiler command line), otherwise DerelictGL3 will mixin its own declarations and conflicts will arise.

Another major new feature is support for context objects. Essentially, this eliminates the free functions and wraps them up in a struct. Instances can then be loaded for each OpenGL context you activate in your program. This way, there’s no need to call DerelictGL3.reload every time you switch contexts. Just load a context object once when the context is created and it will have the proper function pointers as long as the context is valid.

The default implementation, as with the free function approach, includes support for up to OpenGL 4.5 and no extensions. It can be declared like so:

module mypackage.mygl;
public import derelict.opengl;
mixin glContext;
GLContext myContext;

You will also need to specify the version DerelictGL3_Contexts in your dub configuration or compiler command line. Additionally, you can specify a maximum OpenGL version to support.

mixin glContext!(GLVersion.gl33);

With both approaches, you can then declare an instance of GLContext to load like so.

void initGraphics() {
    // You still need to call DerelictGL3.load at some point,
    // but this does not load *any* OpenGL functions when
    // when using contexts -- not even the base 1.1 functions.
    // It will only load the library.
    DerelictGL3.load();

    // Create your OpenGL context (Win32, GLFW3, SDL2, whatever)
    ...

    // Now load the context once and never worry about it again.
    myContext.load();

   // Call OpenGL functions through the context
   myContext.glClear(GL_COLOR_BUFFER_BIT);
}

You can declare multiple instances of GLContext and load one for each context in your program. Just make sure you always call the functions on the instance associated with the currently active context.

Unfortunately, it’s not possible to mixin extensions with the default GLContext implementation. Adding support for that would have complicated the implementation a good deal. Instead, it’s easy to declare your own context type and mixin everything you need.

mypackage.mygl;
public import derelict.opengl;

// Make sure DerelicGL3 is configured with context support.
version(DerelictGL3_Contexts) {

    // Assign the desired GL version to a manifest constant so
    // it need not be repeated below. If none is specified in 
    // the template mixins, the highest supported version will 
    // be used.
    enum glCore = GLVersion.gl33;

    // In order for everything to work, a number a few default
    // modules need to be imported. A string mixin handles that.
    mixin(glImports);

    // Next, it's a good idea to mixin the type and constant
    // delcarations outside of the struct declaration.
    mixin glDecls!glCore;

    // Ditto for any extensions you need
    mixin(arbBindlessTextureDecls);
    mixin(arbComputeShaderDecls);

    // Now declare the context type. Call it whatever you want. 
    // You'll need to mixin the functions and their loaders for 
    // all of the supported versions. If you use extensions, 
    // you'll need to mixin their functions and provide a method, 
    // loadExtensions, with the mixed-in loaders.
    struct MyGLContext {
        mixin glFuncs!glCore;    // Function declarations
        mixin glLoaders!glCore;  // Function loader implementations
        
        // Now the extension functions
        mixin(arbBindlesTextureFuncs);
        mixin(arbComputeShaderFuncs);

        // This is optional, but convenient
        bool hasBindlessTexture;
        bool hasComputeShader;


        // Finally, the extension loader
        void loadExtensions()
        {
            // This ensures 'theLoader' is available
            mixin(glLoaderDecls);

            // Extension loader implementations
            with(theLoader) {
                mixin(arbBindlessTextureLoaderImpl);
                mixin(arbComputeShaderLoaderImpl);
            }
        }
    }

    // Declare a context instance
    MyGLContext myContext;      
}
else static assert(0, 
    "This app requires -version=DerelictGL3_Contexts!");

When using a context, the context instance becomes the vehicle for testing for extensions and OpenGL versions, rather than DerelictGL3.

void initGraphics() {
    // All of these methods would normally be called on DerelictGL3 
    myContext.load();  // Instead of DerelictGL3.reload()

    writeln("Context version is ", myContext.contextVersion);
    writeln("Loaded version is ", myContext.loadedVersion);

    // Assuming the custom context from above
    myContext.hasBindlessTexture = 
        myContext.isExtensionLoaded(ARB_bindless_texture);
    myContext.hasComputeShader = 
        myContext.loadedVersion >= GLVersion.gl43 ||
        myContext.isExtensionSupported(ARB_compute_shader);    
}

This is a massive and complex change. I have only scratched the surface in using it myself, so it has not been tested thoroughly. That said, it has worked flawlessly for me so far. I’m eager to discover any bugs that are sure to exist, so I need more people using it.

Currently, the deprecated OpenGL stuff has not been ported over. I intend to get to that before the end of the month. Then you’ll be able to mix that stuff in as well, if you need it.

DerelictFI 3.0.0-alpha.1

There were no public-facing additions or changes here, other than the dependency on the new DerelictUtil. Currently, there is no support for using DerelictFI as a static binding, though it’s likely that I’ll add it soon.

More to Come

Most of the packages in DerelictOrg are going to get upgraded to use DerelictUtil 3.0.0 and will also have alpha releases, though the lesser used and neglected ones (like DerelictCEF and DerelictIL) are not a priority. I expect to have the important packages ready to go by the end of this month. Then, if all goes well, I’ll drop the alpha suffixes in the beginning of the new year.

If you try any of the new stuff, please report any issues you find. Thanks!

October 29, 2016

Support for SDL 2.0.5 in DerelictSDL2

I’ve just tagged a 2.1.0 release of DerelictSDL2. This adds support for the recently released SDL 2.0.5. There’s no need to upgrade your projects if you don’t need any of the new SDL 2.0.5 features.