Karam Karam - 29 days ago 16
Android Question

Android OpenGL ES Updating VBO data every frame

What is the best way to update VBO data every frame? Currently I am using glBufferSubData after calling glBufferData with null every frame, and I cannot achieve more than 25fps without getting odd stuttering.

My main goal is to flip between two images at 50fps.

Here is some code:

public void onDrawFrame(GL10 glUnused) {
endTime = System.currentTimeMillis();
dt = endTime - startTime;
if (dt < 20)
try {
Thread.sleep(20 - dt);
} catch (InterruptedException e) {
e.printStackTrace();
}
startTime = System.currentTimeMillis();

...some matrix operations

render();
}

void render() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vbo[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, floatbuffer.capacity() * BYTES_PER_FLOAT,
null, GLES20.GL_STREAM_DRAW);

...a bunch of if statements

if(side==1) {
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, floatbuffer.capacity() * BYTES_PER_FLOAT,floatbuffer);
side = 0;
} else {
GLES20.glBufferSubData(GLES20.GL_ARRAY_BUFFER, 0, floatbuffer2.capacity() * BYTES_PER_FLOAT, floatbuffer2);
side = 1;
}

GLES20.glVertexAttribPointer(positionAttribute, POSITION_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, 0);
GLES20.glEnableVertexAttribArray(positionAttribute);

GLES20.glVertexAttribPointer(colorAttribute, COLOR_DATA_SIZE_IN_ELEMENTS, GLES20.GL_FLOAT, false, STRIDE, (POSITION_DATA_SIZE_IN_ELEMENTS) * BYTES_PER_FLOAT);
GLES20.glEnableVertexAttribArray(colorAttribute);

GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, ibo[0]);
GLES20.glDrawElements(GLES20.GL_TRIANGLE_STRIP, indexCount, GLES20.GL_UNSIGNED_SHORT, 0);

GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ELEMENT_ARRAY_BUFFER, 0);
}


I also tried setting up multiple vbo's each with its own data that doesn't change and calling glBindBuffer on the vbo I want to show, and I still noticed the stuttering.

I'm a little new to OpenGL so I'm unclear on the best way to proceed. I understand there is likely a bottleneck in transferring data from the CPU to the GPU. Is there a more efficient way of approaching this?

Answer

It's worth noting that nearly all Android platforms use vsync to lock the final framebuffer scan out to some multiple of the display controller update frequency, to avoid tearing the output during scan-out.

Typically the display will be a 60FPS panel, so you need to get a new update out of the application every 16.6 ms. If you miss a vsync deadline (which you are guaranteed to do with 20 ms sleep) then you will at least get some minor stutter where one frame has to be displayed for more than one frame to bridge the gap in the incoming frame sequence. The application has zero control about which frame is duplicated, so you can't really correct for it ahead of time.

If you application is, for example, 45FPS then you will end up with a final output frame sequence on the display controller which looks like 1, 1, 2, 3, 3, 4, 5, 5, 6, etc. (e.g the display MUST scan out one frame every 1/60th of a second and will just run the latest it has). The display times don't match the exact 1/45th of a second step the application is using, so this will jitter the output animations visually.

The only real fix is to emit frames on a multiple of the panel scan out time (e.g. 60FPS = 16.6ms, 30FPS = 33.3ms, 20FPS = 50ms).