2013-03-22 15 views
11

la versione Android 2.2.1 è il dispositivo è un Samsung Galaxy II il log completo crollo è:createWindowSurface non riuscito: EGL_BAD_MATCH?

java.lang.RuntimeException: createWindowSurface failed: EGL_BAD_MATCH 
at android.opengl.GLSurfaceView$EglHelper.throwEglException(GLSurfaceView.java:1077) 
at android.opengl.GLSurfaceView$EglHelper.createSurface(GLSurfaceView.java:981) 
at android.opengl.GLSurfaceView$GLThread.guardedRun(GLSurfaceView.java:1304) 
at android.opengl.GLSurfaceView$GLThread.run(GLSurfaceView.java:1116) 

questo è il codice relativo al crollo:

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    requestWindowFeature(Window.FEATURE_NO_TITLE); 
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 
         WindowManager.LayoutParams.FLAG_FULLSCREEN); 
    glView = new GLSurfaceView(this); 
    glView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0); 
    glView.setRenderer(this); 
    setContentView(glView); 
    \\etc..............} 

ho usato setEGLConfigChooser() perché l'app si arresterebbe in modo anomalo su API-17 se non fosse presente, quindi per questo specifico dispositivo che si blocca mi sono guardato intorno e ha qualcosa a che fare con PixelFormat per il dispositivo.

Quello che mi chiedo è come posso usare un po 'di codice in modo che questo non si arresti in modo anomalo sulla versione 2.2.1 di android galaxy II, non posso testarlo in un emulatore e non ho il dispositivo per testarlo, ho solo bisogno di un codice sicuro e non sono sicuro di come cambiarlo?

risposta

9

Aggiornamento: Ho trovato un modo per ovviare a questo problema e in realtà è abbastanza semplice.

Prima di tutto: l'implementazione predefinita di Android EGLConfigChooser prende decisioni sbagliate su alcuni dispositivi . Soprattutto i vecchi dispositivi Android sembrano soffrire questo problema EGL_BAD_MATCH. Durante le mie sessioni di debug ho anche scoperto che quei vecchi dispositivi troublemaker disponevano di un insieme abbastanza limitato di configurazioni OpenGL ES disponibili.

La causa di questo problema di "corrispondenza errata" è molto più di una mancata corrispondenza tra il formato pixel di GLSurfaceView e le impostazioni di profondità bit di colore di OpenGL ES. Nel complesso abbiamo a che fare con i seguenti problemi:

  • Una mancata corrispondenza della versione API OpenGL ES
  • Una mancata corrispondenza del target richiesto tipo di superficie
  • La profondità di bit colore richiesto non può essere reso sulla superficie vista

La documentazione per gli sviluppatori Android è gravemente carente quando si tratta di spiegare l'API OpenGL ES. È quindi importante leggere la documentazione originale su Khronos.org. Soprattutto la pagina di documentazione su eglChooseConfig è utile qui.

Al fine di porre rimedio ai problemi sopra elencati si deve fare in modo di specificare la seguente configurazione minima:

  • EGL_RENDERABLE_TYPE deve corrispondere alla versione API OpenGL ES che si sta utilizzando. Nel caso probabile di OpenGL ES 2.x è necessario impostare l'attributo per 4 (vedi egl.h)
  • EGL_SURFACE_TYPE dovrebbe avere la EGL_WINDOW_BIT set

E naturalmente anche voi volete impostare un contesto OpenGL ES che fornisce le impostazioni corrette per il colore, la profondità e il buffer dello stampino.

Sfortunatamente non è possibile selezionare queste opzioni di configurazione in modo semplice. Dobbiamo scegliere tra tutto ciò che è disponibile su un determinato dispositivo. Ecco perché è necessario implementare una custom EGLConfigChooser, che passa attraverso l'elenco dei set di configurazione disponibili e sceglie quello più adatto che corrisponde al meglio ai criteri specificati.

Comunque, ho montata su un esempio di implementazione di un tale selettore di configurazione:

public class MyConfigChooser implements EGLConfigChooser { 
    final private static String TAG = "MyConfigChooser"; 

    // This constant is not defined in the Android API, so we need to do that here: 
    final private static int EGL_OPENGL_ES2_BIT = 4; 

