在开发过程中,我注意到一个奇怪的“bug”,它只能在Android模拟器上重现。我正在使用带有GPU加速的x86版本。请查看以下代码:
public class TestRenderer implements GLSurfaceView.Renderer {
private static final int COUNT = 1000;
private static final int BYTES_PER_FLOAT = 4;
private static final float[] QUAD_VERTICES = new float[] { -0.5f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f };
private static final int COORDS_PER_QUAD = QUAD_VERTICES.length;
private int fps;
private long startTime = SystemClock.uptimeMillis();
private float[] vertices;
private FloatBuffer vertexBuffer;
private int vertexBufferId;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
vertices = new float[COUNT * COORDS_PER_QUAD];
vertexBuffer = FloatBuffer.wrap(vertices);
int[] bufferId = new int[1];
GLES20.glGenBuffers(1, bufferId, 0);
vertexBufferId = bufferId[0];
}
@Override
public void onSurfaceChanged(GL10 gl, int w, int h) {
gl.glViewport(0, 0, w, h);
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, vertices.length * BYTES_PER_FLOAT, vertexBuffer, GLES20.GL_STATIC_DRAW);
/** FPS **/
fps++;
long afterTime = SystemClock.uptimeMillis();
if (afterTime - startTime >= 10000) {
Log.d("FPS", "Renderer FPS: " + fps / 10);
startTime = afterTime;
fps = 0;
}
}
}
这仅用于说明目的。当然不会显示任何内容。
此渲染器由Activity中的GLSurfaceView使用。每个帧渲染器都会创建一个包含1000个四边形顶点数据的新数据存储。如果模拟器保持不动约一分钟,帧速率开始从60 FPS降至1 FPS,最终活动挂起。
问题:是我的错还是模拟器出了问题?实际设备上不会出现问题。
答案 0 :(得分:1)
首先,glBufferData (...)
每次调用时都会在GPU上完全重新分配存储空间。如果要在每个帧上向VBO提供新数据,则应考虑使用使用标记GL_DYNAMIC_DRAW
分配缓冲区一次,然后每帧调用glBufferSubData (...)
。这将限制每帧复制新数据而不是删除旧VBO内存,分配新内存然后复制数据所发生的事情数量。
在某些实现中,删除和重新创建缓冲区对象可能非常昂贵,并且VBO的已分配内存实际上将保留在原位,直到需要它的所有挂起的OpenGL操作都从管道中刷新。您可能因为有多个帧缓冲命令而耗尽内存。
glBufferSubData (...)
将强制进行隐式同步,也就是说,它无法覆盖尚未完成的任何命令的数据。它将等待前一帧完成或分配临时缓冲区,但在任何一种情况下,驱动程序将根据可用内存量知道哪个操作是合适的。这种行为可能是可取的,即使它可能会有一些轻微的性能影响。