Bil*_*ske 14 android opengl-es-2.0 egl
TL; DR
即使完全没有绘图,似乎也无法在Android设备上的OpenGL ES渲染线程上保持60Hz的更新速率.神秘的尖峰经常出现(在底部的代码中展示),并且我已经做出的每一项努力来弄清楚为什么或如何导致死路一条.使用自定义渲染线程在更复杂的示例中进行计时一直表明eglSwapBuffers()是罪魁祸首,经常在17ms-32ms内出现.救命?
更多细节
这尤其令人讨厌,因为我们项目的渲染要求是屏幕对齐元素,从屏幕的一侧到另一侧以固定的高速率平滑地水平滚动.换句话说,一个平台游戏.从60Hz频繁下降会导致明显的弹跳和晃动,无论是否有基于时间的移动.由于滚动速度较高,因此在30Hz下渲染不是一种选择,这是设计中不可协商的部分.
我们的项目是基于Java的,以最大限度地兼容并使用OpenGL ES 2.0.我们只深入了解NDK,用于API 7-8设备上的OpenGL ES 2.0渲染和API 7设备上的ETC1支持.在它和下面给出的测试代码中,我已经验证了除了日志打印和我无法控制的自动线程之外没有分配/ GC事件.
我在一个使用股票Android类而没有NDK的文件中重新创建了这个问题.下面的代码可以粘贴到Eclipse中创建的新Android项目中,只要您选择API级别8或更高级别,它就可以开箱即用.
该测试已在具有各种GPU和OS版本的各种设备上重现:
样本输出(从不到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
Run Code Online (Sandbox Code Playgroud)
我一直在追逐这一个并且碰到了一堵砖墙.如果无法获得修复,那么至少可以解释为什么会发生这种情况,以及在具有类似要求的项目中如何克服这一问题的建议将非常感激.
示例代码
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();
}
Run Code Online (Sandbox Code Playgroud)
您自己没有绑定帧速率。所以它是受CPU约束的。
这意味着当 CPU 无事可做时,它会运行得很快,而当 CPU 正在做其他事情时,它会运行得较慢。
您的手机上正在运行其他内容,这有时会占用游戏的 CPU 时间。垃圾收集器也会这样做,尽管不会像以前那样冻结您的游戏(因为它现在在单独的线程中运行)
您会在正常使用的任何多道程序操作系统上看到这种情况发生。这不仅仅是 Java 的错。
我的建议:如果需要恒定的比特率,请将帧速率绑定到较低的值。提示:混合使用 Thread.sleep() 和忙等待(while 循环),因为睡眠可能会超过所需的等待时间。
| 归档时间: |
|
| 查看次数: |
4525 次 |
| 最近记录: |