    // Our minimum requirements for the graphics context 
    private static int[] mMinimumSpec = { 
      // We want OpenGL ES 2 (or set it to any other version you wish) 
      EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, 

      // We want to render to a window 
      EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT, 

      // We do not want a translucent window, otherwise the 
      // home screen or activity in the background may shine through 
      EGL10.EGL_TRANSPARENT_TYPE, EGL10.EGL_NONE, 

      // indicate that this list ends: 
      EGL10.EGL_NONE 
    }; 

    private int[] mValue = new int[1]; 
    protected int mAlphaSize; 
    protected int mBlueSize; 
    protected int mDepthSize; 
    protected int mGreenSize; 
    protected int mRedSize; 
    protected int mStencilSize; 

    /** 
    * The constructor lets you specify your minimum pixel format, 
    * depth and stencil buffer requirements. 
    */ 
    public MyConfigChooser(int r, int g, int b, int a, int depth, int 
         stencil) { 
     mRedSize = r; 
     mGreenSize = g; 
     mBlueSize = b; 
     mAlphaSize = a; 
     mDepthSize = depth; 
     mStencilSize = stencil; 
    } 

    @Override 
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) { 
     int[] arg = new int[1]; 
     egl.eglChooseConfig(display, mMinimumSpec, null, 0, arg); 
     int numConfigs = arg[0]; 
     Log.i(TAG, "%d configurations available", numConfigs); 

     if(numConfigs <= 0) { 
      // Ooops... even the minimum spec is not available here 
      return null; 
     } 

     EGLConfig[] configs = new EGLConfig[numConfigs]; 
     egl.eglChooseConfig(display, mMinimumSpec, configs,  
      numConfigs, arg); 

     // Let's do the hard work now (see next method below) 
     EGLConfig chosen = chooseConfig(egl, display, configs); 

     if(chosen == null) { 
      throw new RuntimeException(
        "Could not find a matching configuration out of " 
          + configs.length + " available.", 
       configs); 
     } 

     // Success 
     return chosen; 
    } 

    /** 
    * This method iterates through the list of configurations that 
    * fulfill our minimum requirements and tries to pick one that matches best 
    * our requested color, depth and stencil buffer requirements that were set using 
    * the constructor of this class. 
    */ 
    public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, 
      EGLConfig[] configs) { 
     EGLConfig bestMatch = null; 
     int bestR = Integer.MAX_VALUE, bestG = Integer.MAX_VALUE, 
      bestB = Integer.MAX_VALUE, bestA = Integer.MAX_VALUE, 
      bestD = Integer.MAX_VALUE, bestS = Integer.MAX_VALUE; 

     for(EGLConfig config : configs) { 
      int r = findConfigAttrib(egl, display, config, 
         EGL10.EGL_RED_SIZE, 0); 
      int g = findConfigAttrib(egl, display, config, 
         EGL10.EGL_GREEN_SIZE, 0); 
      int b = findConfigAttrib(egl, display, config,   
         EGL10.EGL_BLUE_SIZE, 0); 
      int a = findConfigAttrib(egl, display, config, 
        EGL10.EGL_ALPHA_SIZE, 0); 
      int d = findConfigAttrib(egl, display, config, 
        EGL10.EGL_DEPTH_SIZE, 0); 
      int s = findConfigAttrib(egl, display, config, 
        EGL10.EGL_STENCIL_SIZE, 0); 

      if(r <= bestR && g <= bestG && b <= bestB && a <= bestA 
        && d <= bestD && s <= bestS && r >= mRedSize 
        && g >= mGreenSize && b >= mBlueSize 
        && a >= mAlphaSize && d >= mDepthSize 
        && s >= mStencilSize) { 
       bestR = r; 
       bestG = g; 
       bestB = b; 
       bestA = a; 
       bestD = d; 
       bestS = s; 
       bestMatch = config; 
      } 
     } 

     return bestMatch; 
    } 

    private int findConfigAttrib(EGL10 egl, EGLDisplay display, 
      EGLConfig config, int attribute, int defaultValue) { 

     if(egl.eglGetConfigAttrib(display, config, attribute, 
      mValue)) { 
      return mValue[0]; 
     } 

     return defaultValue; 
    } 
} 
+0

Supponiamo che io desideri RGB_565. Sceglierà RGB_888 su RGB_965 (se esiste) Supponendo che RGB_888 arrivi prima nella lista 'configs'? – Kiran

+0

