1

Currently I'm using a C# OpenGL binding automatically generated from the Khronos .spec files found at the registry.

I'm quite satisfied by the bindings quality; here is a function example:

/// <summary>
/// Binding for glGenFramebuffers function.
/// </summary>
/// <remarks>
/// This function belongs to 'ARB_framebuffer_object'.
/// <para>
/// Depending on driver implementation, this routine could call the following (equivalent) routines:
/// - glGenFramebuffers
/// - glGenFramebuffersEXT
/// </para>
/// </remarks>
/// <param name="n">
/// A <see cref="Int32"/>.
/// </param>
/// <param name="framebuffers">
/// A <see cref="UInt32*"/>.
/// This parameter holds data returned from function.
/// </param>
public static void GenFramebuffer(Int32 n, [Out] UInt32[] framebuffers) {
    unsafe {
        fixed (UInt32* fp_framebuffers = framebuffers)
        {
            if      (Delegates.pglGenFramebuffers != null)
                Delegates.pglGenFramebuffers(n, fp_framebuffers);
            else if (Delegates.pglGenFramebuffersEXT != null)
                Delegates.pglGenFramebuffersEXT(n, fp_framebuffers);
            else
                throw new InvalidOperationException("binding point GenFramebuffer cannot be found");
        }
    }
    LogProc("glGenFramebuffers("+n+", "+framebuffers+")");
}

As you can notice, the fixed block is trying to call two different delegates (Delegates.pglGenFramebuffers and Delegates.pglGenFramebuffersEXT).

This is possible since they have the same signature:

[System.Security.SuppressUnmanagedCodeSecurity()]
internal unsafe delegate void glGenFramebuffers(Int32 n, [Out] UInt32* framebuffers);
internal static glGenFramebuffers pglGenFramebuffers = null;

[System.Security.SuppressUnmanagedCodeSecurity()]
internal unsafe delegate void glGenFramebuffersEXT(Int32 n, [Out] UInt32* framebuffers);
internal static glGenFramebuffersEXT pglGenFramebuffersEXT = null;

The delegates have the same signature because the specification (.spec file) is the same for the two routines, introduced by difference extensions.


Previously, the bindings has support only for core, ARB and EXT extentions; the bindings generator simply avoid definition of those routine in the case exists another equivalent one with greater priority.

To increase extension support (stimulated by this SO question), I require to declare delegates and imports declaration also for those routine which have been promoted to ARB or to core implementation, and write a wrapper implementation which calls all equivalent routines (those which are defined).

In this way I got the source declared above.


The problems starts when dealing with 2K+ function declarations. I have a bindings generator since I can't write all the OpenGL bindings.

But how the binding generator can know whether a routine func is semantically equivalent to another routine funcARB or funcEXT (having the same signature)?

I think the only option I have is to write an external file (developer controlled) which lists the exception cases (i.e. two routines having the base same name and same signature are not semantically equivalent).

The final goal should be a mostly collapsed OpenGL binding wrapper library, in order to minize the effort required for managing OpenGL extensions.


After some experiments...

It is possible that two matching extensions are both implemented (i.e. ARB_framebuffer_object and EXT_framebuffer_object). Since entry points are different (different names), I need available all the functions of the both extensions.... but!

If I give a priority to the supported extensions (say ARB have higher priority than EXT, EXT have higher proprity than VENDOR), an implementation proposed in my question is acceptable for a wrapper framework, because the ARB extension is implemented, the framework implementation would prefer it than the EXT implementation (no matter what functionality).

This would work, but the side effect is that this policy is forced to the users of the OpenGL binding (no one, at the moment! So, nobody will complain with this! :) ).

Community
  • 1
  • 1
Luca
  • 11,646
  • 11
  • 70
  • 125

1 Answers1

1

You're going to have to do some legwork, but it's not as bad as you might think. Instead of working at the level of functions, you need to work at the level of extensions.

Either the extension was promoted to core with the exact same functionality, or it was not. If it was not, then it is inappropriate to substitute one for the other. So what you need is a list of extensions who's promotion to core came with modifications.

For example, EXT_FBO was not promoted to ARB_FBO/core with no changes. There were some significant changes, and it would be wrong to just have an application that used the EXT functions use the ARB equivalents without change.

Generating the list of extensions that changed with promotion is not easy. It will require going through the actual specs and seeing what actually changed from one version to the other.

One positive point is that the ARB has been in the habit recently of making so-called "core extensions". These are ARB extensions that perfectly mirror the core behavior, right down to the function names. So ARB_FBO functions and enumerators do not have the "ARB" suffix. Most new ARB extensions fall into this category.

As an alternative, you could use the alias field in the gl.spec file. This field supposedly represents an alternate version of the function. For example, if you look up "GenFramebuffersEXT" in gl.spec, you will find that it has an alias value of "GenFramebuffers". The alias seems to always point from the extension function to the core equivalent. I have absolutely no clue how up-to-date or accurate this information is, but it is something you could use. It wouldn't be too hard to modify your code generator to use the information and see what you get out of it.

From a cursory examination of gl.spec, it seems that the aliases don't follow the rules I outlined above. There are some aliases (like NV_transform_feedback for core transform feedback) that are really not a good idea. NV_transform_feedback allows you to set feedback parameters after you link the program. And while one might certainly want that functionality, the core does not allow that. So if you allow these to alias, a person could accidentally use the NV functionality on an NVIDIA card, and suddenly their code stops working on non-NVIDIA cards.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • Good idea for *alias*. Pity for the 'supposedly' (I'd like a specification of the specification!). However, the framework brings structures which declares which extension is supported or not; these flags can be used for changing semantics without calling different functions each time. – Luca Aug 26 '11 at 09:31
  • Yes, it is a more stable information than the only function name, but there are cases in which the function signatures doesn't match between aliases, so a double check is required; I will explore the issue more deeply later. – Luca Aug 27 '11 at 16:01