TL; DR的Android EGL/OpenGL ES的幀速率口吃
即使沒有做圖時可言,這似乎是不可能維持在Android設備上上的OpenGL ES渲染線程60Hz的刷新率。經常出現神祕的尖峯(在底部的代碼中顯示),並且我爲了弄清楚爲什麼或如何導致死路一條而做出的每一個努力。使用自定義渲染線程定時更復雜的示例一直表明eglSwapBuffers()是罪魁禍首,經常在17ms-32ms以上。幫幫我?
更多詳細信息
這是特別確鑿因爲我們的項目的呈現要求是屏幕對齊元件順暢地在固定的,高速的其他速率從屏幕的一側水平滾動。換句話說,一個平臺遊戲。從60Hz頻繁下降導致明顯的爆裂和l,,無論是否有基於時間的移動。由於滾動速度很高,因此30Hz下的渲染不是一種選擇,這是設計中不可談判的部分。
我們的項目是基於Java的,以最大限度地提高兼容性並使用OpenGL ES 2.0。我們只能深入到API 7-8設備上的OpenGL ES 2.0渲染和API 7設備上的ETC1支持。在它和下面給出的測試代碼中,除了我的控制之外的日誌打印和自動線程,我沒有驗證分配/ GC事件。
我已經在使用庫存Android類和NDK的單個文件中重新創建了該問題。下面的代碼可以粘貼到在Eclipse中創建的新Android項目中,只要您選擇API級別8或更高版本,即可使用即開即用的代碼。
測試已被再現的各種設備與一系列GPU和OS版本:
- 銀河標籤10.1(3.1的Android)
- 的Nexus S(的Android 2.3.4)
- 星系S II(的Android 2.3.3)
- XPERIA播放(2.3.2的Android)
- Droid難以置信(的Android 2.2)
- 銀河S(的Android 2.1-UPDATE1)(當博士opping API的需求下降到7級)
樣本輸出(從下的運行時間1秒)收集:
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
我一直在追逐這一個了一段時間,有關於打磚壁。如果修復程序不可用,那麼至少應該解釋爲什麼會發生這種情況,以及如何在具有類似要求的項目中克服這些問題的建議將不勝感激。
示例代碼
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();
}
手錶logcat的輸出GC的消息,這是一個事實的生活,如果你在Java中構建它。較新版本的Android具有併發GC,但很難避免偶爾發生的全面GC和伴隨的暫停。你應該對40 fps感到滿意,並爭取併發GC。 –