Search code examples
androidopengl-estextures

Frequent switching over to another texture crushes the game


I make a tiny pretty test game for Android using Open GL ES (1.1). For simplicity and convenience in dealing with textures I use the class containing the following methods encapsulating necessarry gl-operations (in the code I use some own additional classes):

public void load() {
    GL10 gl = glGraphics.getGL();
    int[] textureIds = new int[1];
    gl.glGenTextures(1, textureIds, 0);
    textureId = textureIds[0];
    InputStream in = null;
    try {
        in = fileIO.readAsset(fileName);
        Bitmap bitmap = BitmapFactory.decodeStream(in);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
        GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
        gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
        bitmap.recycle();
    }
    catch(IOException e) {
        throw new RuntimeException("Couldn't load texture '" + fileName + "'", e);
    }
    finally
    {
        if(in != null)
        {
            try {
                in.close();
            }
            catch (IOException e) { }
        }
    }
}

public void reload() {
    load();
    setFilters(minFilter, magFilter);
    glGraphics.getGL().glBindTexture(GL10.GL_TEXTURE_2D, 0);
}

public void setFilters(int minFilter, int magFilter) {
    this.minFilter = minFilter;
    this.magFilter = magFilter;
}

public void bind() {
    GL10 gl = glGraphics.getGL();
    gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, minFilter);
    gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, magFilter);
}

public void dispose() {
    GL10 gl = glGraphics.getGL();
    gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
    int[] textureIds = { textureId };
    gl.glDeleteTextures(1, textureIds, 0);
}

Starting to work with some texture I load() it. When I wish to draw a sprite I use the method bind() for the texture. When the context may be lost I use the method reload(). When I say good bye to the texture I call dispose().

I use two texture atlases 1136 x 640 simultaneously (one of them contains playing elements and the other contains the background). To avoid boredom, when the user leaves to the start game menu and come into the game itself again the atlases switch over to two similar atlases colored in some other way.

The problem is that if the user do it frequently then each time the game loads slower and slower and in a few times (5-7) crashes. Disposing method works in time, the method finalize() for the whole class works in time.

Have you any ideas what it may be?


Solution

  • It's difficult to say with the information you've given, but you're probably running out of memory. Are you calling your reload() method at all during this process? One thing I noticed is that your reload() method will create an entirely new copy of the texture, while never deleting the previous copy.

    To summarize: make sure every call to glGenTextures is paired with a call to glDeleteTextures. An easy but slow fix to your reload method would be to do:

    public void reload() {
       // call dispose() before load()
       dispose();
       load();
       // ...
    }
    

    But a safer fix is to just replace the contents of your previously allocated texture if load() is called again. In your load() method skip the call to glGenTextures if the texture Id has been allocated. Like so:

    // initialize texture id
    private int textureId = 0;
    
    public void load() {
       // ...
    
       // only allocate texture if needed
       if (textureId == 0) {
          int[] textureIds = new int[1];
          gl.glGenTextures(1, textureIds, 0);
          textureId = textureIds[0];
       }
    
       // ...
    }
    
    public void dispose() {
       // ...
    
       // delete texture if allocated, and
       // set the id to zero
       if (textureId != 0) {
          int[] textureIds = { textureId };
          gl.glDeleteTextures(1, textureIds, 0);
          textureId = 0;
       }
    
       // ...
    }
    

    I'd also recommend trying power of two textures (i.e. 1024x512 instead of 1136x640). I've seen some chips / drivers have undocumented issues with non-POT textures.