Speed up Android OpenGL ES glReadPixels by using PBO

[ ]

Recently, I am working on a prototype which requires to use OpenGL to process the streams from Camere and save the processed results to a Video. Thus the overall pipeline is that:

CameraDevice -> ImageReader -> OpenGL -> MediaCodec

I choose OpenGL to proess the streams because the CPUs is too slow to perform real time processing. However, with OpenGL I find I still cannot achieve 30 FPS on 1080P, where the bottle neck nows comes from OpenGL -> MediaCodec.

To read the data from OpenGL, I use a texture, which is configured as:

GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(mGLUniformTexture, 0);

Then read the results as:

GLES20.glReadPixels(0, 0, mRowStride, mInputHeight, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE);

However the profiling indicates glReadPixels could take about 40ms on 1080P, which makes it impossible for 30FPS. The reason is that glReadPixels for texture is synchronous thus it needs to wait for all the OpenGL operations to finish before the reading.

After some research, I decided to use Pixel Pack Buffer (PBO), where glReadPixels for PBO is asynchronous while glReadPixels for texture is synchronous. To fully utilize this asynchronous, we will need to have two PBOs working in a ping-pong buffer: one for reading out and one for processing.

The code for configuring the PBO (note the configuration of texture is still required):

mPboIds = IntBuffer.allocate(2);
GLES30.glGenBuffers(2, mPboIds);

GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(0));

GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(1));


The code for reading data from PBO:

private ByteBuffer bindPixelBuffer() {
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboIndex));
    ByteBuffer result = GLES30.glReadPixels(0, 0, mRowStride, mInputHeight, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE);

    if (mInitRecord) {
        mInitRecord = false;

    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, mPboIds.get(mPboNewIndex));

    ByteBuffer byteBuffer = (ByteBuffer) GLES30.glMapBufferRange(GLES30.GL_PIXEL_PACK_BUFFER, 0, mPboSize, GLES30.GL_MAP_READ_BIT);


    mRecordHelper.onRecord(byteBuffer, mInputWidth, mInputHeight, mRowStride, mLastTimestamp);
    return result;

private void unbindPixelBuffer() {
    GLES30.glBindBuffer(GLES30.GL_PIXEL_PACK_BUFFER, 0);

    mPboIndex = (mPboIndex + 1) % 2;
    mPboNewIndex = (mPboNewIndex + 1) % 2;

With PBO, the reading time is about 20ms.


Android 关于美颜/滤镜 利用PBO从OpenGL录制视频 MagicCamera

Written on October 17, 2017