7

我正在 Android 上尝试一些 OpenGL,但我以前没有任何 3D 编程经验。所以很明显我在我的程序中犯了很多错误。

当我遇到问题并发现glGetError产生错误代码时,我只是glGetError在每次调用我的绘图代码中的 OpenGL 命令之后添加调用。虽然这行得通并且我以这种方式发现了我的错误,但在我看来,我的绘图代码现在是原来的两倍大并且更难阅读。

有没有办法摆脱所有这些显式调用glGetError并自动调用它?最好,如果发生 OpenGL 错误,我的应用程序应该只是中止一个错误,指示哪个命令负责。

4

5 回答 5

10

从 4.2 版开始,Android 在手机的开发者选项中提供了一个名为“启用 OpenGL 跟踪”的选项。如果将其设置为“调用 glGetError 上的堆栈”,您将获得类似的输出

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

在日志中。在这种情况下,我传递了一个错误的 enum / intglEnableClientState()来触发错误。请注意,启用此选项将“消耗”错误,进一步glGetError()检查将不再报告此问题。相反,您现在可以节省glGetError()在代码中调用的时间,只需 grep 日志输出“glGetError:”即可。

于 2013-07-15T13:52:16.213 回答
2

在 OpenGL ES 上你不能做得更好,如果你的目标是 OpenGL ES 2.0,你还应该使用一些供应商工具(取决于你的参考/目标设备)来帮助你进行着色器开发和性能调整。

必须在循环中调用 glError,例如在 java 中:

public void checkGLError(String op) {
    int error;
    while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
            Log.e("MyApp", op + ": glError " + error);
    }
}

但是留下这些检查的生产代码是个坏主意,glError 很慢。最好的选择是封装在禁用 glError 的日志记录类中,除非在前一帧中发现错误。

于 2013-05-16T19:33:12.960 回答
1

Desktop OpenGL 4.3+ 具有扩展的调试和回调功能(尽管我没有任何经验)。但是在 ES 中并没有什么比这更好的了。可悲的是,最好的解决方案仍然是不写任何s (或者可能只在某些选定的重要点,例如每一帧的结尾或其他东西),并且只在某些东西“不起作用”glGetError时才集体引入它们。

除此之外,您还可以制作一些包装器,例如

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(...); });

(假设 C++11,但其他语言应该有类似的设施)

但我认为这样的解决方案仍然不能完全等同于 no glGetError,就可读性和简洁性而言。

于 2013-05-16T18:29:23.050 回答
1

检查 glDebugMessageCallback

void GLAPIENTRY
MessageCallback( GLenum source,
                 GLenum type,
                 GLuint id,
                 GLenum severity,
                 GLsizei length,
                 const GLchar* message,
                 const void* userParam )
{
  fprintf( stderr, "GL CALLBACK: %s type = 0x%x, severity = 0x%x, message = %s\n",
           ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ),
            type, severity, message );
}


// During init, enable debug output
glEnable ( GL_DEBUG_OUTPUT );
glDebugMessageCallback( MessageCallback, 0 );
于 2018-09-18T07:30:02.060 回答
1

有一种更好的方法称为 AOP(面向方面​​编程)。过去(大约 7 年前)我在 C# 中使用 SpringFramework 和 PostSharp 有过一些经验。这种方法广泛使用代码注入技术。

所以当我遇到这个问题(跟踪 GL 错误)时,它似乎是 AOP 的一个经典问题。由于代码注入会带来一些性能损失,因此我假设此更改(启用 GL 日志记录)是暂时的,并将在我想要使用它的情况下将其保存在 git 补丁中。

1)首先,更改您的 gradle 构建脚本:在顶级构建脚本中添加:

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

在应用程序级脚本中添加:

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

这将在 gradle 中启用 aspectj 插件。这个项目(托管在这里:https ://github.com/uPhyca/gradle-android-aspectj-plugin )现在似乎已被弃用,但它正在工作。您可以在这里查看更新的版本:https ://github.com/HujiangTechnology/gradle_plugin_android_aspectjx 。对于我的需要(基本的 java 代码编织),旧版本运行良好。重建以查找是否有任何问题。

2)添加我们稍后将使用的注释来标记我们希望将方面应用于的方法:

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)添加我们的方面:

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)用@GlTrace注解标记执行GL代码的方法或构造函数:

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

现在完成所有这些之后,只需在 AndroidStudio 中重新运行该项目。您将获得以下输出:

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

在我的项目中,我只有两个带有 GL 调用的方法:draw 方法和 GlEngine 类的构造函数。

在您使用它并获得那些烦人的“无错误”消息后,您可以进行一些改进: 0)仅打印错误 1)监视与 gl* 匹配的所有方法(所有 OpenGL 方法)。

享受!

于 2016-07-18T10:52:26.780 回答