I’m using a framebuffer with several large 1280x720 2D textures. I notice that the first time I draw into one of the textures, it takes 40-60ms, but after that, drawing into that same texture is fast. How can I avoid these slow first accesses? I haven’t noticed this issue on another competing mobile GPU.
This is on a Galaxy Nexus (PowerVR SGX 540) with Android 4.2.2. My application deals with video frames, but below is a simple program that demonstrates the problem simply using glClear(). Run the program and view the Android LogCat output and you’ll see that the first glClear() calls take 40-60ms and later ones take 0ms. Tap the screen to draw again, or kill the program and start it fresh to see the 40-60ms again. Thanks.
package com.example.gltest;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity implements GLSurfaceView.Renderer {
private static final String TAG = "MainActivity";
private static final int NUM_TEXTURES = 10;
// texture dimensions, fairly big
private static final int WIDTH = 1280;
private static final int HEIGHT = 720;
private int mFramebuffer;
private int[] mTextures = new int[NUM_TEXTURES];
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
int[] values = new int[1];
GLES20.glGenFramebuffers(1, values, 0);
checkGlError("glGenFramebuffers");
mFramebuffer = values[0];
GLES20.glGenTextures(mTextures.length, mTextures, 0);
checkGlError("glGenTextures");
for (int i = 0; i < mTextures.length; i++) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures);
checkGlError("glBindTexture");
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
WIDTH, HEIGHT, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
null);
checkGlError("glTexImage2D");
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D,
GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
checkGlError("glTexParameter");
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); // unbind
checkGlError("glBindTexture");
}
Log.i(TAG, "onSurfaceCreated complete");
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFramebuffer);
checkGlError("glBindFramebuffer");
// do this 5 times
for (int iterations = 0; iterations < 5; ++iterations) {
// glClear each texture
for (int i = 0; i < mTextures.length; ++i) {
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
mTextures, 0);
checkGlError("glFramebufferTexture2D");
long start = SystemClock.uptimeMillis();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
long end = SystemClock.uptimeMillis();
checkGlError("glClear");
Log.i(TAG, "clear[" + i + "]: " + (end - start) + " ms");
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER,
GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D,
0, 0);
checkGlError("glFramebufferTexture2D");
}
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); // unbind
checkGlError("glBindFramebuffer");
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {}
private void checkGlError(String op) {
int error;
while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) {
Log.e(TAG, op + ": glError " + error);
throw new RuntimeException(op + ": glError " + error);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final GLSurfaceView view = new GLSurfaceView(getApplication());
view.setEGLContextClientVersion(2);
view.setRenderer(this);
view.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
view.requestRender();
}
});
setContentView(view);
}
}
Observed output:
onSurfaceCreated complete
clear[0]: 52 ms <-- first glClear() of texture 0 is slow
clear[1]: 97 ms <-- first glClear() of texture 1 is slow
clear[2]: 70 ms
clear[3]: 69 ms
clear[4]: 52 ms
clear[5]: 48 ms
clear[6]: 45 ms
clear[7]: 73 ms
clear[8]: 60 ms
clear[9]: 64 ms
clear[0]: 0 ms <-- second glClear() of texture 0 is fast
clear[1]: 0 ms <-- second glClear() of texture 1 is fast
clear[2]: 0 ms
clear[3]: 0 ms
clear[4]: 0 ms
clear[5]: 0 ms
clear[6]: 0 ms
clear[7]: 0 ms
clear[8]: 0 ms
clear[9]: 0 ms