I'm experimenting with some OpenGL on Android, and I don't have any previous experience with 3D programming. So obviously I made quite a few mistakes in my program.
When I encountered a problem and found that glGetError
produced an error code, I just added calls to glGetError
after each call to an OpenGL command in my drawing code. While this worked and I found my errors this way, my drawing code is now twice as big and harder to read in my opinion.
Is there a way to get rid of all these explicit calls to glGetError
and just call it automatically? Preferably, my app should just abort with an error indicating which command is responsible if an OpenGL error occurs.
Debug Output is an OpenGL feature that makes debugging and optimizing OpenGL applications easier. Briefly, this feature provides a method for the driver to provide textual message information back to the application.
The errors are stored using the data type GLenum . GL_NO_ERROR always has the value 0 and you need to call glGetError() multiple times (one for each error that has occurred) until it returns GL_NO_ERROR . A general rule of thumb is to check for error at least once in a rendering cycle.
To use the glDebugMessageCallback, you need to create a OpenGL context in debug mode. In FreeGlut you create a debug context by passing the GLUT_DEBUG enum to the glutInitContextFlags function. Usually you only want a debug context in a debug build.
As of version 4.2 Android offers an option called "Enable OpenGL traces" in the phone's developer options. If you set this to "Call stack on glGetError" you'll get an output like
07-15 15:44:43.045: D/libEGL(14251): [glEnableClientState] 0x500
07-15 15:44:43.068: D/CallStack(14251): glGetError:glEnableClientState#00 pc 00019388 /system/lib/libEGL.so
07-15 15:44:43.076: D/CallStack(14251): glGetError:glEnableClientState#01 pc 0001e290 /system/lib/libdvm.so (dvmPlatformInvoke+112)
07-15 15:44:43.076: D/CallStack(14251): glGetError:glEnableClientState#02 pc 0004d410 /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+395)
07-15 15:44:43.076: D/CallStack(14251): glGetError:glEnableClientState#03 pc 000276e4 /system/lib/libdvm.so
in the log. In this case, I was passing a wrong enum / int to glEnableClientState()
to trigger the error. Note that the error will be "consumed" by enabling this option and further glGetError()
checks will not report this anymore. Instead, you can now save the time putting glGetError()
calls in your code and just grep the log output for "glGetError:".
On OpenGL ES you cannot do much better, if you are targeting OpenGL ES 2.0 you should also be using some vendor tools (depending on your reference/target device) to aid you in shader development and performance tuning.
You must call glError in a loop, for example in java:
public void checkGLError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e("MyApp", op + ": glError " + error);
}
}
But leaving production code with those check is a bad idea, glError is slow. Best option is to encapsulate inside a logging class that disables glError unless an error was found in the previous frame.
Desktop OpenGL 4.3+ has extended debugging and callback functionalities (though I don't have any experience with those). But in ES there isn't really anything better. Sadly the best solution is still to not write any glGetError
s (or maybe only at some selected important points, like the end of each frame or something) and only introduce them en masse when something "doesn't work".
Other than that you could also make some wrapper like
template<typename F> void checked(F fn)
{
fn();
auto error = glGetError();
if(error != GL_NO_ERROR)
throw std::runtime_error("OpenGL error: "+std::to_string(error));
}
...
checked([&]() { glDrawElements(...); });
(assuming C++11, but other languages should have similar facilities)
But I think such solutions still cannot be made perfectly equivalent to no glGetError
s at all, regarding readability and conciseness.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With