TL; DRAndroid EGL/OpenGL ES Frame Rate balbuzie
Anche quando si fa alcun disegno in tutto, sembra impossibile mantenere una velocità di aggiornamento 60Hz su un thread di rendering OpenGL ES su un dispositivo Android. Frequentemente spuntano punte misteriose (dimostrate nel codice in fondo) e ogni sforzo che ho fatto per capire perché o come ha portato a un vicolo cieco. Il tempismo in esempi più complicati con un thread di rendering personalizzato ha mostrato in modo coerente che eglSwapBuffers() è il colpevole, che frequenta spesso oltre i 17ms-32ms. Aiuto?
Dettagli
Ciò è particolarmente schiacciante perché i requisiti di rendering per il nostro progetto è elementi di schermo allineati uniformemente scorrimento orizzontalmente alla, alto tasso fisso di velocità da un lato dello schermo all'altro. In altre parole, un gioco platform. Le frequenti cadute da 60 Hz provocano notevoli scoppi e sobbalzi, sia con che senza movimento basato sul tempo. Il rendering a 30 Hz non è un'opzione a causa dell'alta velocità di scorrimento, che è una parte non negoziabile del progetto.
Il nostro progetto è basato su Java per massimizzare la compatibilità e utilizza OpenGL ES 2.0. Scendiamo solo nel NDK per il rendering OpenGL ES 2.0 su dispositivi API 7-8 e supporto ETC1 su dispositivi API 7. In entrambi e il codice di test indicato di seguito, non ho verificato nessun allocazione/eventi GC ad eccezione della stampa dei log e dei thread automatici al di fuori del mio controllo.
Ho ricreato il problema in un singolo file che utilizza le classi Android standard e nessun NDK. Il codice sottostante può essere incollato in un nuovo progetto Android creato in Eclipse e dovrebbe funzionare praticamente subito dopo aver scelto l'API di livello 8 o superiore.
Il test è stato riprodotto su una varietà di dispositivi con una gamma di versioni GPU e OS:
- Galaxy Tab 10.1 (Android 3.1)
- Nexus S (Android 2.3.4)
- Galaxy S II (Android 2.3.3)
- XPERIA Play (Android 2.3.2)
- Droid Incredible (Android 2.2)
- Galaxy S (Android 2.1-update1) (quando dr opping requisiti API fino al livello 7)
Esempio di output (raccolte da meno di 1 secondo di tempo di esecuzione):
Spike: 0.017554
Spike: 0.017767
Spike: 0.018017
Spike: 0.016855
Spike: 0.016759
Spike: 0.016669
Spike: 0.024925
Spike: 0.017083999
Spike: 0.032984
Spike: 0.026052998
Spike: 0.017372
Sto inseguendo questo per un po 'e ho circa colpire un mattone parete. Se una correzione non è disponibile, almeno una spiegazione sul perché questo accada e consigli su come questo è stato superato in progetti con requisiti simili sarebbe molto apprezzato.
Esempio Codice
package com.test.spikeglsurfview;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
/**
* A simple Activity that demonstrates frequent frame rate dips from 60Hz,
* even when doing no rendering at all.
*
* This class targets API level 8 and is meant to be drop-in compatible with a
* fresh auto-generated Android project in Eclipse.
*
* This example uses stock Android classes whenever possible.
*
* @author Bill Roeske
*/
public class SpikeActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Make the activity fill the screen.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Get a reference to the default layout.
final LayoutInflater factory = getLayoutInflater();
final LinearLayout layout = (LinearLayout)factory.inflate(R.layout.main, null);
// Clear the layout to remove the default "Hello World" TextView.
layout.removeAllViews();
// Create a GLSurfaceView and add it to the layout.
GLSurfaceView glView = new GLSurfaceView(getApplicationContext());
layout.addView(glView);
// Configure the GLSurfaceView for OpenGL ES 2.0 rendering with the test renderer.
glView.setEGLContextClientVersion(2);
glView.setRenderer(new SpikeRenderer());
// Apply the modified layout to this activity's UI.
setContentView(layout);
}
}
class SpikeRenderer implements GLSurfaceView.Renderer
{
@Override
public void onDrawFrame(GL10 gl)
{
// Update base time values.
final long timeCurrentNS = System.nanoTime();
final long timeDeltaNS = timeCurrentNS - timePreviousNS;
timePreviousNS = timeCurrentNS;
// Determine time since last frame in seconds.
final float timeDeltaS = timeDeltaNS * 1.0e-9f;
// Print a notice if rendering falls behind 60Hz.
if(timeDeltaS > (1.0f/60.0f))
{
Log.d("SpikeTest", "Spike: " + timeDeltaS);
}
/*// Clear the screen.
gl.glClear(GLES20.GL_COLOR_BUFFER_BIT);*/
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height)
{
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// Set clear color to purple.
gl.glClearColor(0.5f, 0.0f, 0.5f, 1.0f);
}
private long timePreviousNS = System.nanoTime();
}
orologio uscita logcat per i messaggi GC, che è una vita fatto-of-se si sta costruendo in Java. le versioni più recenti di Android hanno un GC concorrente, ma è difficile evitare un GC completo occasionale e le pause di accompagnamento. dovresti essere soddisfatto di 40 fps e cercare il GC simultaneo. –