2013-05-16 13 views
7

Sto sperimentando con OpenGL su Android e non ho alcuna esperienza precedente con la programmazione 3D. Quindi ovviamente ho fatto parecchi errori nel mio programma.C'è un modo migliore per eseguire il debug di OpenGL piuttosto che chiamare glGetError dopo ogni comando?

Quando ho riscontrato un problema e ho rilevato che glGetError ha generato un codice di errore, ho appena aggiunto le chiamate a glGetError dopo ogni chiamata a un comando OpenGL nel mio codice di disegno. Mentre questo ha funzionato e ho trovato i miei errori in questo modo, il mio codice di disegno è ora due volte più grande e più difficile da leggere a mio parere.

C'è un modo per sbarazzarsi di tutte queste chiamate esplicite a glGetError e chiamarlo automaticamente? Preferibilmente, la mia app dovrebbe semplicemente interrompere con un errore che indica quale comando è responsabile se si verifica un errore OpenGL.

risposta

1

Desktop OpenGL 4.3+ ha estese funzionalità di debug e callback (anche se non ho alcuna esperienza con quelle). Ma in ES non c'è davvero niente di meglio. Purtroppo la soluzione migliore è ancora non scrivere alcun glGetError s (o forse solo in alcuni punti importanti selezionati, come la fine di ogni fotogramma o qualcosa del genere) e solo introdurli in massa quando qualcosa "" non funziona ".

Diverso da quello che si potrebbe anche fare qualche involucro come

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

(supponendo che C++ 11, ma altre lingue dovrebbe avere strutture simili)

Ma penso che tali soluzioni non possono ancora essere fatte alla perfezione equivalente a n. glGetError s per quanto riguarda la leggibilità e la concisione.

2

Su OpenGL ES non si può fare molto meglio, se si mira a OpenGL ES 2.0 si dovrebbero anche usare alcuni strumenti del fornitore (a seconda del proprio dispositivo di riferimento/target) per aiutarvi nello sviluppo dello shader e nell'ottimizzazione delle prestazioni.

È necessario chiamare glError in un ciclo, ad esempio in Java:

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

Ma lasciando codice di produzione con quelli di controllo è una cattiva idea, glError è lento. La migliore opzione è quella di incapsulare all'interno di una classe di registrazione che disabilita glError a meno che non sia stato trovato un errore nel frame precedente.

10

A partire dalla versione 4.2 Android offre un'opzione chiamata "Abilita tracce OpenGL" nelle opzioni di sviluppo del telefono. Se si imposta questa opzione "Call stack glGetError" si otterrà un output come

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 

nel registro. In questo caso, stavo passando un enum/int errato a glEnableClientState() per attivare l'errore. Si noti che l'errore verrà "consumato" abilitando questa opzione e ulteriori controlli glGetError() non lo segnaleranno più. Invece, è ora possibile risparmiare tempo inserendo le chiamate glGetError() nel proprio codice e solo grep l'output del registro per "glGetError:".

+1

JIC, "Abilita tracce OpenGL" è entrato in 4.2 ... :) –

+0

Grazie, ho modificato il mio post di conseguenza. – sschuberth

+0

Non riesco a trovare "Abilita tracce OpenGL" su Oreo. Qualche idea sul perché sono andati? – Pavel

1

C'è un approccio migliore a questo chiamato AOP (Aspect Oriented Programming). Ho avuto qualche esperienza in C# in passato (circa 7 anni fa) con SpringFramework e PostSharp. Questo approccio usa ampiamente tecniche di iniezione di codice.

Così, quando ho affrontato questo problema (tracciando gli errori GL) è apparso come un classico problema per AOP.Dal momento che l'introduzione di codice introduce alcune penalità prestazionali, presumo che questa modifica (abilitazione della registrazione GL) sia temporanea e la terrò in una patch git per i casi in cui voglio usarla.

1) In primo luogo, modificare le script di build Gradle: In script di build di livello superiore aggiungere questo:

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

In script a livello di app aggiungere questo:

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

Ciò consentirà aspectj plugin in gradle. Questo progetto (ospitato qui: https://github.com/uPhyca/gradle-android-aspectj-plugin) sembra ora deprecato, ma funziona. Puoi guardare ad una versione più recente qui: https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx. Per le mie esigenze (tessitura di base del codice java) la vecchia versione funzionava bene. Ricostruisci per trovare se hai qualche problema.

2) Aggiungere la nostra annotazione, che useremo in seguito per contrassegnare i metodi vogliamo che il nostro aspetto da applicare:

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) Aggiungi il nostro aspetto:

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 metodi o costruttori che eseguire codice GL con @GlTrace annotazione:

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

Ora, dopo tutto questo fatto, basta eseguire nuovamente il progetto in AndroidStudio. Si avrà il seguente 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 

Nel mio progetto ho solo due metodi con GL chiama: Metodo di pareggio e il costruttore della classe di GlEngine.

Dopo aver giocato un po 'con questo e ottenere quei fastidiosi messaggi di "errore" non si può fare alcuni miglioramenti: errori 0) stampare solo 1) monitorare tutti i metodi che partita gl * (tutti i metodi OpenGL).

Divertiti!

Problemi correlati