@Kiran Questa è una buona domanda. Temo che ciò dipenda dall'ordine della lista dei set di configurazione di OpenGL ES (ovvero l'array 'EGLConfig [] config'), e credo che possa essere completamente specifico per il dispositivo. Se si vuole essere sicuri di poter modificare il selettore di configurazione per prendere in considerazione i limiti superiori per la configurazione del colore. Il mio chooser di esempio non è così restrittivo, considera solo le profondità di bit fornite dall'utente come requisiti minimi e sceglie felicemente tutto ciò che va oltre. – tiguchi

+0

@Kiran BTW, credo che Android sia in grado di convertire automaticamente un contesto EGL 'RGB_888' in' RGB_565' della vista superficie di destinazione del rendering. Nelle mie sessioni di debug ho capito che il flag 'EGL_TRANSPARENT_TYPE' era in effetti il ​​principale creatore di problemi (almeno per il mio caso). Se un selettore non limita il valore a 'EGL_NONE', è possibile che venga selezionata accidentalmente una configurazione che richiede che la vista di superficie stessa supporti i pixel traslucidi per consentire alle viste al di sotto di" risplendere ". Di solito non è l'impostazione predefinita e su alcuni dispositivi meno recenti non è supportata. – tiguchi

3

Non ho il punteggio di reputazione per aggiungere un commento ancora, altrimenti avrei messo un breve commento su Nobu Risposta dei giochi. Ho riscontrato questo stesso errore EGL_BAD_MATCH e la loro risposta mi ha aiutato a mettermi sulla giusta strada. Invece, devo creare una risposta separata.

Come menzionato da Nobu Games, sembra esserci una discrepanza tra PixelFormat di GLSurfaceView ei parametri di formato pixel passati a setEGLConfigChooser(). Nel mio caso, stavo chiedendo RGBA8888 ma il mio GLSurfaceView era RGB565. Ciò ha causato l'errore EGL_BAD_MATCH più tardi durante l'inizializzazione.

Il miglioramento della risposta è che è possibile ottenere il PixelFormat desiderato per la finestra e utilizzarlo per scegliere dinamicamente un contesto EGL.

Per rendere il mio codice il più generico possibile, ho modificato GLSurfaceView in modo da includere un parametro aggiuntivo: il formato pixel del display. Ottengo questo dalla mia attività chiamando:

getWindowManager().getDefaultDisplay().getPixelFormat(); 

passo questo valore fino al GLSurfaceView e quindi estrarre i profondità di bit ottimali per ciascuna delle RGBA come questo:

if (pixelFormatVal > 0) { 

    PixelFormat info = new PixelFormat(); 
    PixelFormat.getPixelFormatInfo(pixelFormatVal, info); 

    if (PixelFormat.formatHasAlpha(pixelFormatVal)) { 

     if (info.bitsPerPixel >= 24) { 
      m_desiredABits = 8; 
     } else { 
      m_desiredABits = 6; // total guess 
     } 

    } else { 
     m_desiredABits = 0; 
    } 

    if (info.bitsPerPixel >= 24) { 
     m_desiredRBits = 8; 
     m_desiredGBits = 8; 
     m_desiredBBits = 8; 
    } else if (info.bitsPerPixel >= 16) { 
     m_desiredRBits = 5; 
     m_desiredGBits = 6; 
     m_desiredRBits = 5; 
    } else { 
     m_desiredRBits = 4; 
     m_desiredGBits = 4; 
     m_desiredBBits = 4; 
    } 

} else { 
    m_desiredRBits = 8; 
    m_desiredGBits = 8; 
    m_desiredBBits = 8; 
} 

Ho poi passare questi valori giù al mio selettore di configurazione. Questo codice funziona per me su un dispositivo RGB565 e su un dispositivo RGBA8888.

La mia ipotesi è che il fornitore abbia scelto il valore predefinito per un motivo e che fornirà i risultati più performanti. Ovviamente non ho nulla a sostegno di questa affermazione, ma è la strategia con cui sto andando.

+0

Grazie per la risposta dettagliata! Sembrava promettente, ma sfortunatamente sembra che getPixelFormat() sia ora deprecato e restituirà RGBA_8888 ogni volta, rendendolo molto meno utile. (Vedi: http://developer.android.com/reference/android/view/Display.html#getPixelFormat()). Sto ancora cercando una buona soluzione per questo ... – gkanwar