Is there a better way to debug OpenGL than calling glGetError after each command?

I am experimenting with some OpenGL on Android and I have no previous experience with 3D programming. Therefore, obviously, I made a lot of mistakes in my program.

When I ran into a problem and found that glGetError generated the error code, I simply added glGetError calls after every call to the OpenGL command in my code. While this worked, and I found my errors this way, my drawing code is now twice as large 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 application should just abort an error indicating which command is responding if an OpenGL error occurs.

+7
source share
4 answers

Starting with version 4.2, Android offers the option "Enable OpenGL tracing" in the phone’s developer settings. If you set this to "Call the stack on glGetError", you will get a result, for example

 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 journal. In this case, I skipped the wrong enum / int to glEnableClientState() to cause an error. Note that the error will be "consumed" by enabling this option, and further glGetError() checks will no longer report this. Instead, you can save time by introducing glGetError() calls into your code and simply grep the log output for "glGetError:".

+10
source

In OpenGL ES, you cannot do much better if you are targeting OpenGL ES 2.0, you should also use some vendor tools (depending on your link / target device) to help you develop shaders and tune performance.

You should 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 these checks is a bad idea, glError is slow. The best option is encapsulation inside the log class, which disables glError if no error is detected in the previous frame.

+2
source

Desktop OpenGL 4.3+ has advanced debugging and callback functions (although I have no experience with them). But in ES there is nothing better. Unfortunately, the best solution is still not to write any glGetError (or maybe only at some selected important points, such as the end of each frame or something else), and only enter them in bulk when something " does not work".

Alternatively, you could also create some kind of shell, for example

 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 capabilities)

But I think that such solutions still cannot be absolutely equivalent to no glGetError in general, regarding readability and brevity.

+1
source

There is a better approach to this called AOP (Aspect Oriented Programming). I had some experience working with it in C # in the past (about 7 years ago) with SpringFramework and PostSharp. This approach makes extensive use of code injection techniques.

So, when I ran into this problem (tracking GL errors), it arose as a classic problem for AOP. Since injecting the code introduces some performance penalties, I assume that this change (enabling GL logging) is temporary and will contain it in the git patch for cases where I want to use it.

1) First change the gradle build scripts: In the top-level build script add this:

 buildscript { dependencies { classpath 'com.uphyca.gradle:gradle-android-aspectj-plugin:0.9.14' 

In the app-level script add this:

 apply plugin: 'com.uphyca.android-aspectj' 

This will enable the aspectj plugin in gradle. This project (located here: https://github.com/uPhyca/gradle-android-aspectj-plugin ) seems deprecated, but it works. You can see the newer version here: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx . For my needs (basic coding of java code) the old version worked well. Rebuild to find out if you have any problems.

2) Add our annotation, which we will use later, to mark the methods to which we want to apply our aspect:

 package com.example.neutrino.maze; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Created by Greg Stein on 7/18/2016. */ @Retention(RetentionPolicy.CLASS) @Target({ ElementType.CONSTRUCTOR, ElementType.METHOD }) public @interface GlTrace { } 

3) Add our aspect:

 package com.example.neutrino.maze; import android.opengl.GLES20; import android.opengl.GLU; import android.util.Log; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; /** * Created by Greg Stein on 7/18/2016. */ @Aspect public class GlTraceAspect { private static final String POINTCUT_METHOD = "execution(@com.example.neutrino.maze.GlTrace * *(..))"; private static final String POINTCUT_CONSTRUCTOR = "execution(@com.example.neutrino.maze.GlTrace *.new(..))"; @Pointcut(POINTCUT_METHOD) public void methodAnnotatedWithGlTrace() {} @Pointcut(POINTCUT_CONSTRUCTOR) public void constructorAnnotatedWithGlTrace() {} @Around("methodAnnotatedWithGlTrace() || constructorAnnotatedWithGlTrace()") public Object weaveJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable { Signature signature = joinPoint.getSignature(); String className = signature.getDeclaringType().getSimpleName(); String methodName = signature.getName(); // Before method execution // -- nothing -- Object result = joinPoint.proceed(); // After method execution Log.d(className, buildLogMessage(methodName)); return result; } /** * Create a log message. * * @param methodName A string with the method name. * @return A string representing message. */ private static String buildLogMessage(String methodName) { StringBuilder message = new StringBuilder(); int errorCode = GLES20.glGetError(); message.append("GlState["); message.append(methodName); message.append("]: "); if (GLES20.GL_NO_ERROR != errorCode) { message.append("ERROR:"); } message.append(GLU.gluErrorString(errorCode)); return message.toString(); } } 

4) Note the methods or constructors that execute GL code with the @GlTrace annotation:

 ... @GlTrace public GlEngine(int quadsNum) { ... @GlTrace public void draw(float[] mvpMatrix) { ... 

Now, after all this, just run the project in AndroidStudio. You will have the following output:

 07-18 12:34:37.715 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[<init>]: no error 07-18 12:34:37.715 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[draw]: no error 07-18 12:34:37.733 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[<init>]: no error 07-18 12:34:37.735 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[draw]: no error 07-18 12:34:37.751 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[<init>]: no error 07-18 12:34:37.751 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[draw]: no error 07-18 12:34:37.771 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[<init>]: no error 07-18 12:34:37.771 19167-19187/com.example.neutrino.maze D/GlEngine: GlState[draw]: no error 

In my project, I have only two methods with GL calls: the draw method and the constructor of the GlEngine class.

After you play around a bit with this and get these annoying error-free messages, you can make some improvements: 0) Only print errors 1) control all methods that match gl * (all OpenGl methods).

Enjoy it!

+1
source

All Articles