Chiral Code
Chiral Code

Reputation: 1436

Why repeatedly calling glBufferData crashes emulator?

During development I've noticed a strange "bug" which is reproducible only on Android emulator. I'm using x86 version with GPU acceleration. Please take a look a at the following code:

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;
        }

    }

}

This is for illustrative purposes only. Of course nothing will be displayed.

This renderer is used by a GLSurfaceView in the Activity. Every frame, renderer creates a new data store with data for 1000 quad vertices. If the emulator is left untouched for about a minute, frame rate begins to drop from 60 FPS to 1 FPS and eventually activity hangs up.

Question: is it my fault or is it something wrong with the emulator? Issue doesn't occur on real devices.

Upvotes: 1

Views: 336

Answers (1)

Andon M. Coleman
Andon M. Coleman

Reputation: 43359

First of all, glBufferData (...) completely re-allocates storage on the GPU every time you call it. If you are supplying new data to your VBO on every frame, you should consider allocating the buffer once, with the usage flag: GL_DYNAMIC_DRAW and then calling glBufferSubData (...) each frame. This will limit the number of things that happen per-frame to copying new data instead of deleting the old VBO memory, allocating new memory and then copying the data.

Deleting and re-creating buffer objects can be very expensive on some implementations, and the VBO's allocated memory will actually remain in-place until all pending OpenGL operations that needed it are flushed from the pipeline. It is possible that you are running out of memory simply because you have commands buffered for multiple frames.

glBufferSubData (...) will force an implicit synchronization, that is, it cannot overwrite the data for any commands that have yet to complete. It will either wait for the previous frame to finish or allocate a temporary buffer, but in either case the driver will know which action is appropriate based on how much memory is available. This behavior is probably desirable, even though it could have some minor performance implications.

Upvotes: 1

Related Questions