Android OpenGL ES 2.0 -- glReadPixels() and glTexImage2D() drawing a black texture?

Jon Gill picture Jon Gill · Aug 26, 2011 · Viewed 9.9k times · Source

I'm working on some Android code for caching and redrawing a framebuffer object's color buffer between the loss and recreation of EGL contexts. Development is primarily happening on a Xoom tablet running Honeycomb. Anyway, what I'm trying to do is store the result of calling glReadPixels() on the FBO in a direct ByteBuffer, then use that buffer with glTexImage2D() and draw it back into the (now cleared) framebuffer. All of this seems to work fine — the ByteBuffer contains the right values ([-1, 0, 0, -1] etc. for a pixel, according to Java's inability to understand unsigned bytes), no GlErrors seem to be thrown, and the quad is drawn to the right part of the screen (currently the top-left quarter of the framebuffer for testing purposes).

However, no matter what I try, glTexImage2D() always outputs a plain black texture. I've had some issues with this before — when displaying Bitmaps, I eventually gave up trying to use the basic GLES20.glTexImage2D() with Buffers and skipped to using GLUtils.glTexImage2D(), which processes the Bitmap for you. Unfortunately, that's less of an option here (I did actually try converting the ByteBuffer to a Bitmap so I could use GLUtils, without much success), so I've really run out of ideas.

Can anyone think of anything that could be causing glTexImage2D() to not correctly process a perfectly good ByteBuffer? Any and all suggestions would be welcome.

ByteBuffer pixelBuffer;

void storePixels() {
  try {
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fbuf);
    pixelBuffer = ByteBuffer.allocateDirect(width * height * 4).order(ByteOrder.nativeOrder());
    GLES20.glReadPixels(0, 0, width, height, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixelBuffer);
    GLES20.glBindFrameBuffer(GLES20.GL_FRAMEBUFFER, 0);
    gfx.checkGlError("store Pixels");
  }catch (OutOfMemoryError e) {
    pixelBuffer = null;
  }
}

void redrawPixels() {
  GLES20.glBindFramebuffer(GL20.GL_FRAMEBUFFER, fbuf);
  int[] texId = new int[1];
  GLES20.glGenTextures(1, texId, 0);
  int bufferTex = texId[0];
  GLES20.glBindTexture(GL20.GL_TEXTURE_2D, bufferTex);
  GLES20.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MAG_FILTER, GL20.GL_LINEAR);
  GLES20.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_MIN_FILTER, GL20.GL_LINEAR);
  GLES20.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_S, repeatX ? GL20.GL_REPEAT
    : GL20.GL_CLAMP_TO_EDGE);
  GLES20.glTexParameterf(GL20.GL_TEXTURE_2D, GL20.GL_TEXTURE_WRAP_T, repeatY ? GL20.GL_REPEAT
    : GL20.GL_CLAMP_TO_EDGE);
  GLES20.glTexImage2D(GL20.GL_TEXTURE_2D, 0, GL20.GL_RGBA, width, height, 0, GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixelBuffer);
  gfx.drawTexture(bufferTex, width, height, Transform.IDENTITY, width/2, height/2, false, false, 1);
  GLES20.glDeleteTextures(1, IntBuffer.wrap(new int[] {bufferTex}));
  pixelBuffer = null;
  GLES20.glBindFrameBuffer(GLES20.GL_FRAMEBUFFER, 0);
}

gfx.drawTexture() builds a quad and draws it to the currently bound framebuffer, by the way. That code has been well-tested in other parts of my project — it shouldn't be the issue here.

Answer

Jon Gill picture Jon Gill · Aug 30, 2011

For those of you playing along at home, this code is in fact totally valid. Remember when I swore blind that gfx.drawTexture() has been well-tested and shouldn't be the issue here"? Yeah, it was totally the issue there. I was buffering vertices to draw without actually flushing them through a glDrawElements() call. Whoops.