Passing OpenGL functions from Platform to Game?

Greetings!

Today I implemented dynamic code reloading in my game and it was AWESOME!

I'm using OpenGL and loading the gl functions myself (not using Glew etc).
I was just wondering what's the best way to give the game access to those functions and still have everything work nice with hot reload?

Previously I had it where the functions are just global, they're initialized in the game and it would just use them.

Having the game store the pointers means that when we reload we'll have to reinitialize them again. So a simple boolean hack would work:

void GameUpdateAndRender(...)
{
if (!glInitialized) { glInit(); glInitialized = true; }
}

Another idea which makes more sense to me is to load the functions in the platform layer, and give the game a pointer to a struct that holds all the functions. And the game would use it like gl.Enable, gl.Viewport, etc.

Only downside I find to that is the fact that I have to pass an extra parameter to auxiliary/helper OpenGL functions (e.g. a function to conveniently load a shader, texture etc)

Was wondering if there's another/better way to go about this!

Thanks!
vexe/elxenoanizd

Edited by vexe on
Make gl variable global, and assign to it at beginning of GameUpdateAndRender - same as HandamdeHero does with platform api functions. Then there won't be need to pass additional argument around.

Edited by Mārtiņš Možeiko on
Maybe another approach is to expose a header file with the gl function prototypes (marked with 'extern') to the game, and still have them be global functions and load them in the platform layer... would that work?
Thanks for the reply mmozeiko! I thought about that too, does sound reasonable. I'll give it a shot.
Extern'ing prototypes wouldn't work. Because when you would be linking game.dll the linker will not be able to find definition of function.

What would work is, you can create function pointers in game layer, export them as data variables from game.dll and then access them with GetProcAddress in main.exe (to fill them in on each reload).

Like this:

game dll:
1
2
3
4
5
__declspec(dllexport) void (*glSomething)();
__declspec(dllexport) void game()
{
  glSomething();
}


main exe:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <windows.h>
#include <stdio.h>

static void realOpenGLFunction()
{
  printf("I'm a real function!\n");
}

int main()
{
  HMODULE h = LoadLibrary("game.dll");
  void** glSomething = (void**)GetProcAddress(h, "glSomething");
  void (*game)() = (void(*)())GetProcAddress(h, "game");

  *glSomething = (void*)&realOpenGLFunction;

  game();
}

Edited by Mārtiņš Možeiko on
I went with the global gl variable approach in game code that gets passed in with the platform's memory struct (like hmh's debug code).

I have a small metaprogram that generates the GL functions and loader code (by parsing glcorearb.h)

GLFunctions.h

[Code]
#ifndef GL_FUNCTIONS_H
#define GL_FUNCTIONS_H
#include <GL/glcorearb.h>
typedef void (*glFunction)(void);
glFunction LoadFunction(char *);

struct gl_functions
{
PFNGLCULLFACEPROC CullFace;
PFNGLFRONTFACEPROC FrontFace;
PFNGLHINTPROC Hint;
PFNGLLINEWIDTHPROC LineWidth;
// etc...
};
[/Code]


GLLoader.h

[Code]
#ifndef GL_LOADER_H
#define GL_LOADER_H

#include "GLFunctions.h"
#include <Windows.h>

typedef void(*glFunction)(void);
gl_functions LoadFunctions();

HMODULE GLModule;

struct gl_version
{
int Major;
int Minor;
} GLVersion;

glFunction LoadFunction(char *FunctionName)
{
glFunction Function = (glFunction)wglGetProcAddress(FunctionName);
if (!Function)
Function = (glFunction)GetProcAddress(GLModule, FunctionName);
return Function;
}

bool glInit(gl_functions *Functions)
{
GLModule = LoadLibrary("opengl32.dll");
gl_functions GL = LoadFunctions();
FreeLibrary(GLModule);
GL.GetIntegerv(GL_MAJOR_VERSION, &GLVersion.Major);
GL.GetIntegerv(GL_MINOR_VERSION, &GLVersion.Minor);
*Functions = GL;
return GLVersion.Major >= 3;
}

bool glIsSupported(int Major, int Minor)
{
if (Major < 3 || Major > GLVersion.Major)
return false;
if (Major < GLVersion.Major)
return true;
return Minor <= GLVersion.Minor;
}

gl_functions LoadFunctions()
{
gl_functions Result;
Result.CullFace = (PFNGLCULLFACEPROC)LoadFunction("glCullFace");
Result.FrontFace = (PFNGLFRONTFACEPROC)LoadFunction("glFrontFace");
Result.Hint = (PFNGLHINTPROC)LoadFunction("glHint");
Result.LineWidth = (PFNGLLINEWIDTHPROC)LoadFunction("glLineWidth");
// etc...
return (Result);
}

