2013-05-16 6 views
7

Ich experimentiere mit etwas OpenGL auf Android, und ich habe keine vorherige Erfahrung mit 3D-Programmierung. Also habe ich offensichtlich einige Fehler in meinem Programm gemacht.Gibt es eine bessere Möglichkeit, OpenGL zu debuggen als glGetError nach jedem Befehl aufzurufen?

Wenn ich ein Problem festgestellt und festgestellt habe, dass glGetError einen Fehlercode erzeugt, fügte ich nur Aufrufe glGetError nach jedem Aufruf eines OpenGL-Befehls in meinem Zeichnungscode. Während das funktionierte und ich meine Fehler auf diese Weise fand, ist mein Zeichencode meiner Meinung nach jetzt doppelt so groß und schwerer zu lesen.

Gibt es eine Möglichkeit, all diese expliziten Aufrufe an glGetError loszuwerden und es einfach automatisch aufzurufen? Vorzugsweise sollte meine App nur mit einem Fehler abgebrochen werden, der angibt, welcher Befehl für einen OpenGL-Fehler verantwortlich ist.

Antwort

1

Desktop OpenGL 4.3+ hat erweiterte Debugging und Callback-Funktionen (obwohl ich keine Erfahrung damit habe). Aber in ES gibt es eigentlich nichts Besseres. Leider ist die beste Lösung, immer noch keine glGetError s (oder nur an einigen ausgewählten wichtigen Punkten, wie das Ende jedes Rahmens oder etwas) zu schreiben und sie nur en masse einzuführen, wenn etwas "" nicht funktioniert ".

Anders als das Sie könnten auch einige Wrapper machen wie

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

(unter der Annahme, C++ 11, aber auch andere Sprachen sollten ähnliche Einrichtungen haben)

Aber ich denke, solche Lösungen immer noch nicht perfekt gemacht werden äquivalent zu Nr. glGetError s in Bezug auf Lesbarkeit und Prägnanz.

2

Auf OpenGL ES können Sie nicht viel besser machen, wenn Sie auf OpenGL ES 2.0 abzielen, sollten Sie auch einige Hersteller-Tools verwenden (abhängig von Ihrer Referenz/Zielgerät), um Sie bei der Shader-Entwicklung und Leistungsoptimierung zu unterstützen.

Sie müssen glError in einer Schleife, zum Beispiel in Java nennen:

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

Aber verlassen Produktionscode mit diesem Scheck eine schlechte Idee ist, glError langsam ist. Die beste Option besteht darin, in eine Protokollierungsklasse zu kapseln, die glError deaktiviert, sofern im vorherigen Frame kein Fehler gefunden wurde.

10

Ab Version 4.2 bietet Android eine Option namens "Enable OpenGL traces" in den Entwickleroptionen des Telefons an. Wenn Sie dies „Anrufstapel auf glGetError“ gesetzt werden Sie eine Ausgabe wie

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 

im Protokoll erhalten. In diesem Fall habe ich eine falsche enum/int an glEnableClientState() übergeben, um den Fehler auszulösen. Beachten Sie, dass der Fehler "konsumiert" wird, indem Sie diese Option aktivieren und weitere glGetError() Schecks dies nicht mehr melden. Stattdessen können Sie jetzt die Zeit speichern, indem Sie glGetError() Aufrufe in Ihrem Code eingeben und einfach die Protokollausgabe für "glGetError:" grep.

+1

JIC, "Enable OpenGL Spuren" kam in 4.2 ... :) –

+0

Danke, ich habe meinen Beitrag entsprechend bearbeitet. – sschuberth

+0

Ich kann "OpenGL-Traces aktivieren" auf Oreo nicht finden. Irgendwelche Ideen, warum sie weg sind? – Pavel

1

Es gibt einen besseren Ansatz für diese sogenannte AOP (Aspect Oriented Programming). Ich habe in der Vergangenheit (vor etwa 7 Jahren) mit SpringFramework und PostSharp einige Erfahrungen mit C# gemacht. Dieser Ansatz verwendet weitgehend Codeinjektionstechniken.

Also als ich dieses Problem (Verfolgung von GL-Fehlern) konfrontiert wurde, erschien es als ein klassisches Problem für AOP.Da die Code-Injection einige Leistungseinbußen mit sich bringt, gehe ich davon aus, dass diese Änderung (Aktivierung der GL-Protokollierung) zeitlich ist und sie in einem git-Patch für die Fälle, in denen ich sie verwenden möchte, erhalten bleiben.

1) Zuerst wird Ihre gradle Build-Skripte ändern: In Top-Level-Build-Skript hinzufügen:

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

In app-Ebene Skript fügen Sie diese:

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

Diese aspectj ermöglichen Plugin in Großbuchstaben. Dieses Projekt (gehostet hier: https://github.com/uPhyca/gradle-android-aspectj-plugin) scheint jetzt veraltet, aber es funktioniert. Sie können hier eine neuere Version anzeigen: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx. Für meine Bedürfnisse (einfaches Java-Code-Weben) funktionierte die alte Version gut. Neu erstellen, um festzustellen, ob Probleme auftreten.

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) unsere Aspect hinzufügen:

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

2) Fügen Sie unsere Anmerkung, die wir später Methoden zur Markierung verwenden wir unser Aspekt wollen angewendet werden Methoden oder Konstrukteure, die GL-Code mit @GlTrace Anmerkung ausführen:

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

Jetzt, nach all dies nur getan, erneut ausführen das Projekt in AndroidStudio. Sie werden die folgende Ausgabe haben:

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 meinem Projekt habe ich nur zwei Methoden, mit GL ruft: Draw-Verfahren und der Konstruktor von GlEngine Klasse.

Nachdem Sie ein wenig mit diesem spielen und die lästigen „kein Fehler“ -Meldungen erhalten Sie können einige Verbesserungen tun: 0) Druckfehler nur 1) überwachen alle Methoden welche Match gl * (alle OpenGl Methoden).

Viel Spaß!

Verwandte Themen