I’ve been doing a fair bit of GL ES coding recently for our upcoming more graphically advanced iPhone titles.
It’s been good fun (on the whole!) even though integrating GL ES with our multi-platform render backend has possibly exposed that our long established flexible render interface doesn’t allow for certain optimisations without introducing certain restrictions (we did encounter the same thing on PS2 but it’s probably a topic for another day).
One nice and simple method for working on optimisation of usage of an API is to use wrapper functions. In terms of graphics API programs such as PIX, gDebugger and other 3D analysis/ripping tools are (for the most part) intercepting the function calls from the application and then interpreting the data themselves in a similar way.
We’ve been doing a similar thing when debugging API usage in the past and it seems a popular technique as I saw John Carmack using a similar simple GL wrapper in his Wolf 3d code (and he had a nice additional idea I’ll mention below).
The idea works by having a private header file in your own middleware or in your game which you include in every file you would use GL or other wrapped API from (or just include it in a global header if you wish).
Inside the header file you’ll have a structure like :-
// Wrap settings, uncomment these to enable.
// helper macros
# define DO_ERR_CHECK_GL(x) CheckGLError(x)
# define DO_ERR_CHECK_GL(x) (void)(x)
// wrapped implementations
// wrapping enabled, implement each function to be wrapped
static inline void _glDepthFunc(GLenum func)
lLogSys(“GLES”, “glDepthFunc(func=%s)\n”, GLenumToString( func ));
// call the actual function
// Use preprocessor to force any references to the unwrapped function to cause a compiler error (idea from Wolf3D code, nice way to catch!)
#define glDepthFunc ERROR_USE_WRAPPED_VERSION_OF_glDepthFunc
// wrapping disabled, use pre-processor to point to actual functions
#define _glDepthFunc glDepthFunc
This implementation is done for every function that you wish to wrap in the particular API.
In the above example we can perform whatever logging we want about each function call, using helper functions we can translate things like enums to human readable strings for output to HTML or other log file. Texture data can be intercepted in glTexImage2D calls and then stored out for future reference in the log (by associating it with the correct texture ref), likewise for shader programs.
Also above we perform a call to an error checking function if the relevant #define setting is set at the top of the file, this CheckGLError function takes the string name of the wrapped function it Is called from, performs a glGetError, checks its validity and then logs / spawns a debugger depending on other current settings.
The possibilities though aren’t limited to that..
There is the ability to add redundant call checking by storing the previous value that was passed to a particular function, in the above case we can track the current internal GL setting for glDepthFunc (this works on the assumption that no other piece of code could somehow set this and break our redundancy checks – in our case we know that all our code is wrapped). Some functions are harder to track redundant state sets for but if you focus on making the code re-useable you’ll find several functions in APIs have similar usage / internal state patterns .
Another feature I added to our internal wrappers was a GL call limit, at the top of each wrapper function a call (again to a function hidden by a #define MACRO meaning it could be easily disabled and compiled out of the code). The function that the macro called would return a bool of whether to continue execution of that wrapped function. This allowed me to have a ‘stop GL functions after xxx have been called’ function, I was able to trace using a simple interface in-game where certain rendering issues where introduced and also look at draw order very easily.
Related to that is overriding of certain states, because you can stop any state being set you can force things like texture sets to not go through (or to keep the texture set to a 1×1 dummy texture to test texture bandwidth impact on your framerate) or perhaps to override the colour of each batch passed to the renderer (I’m ignoring shaders in that example but again you could override a shader if you wished).
Implementing this system wasn’t something that took very long though and it can be very useful depending on what stage of development you’re at. It’s important to spend time working out how to minimise the effort needed for each wrapper function, I think the redundancy checking could be wrapped up nicely through some well thought out template usage and the whole thing could be done via more pre-processor macros to minimise typing errors (and to improve readability).
Hopefully this idea will come in use for your API debugging / logging / optimisation work!
Things we’ve been enjoying this week
- In-game Minecraft CPU completed and save game made available, this is just crazy to look at
- A great post with links to all you need to know on the Kinect ‘hacking’ people have been doing. I got the code compiling on Win32 when it was first being ported to libusb-win32 but haven’t had chance to do much with it since (other than hook it up to our engine). Looking forward to seeing interesting stuff from this, markerless motion capture already starting to emerge.
- A great stop-motion video, the zombie is cute
- A good look at various interesting sites / projects on the net, this is a bit of an older link (in internet terms) but had some cool stuff I’d not seen before.