Android OpenGL textures: creating and deleting them on the fly
Asked Answered
S

2

5

I am currently implementing a 3D viewer which basically renders a subset of all the images the user has on his SD Card. The closest matching product I would think of would be CoolIris:

enter image description here

It simply show a scrolling board of N tiles on screen, each showing different images, with new tiles entering the screen and showing new images.

Now for my problem: I have the program working and rendering nicely the quads. When a quad goes out of the screen, it gets recycled/released. And new quads keep on being added to the tile board before they enter the screen.

Because there can be hundreds of images, the textures need to be created and deleted on the fly (so that we don't run out of memory). The problem I have is that after I delete textures, newly created textures seem to get some IDs of other textures currently in use.

My rendering loop looks like this:

void render(GL10 gl) {
  0. Move the camera

  // Tile board maintenance
  1. Remove tiles out of screen
  2. Add new tiles which are about to enter screen

  // Texture handling
  3. glDeleteTextures on all unused textures followed by glFlush
  4. For newly used images
     - Create corresponding Bitmap
     - Create the OpenGL texture, followed by glFlush
     - Release the Bitmap

  // Rendering
  5. Render the tile (using a textured quad)

}

To give a better idea of how the data is organised, here is an overview of the classes:

TileBoard {
  Image[] allImages;
  Tile[] board;
}

Tile {
  Image image;
}

Image {
  String path;
  int textureId;
  int referenceCount;
}

Texture creation code:

protected void loadSingleTexture(GL10 gl, long objectId, Bitmap bmp) {
    int[] textures = new int[1];
    gl.glGenTextures(1, textures, 0);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
    gl.glFlush();

    if (bmp != null) bmp.recycle();

    if (listener != null) listener.onTextureLoaded(gl, objectId, textures[0]);
}

Texture deletion code:

// pendingTextureUnloads is a Set<Integer>
if (pendingTextureUnloads.size() > 0) {
  int[] textureIds = new int[pendingTextureUnloads.size()];
  int i = 0;
  Iterator<Integer> it = pendingTextureUnloads.iterator();
  while (it.hasNext()) {
    textureIds[i] = it.next();
  }

  gl.glDeleteTextures(textureIds.length, textureIds, 0);
  gl.glFlush();
}
Schopenhauer answered 13/3, 2012 at 12:14 Comment(1)
Tried calling glGetError anywhere?Noelianoell
S
5

I have solved the problem: the issue was that you have to keep the texture array passed to glGenTextures and reuse it.

Here is the modified overview for the ones who will have the same problem:

Image {
  String path;
  int[] textureIds;
  int referenceCount;
}

Texture creation code:

// Notice that I don't allocate the int[] at the beginning but use the one of the image
protected void loadSingleTexture(GL10 gl, Image img, Bitmap bmp) {
    gl.glGenTextures(1, img.textureIds, 0);
    gl.glBindTexture(GL10.GL_TEXTURE_2D, img.textureIds[0]);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
    GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bmp, 0);
    gl.glFlush();

    if (bmp != null) bmp.recycle();
}

Texture deletion code:

foreach Image img {
  gl.glDeleteTextures(img.textureIds.length, img.textureIds, 0);
}
gl.glFlush();
Schopenhauer answered 14/3, 2012 at 10:29 Comment(1)
It would be nice if someone knows how this all works in detail and why the same array has to be used in this case. I've seen many other examples when the textures are generated using multiple 1-dim arrays. Also, do you not initiate the array before calling glGenTextures?Aniseed
L
4

I know you said that you solved your issue, but I think I noticed your error in the first bit of code. Look at the while loop in your texture deletion code, you are not increasing the index i for your array, so you will continuously assign the first index until you reach the last entry in pendingTextureUnloads, the rest of the indices will be 0 (null). That might be problematic.

And by the way, I have got texture generation working by not reusing the array, just returning the index that was generated by glGenTextures. My code is to the line equal to yours, except from creating a new int array in the beginning of the method and returning the int at the first index at the end. Your code for texture generation should work, the error was just in the texture deletion.

Leanoraleant answered 20/12, 2013 at 8:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.