为什么反复调用glBufferData会使模拟器崩溃?

时间:2013-12-01 22:39:01

标签: android android-emulator opengl-es-2.0

在开发过程中,我注意到一个奇怪的“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,最终活动挂起。

问题:是我的错还是模拟器出了问题?实际设备上不会出现问题。

1 个答案:

答案 0 :(得分:1)

首先,glBufferData (...)每次调用时都会在GPU上完全重新分配存储空间。如果要在每个帧上向VBO提供新数据,则应考虑使用使用标记GL_DYNAMIC_DRAW分配缓冲区一次,然后每帧调用glBufferSubData (...)。这将限制每帧复制新数据而不是删除旧VBO内存,分配新内存然后复制数据所发生的事情数量。

在某些实现中,删除和重新创建缓冲区对象可能非常昂贵,并且VBO的已分配内存实际上将保留在原位,直到需要它的所有挂起的OpenGL操作都从管道中刷新。您可能因为有多个帧缓冲命令而耗尽内存。

glBufferSubData (...)将强制进行隐式同步,也就是说,它无法覆盖尚未完成的任何命令的数据。它将等待前一帧完成或分配临时缓冲区,但在任何一种情况下,驱动程序将根据可用内存量知道哪个操作是合适的。这种行为可能是可取的,即使它可能会有一些轻微的性能影响。