#endif
[/Code]

Then in my platform layer, I initialize GL and keep a pointer to it in my game_memory:

[Code]
// Initialize GL (_must_ be done after creating GL Context)
gl_functions GLFunctions = {};
Assert(glInit(&GLFunctions));

char GLVersion[128] = {};
sprintf(GLVersion, "OpenGL %s, GLSL %s\n",
GLFunctions.GetString(GL_VERSION), GLFunctions.GetString(GL_SHADING_LANGUAGE_VERSION));
OutputDebugString(GLVersion);

game_memory Memory;
// Initialize persistent and transient memory...
Memory.GLFunctions = GLFunctions;
[/Code]

In the game:

[Code]
#include "GLFunctions.h"

gl_functions gl;

// more include stuff...

void UpdateAndRender(r32 DeltaTime, game_memory *Memory, platform_services *Platform)
{
// platform_services contains Input key values and a GetTime function pointer to get the current time for use in some shader code...

gl = Memory->GLFunctions;
}
[/Code]

Then any GL helper function should be included _after_ the global 'gl' variable, and instead of writing glXXX it writes gl.XXX

Hope somebody else finds this useful. If anybody's interested in the parsing/metaprogram code let me know!

Edited by vexe on
Few comments:

1) You shouldn't do this:
1
2
3
    GLModule = LoadLibrary("opengl32.dll");
    gl_functions GL = LoadFunctions();
    FreeLibrary(GLModule);

After call to FreeLibrary OS can invalidate anything you got from DLL - that includes all the function pointers. It probably works because LoadLibrary doesn't load library, it uses already existing one in memory (because of all the wgl functions). But still that's very unsafe code. Don't free the handle to dynamic library while you are using some stuff from it.

2) Change "gl_functions LoadFunctions()" to "void LoadFunctions(gl_functions* Functions)". There's no reason to pass such large object by value and copy it around.

3) Why not put GLFunctions in platform_services structure instead of game_memory? In my opinion platform functions really belong to platform stuff, not memory stuff.
1) I didn't know that, thanks! Now that you brought this, there's actually a few functions that are null. I'm not sure why, e.g. glGetnTexImage, glGetnUniformdv, glCreateSyncFromCLeventARB, etc. Could it be that they're not supported by my card?

2, 3) Good points! Platform services make more sense.
Those functions are from OpenGL 4.5 version. If your card doesn't support 4.5, then they will be NULL.

Oh, btw you can generate GL function prototypes and constants from official XML specs. No need to parse C header if you don't want it. Specs are available here: https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/
I do support 4.5 AFAIK. Here's what the print out says after loading:

[Code]
OpenGL 4.5.13397 Compatibility Profile Context 15.200.1046.0, GLSL 4.40
[/Code]

Not sure what you mean by XML specs. But that site is where I downloaded glcorearb.h and parsed it (parse and generation code is barely 100 LOC :P)
Btw, I'm still catching up. Did Casey ever find a better way to handle the PDB file of the game DLL other than that batch date code from Stack Overflow?
OpenGL 4.5 doc (https://www.opengl.org/registry/doc/glspec45.compatibility.pdf) says there is GetnTexImage function. Page 297. So something is wrong on your end - code, driver, OS. I don't know.

By xml I mean this file: https://cvs.khronos.org/svn/repos...nk/doc/registry/public/api/gl.xml
It includes all the meta-information about GL functions/enums you need to generate function declarations. In any language.

He later changed data in file name to random number. You can use %RANDOM% to get random number in bat files. After that nothing really changed.

Edited by Mārtiņš Možeiko on
Have to figure those nulls out...

This glcorearb.h seem to have the stuff I want for now (only core profile). This XML file seem to have fixed-function stuff. I'm not an OpenGL expert so I'm trying to stay away from legacy stuff, adding more functions would confuse me :D

Never thought of %RANDOM%. Thanks for the help!
With regards to loading OpenGL functions, I use gl3w https://github.com/skaslev/gl3w

It is very simple to use and best of all, it's in the public domain. I have only needed to create the gl3w.h and gl3w.c file once. You can probably make it into one file too to make it easier to include like an stb library (#define GL3W_IMPLEMENTATION).

When I reload my dll, I just reinitialize the OpenGL function pointers. If you do not send a message to the dll code when it has reloaded, I would suggest you do or just check to see if one of the function pointers in null e.g. `if (glViewport == nullptr) gl3wInit();`.