14 January 2017

OpenGL – Calling version 1.1+ functions from .net under Windows

I learned that the Windows OpenGL DLL only exports OpenGL 1.1 functions. To use newer functions, you must obtain the function pointer first. Conveniently, the following method is provided to do so:

[DllImport("opengl32", EntryPoint = "wglGetProcAddress", SetLastError = true)]
private static extern IntPtr GetProcAddressExtern(string name);
internal static IntPtr GetProcAddress(string name)
{
    var address = GetProcAddressExtern(name);
    if (address == IntPtr.Zero) throw new ApplicationException($@"Get procedure address for ""{name}""");
    return address;
}

To call the method, you must define a delegate for it, and use it as follows:

private delegate void GenBuffersDelegate(int n, uint[] buffers);
internal static void GenBuffers(int n, uint[] buffers)
{
    var funcptr = GetProcAddress("glGenBuffers");
    var func = Marshal.GetDelegateForFunctionPointer(funcptr);
    func(n, buffers);
}

There is a performance cost however. Therefore it's best to store the function once retrieved. I also used a generic method to make the whole thing more convienient.

private static Dictionary<string, object> Functions = new Dictionary<string, object>();
internal static Del Function<del>() where Del : class
{
    var type = typeof(Del).Name;
    if (Functions.ContainsKey(type))
        return Functions[type] as Del;

    IntPtr ptr = GetProcAddress(type);
    if (ptr == null || ptr == IntPtr.Zero)
        throw new ApplicationException($@"Pointer to OpenGL function ""{type}"" could not be retrieved.");
    var function = Marshal.GetDelegateForFunctionPointer<del>(ptr);
    Functions.Add(type, function);
    return function;
}

The same method can now simply be called like this:

private delegate void glGenBuffers(int n, uint[] ids);
public static void GenBuffers(int n, uint[] ids) => Win.GL.Function<glGenBuffers>()(n, ids);

No comments:

Post a Comment