SkyBox Out Of Memory Error

Started by robert, October 24, 2012, 11:13:05 AM

Previous topic - Next topic

robert

Any idea why this happens ?

java.lang.OutOfMemoryError
at com.threed.jpct.GLRenderer.convertTexture(GLRenderer.java:845)
at com.threed.jpct.GLRenderer.setTextures(GLRenderer.java:2270)
at com.threed.jpct.GLRenderer.drawVertexArray(GLRenderer.java:2195)
at com.threed.jpct.World.draw(World.java:1307)
at com.threed.jpct.World.draw(World.java:1074)
at com.threed.jpct.util.SkyBox.render(SkyBox.java:201)
at com.example.MyRenderer.onDrawFrame(MyRenderer.java:254)

It happened to a single user out of 300 who have installed the application.

EgonOlsen


robert

Quote from: EgonOlsen on October 24, 2012, 11:16:14 AM
Textures too large...most likely. Consider some of these tips: http://www.jpct.net/wiki/index.php/Reducing_memory_usage#Reduce_texture_memory_usage

1 RGBA texture = 512x512x4 = 1048576 (1MB)
6 textures = 6MB

Is this really the reason ? The scene is very simple, it's just composed of a billboarded plane with a 256x256 texture, and a single mesh.

EgonOlsen

Quote from: robert on October 24, 2012, 02:31:25 PM
Is this really the reason ?
Yes, that's the reason if it happens in convertTexture(). You have to keep in mind that more memory than these 1mb per texture is being used. It approx. like this:

x*y*4 (Texture size * bytes) * 2 (Because there's a local copy and the one the GPU is using) + (x*0.5*y*0.5)*4 (first level of mip maps) + (x*0.25*y*0.25)*4 (second level of mip maps)...

So for a 512*512 texture, you get something like

512*512*4*2+256*256*4+128*128*4+...= ~ 2.5mb * 6 ~15mb

Older Android versions limit the amount of VM memory to 16mb or 24mb. Try to use some of the means mentioned in the wiki if possible.

robert

I see. So do I really need mipmap generation if I'm just rotating the camera around the scene ? How do I disable it for the SkyBox ?

EgonOlsen

You disable it for the textures, not for the object. If you need it depends on the scene. In case of a sky box, i would say that if you need mip maps to make the sky box look good, you don't need the high texture resolution you are using in the first place, because it won't be used anyway.

robert

Well, I disabled the mipmap for the skybox textures but I'm still getting crash reports  :(

Since I need 32 bit textures, the only other thing left to do seems to "Compress the in-memory copy of the texture data". Do you think this will help Egon ?

Also, the odd thing is that the crashes happen in the render method of the Skybox object, not while loading the Skybox textures...


Thomas.

or you can use Texture.defaultToKeepPixels(false) and Virtualizer ;)

EgonOlsen

Quote from: robert on October 25, 2012, 06:12:51 PM
Since I need 32 bit textures, the only other thing left to do seems to "Compress the in-memory copy of the texture data". Do you think this will help Egon ?

Also, the odd thing is that the crashes happen in the render method of the Skybox object, not while loading the Skybox textures...
It's worth a try. The place where it happens isn't odd. Texture will be uploaded to the gpu the first time they get rendered, not at load time.

robert

Quote from: Thomas. on October 25, 2012, 06:47:37 PM
or you can use Texture.defaultToKeepPixels(false) and Virtualizer ;)

@Thomas, If I use Texture.defaultToKeepPixels(false) and I reload all textures when the context is lost, then there is no need to use the Virtualizer right ?

@Egon, Unfortunately I tried to compress the textures but it takes too much time to load the skybox (from 2 to 8 seconds), which is unacceptable for a live wallpaper.


Thomas.

Yes, it is probably the best way.

robert

I used your suggestion of calling Texture.defaultToKeepPixels(false) and loading the textures every time the context is lost, but now I'm getting:

java.lang.NullPointerException
at com.threed.jpct.GLRenderer.drawVertexArray(GLRenderer.java:2139)
at com.threed.jpct.World.draw(World.java:1321)
at com.threed.jpct.World.draw(World.java:1083)
at com.threed.jpct.util.SkyBox.render(SkyBox.java:201)
at com.wallpaper.MyRenderer.onDrawFrame(MyRenderer.java:238)
at net.rbgrn.android.glwallpaperservice.GLThread.guardedRun(GLWallpaperService.java:627)
at net.rbgrn.android.glwallpaperservice.GLThread.run(GLWallpaperService.java:492)


and:

java.lang.RuntimeException: [ 1351899406641 ] - ERROR: A texture with the name 'back' has been declared twice!
at com.threed.jpct.Logger.log(Logger.java:189)
at com.threed.jpct.TextureManager.addTexture(TextureManager.java:138)
at com.wallpaper.MyRenderer.onSurfaceChanged(MyRenderer.java:133)
at net.rbgrn.android.glwallpaperservice.GLThread.guardedRun(GLWallpaperService.java:622)
at net.rbgrn.android.glwallpaperservice.GLThread.run(GLWallpaperService.java:492)


or this:

java.lang.RuntimeException: [ 1351899405201 ] - ERROR: Requested texture not found!
at com.threed.jpct.Logger.log(Logger.java:189)
at com.threed.jpct.TextureManager.getTexture(TextureManager.java:403)
at com.threed.jpct.util.SkyBox.(SkyBox.java:72)
at com.threed.jpct.util.SkyBox.(SkyBox.java:38)
at com.wallpaper.MyRenderer.onSurfaceChanged(MyRenderer.java:136)
at net.rbgrn.android.glwallpaperservice.GLThread.guardedRun(GLWallpaperService.java:622)
at net.rbgrn.android.glwallpaperservice.GLThread.run(GLWallpaperService.java:492)


Note that I call .flush() on the TextureManager before loading any texture, so I don't really have a clue about what's going on... and how come the errors are only triggered on some phones.

Here's the code I use in onSurfaceChanged():


    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    Log.d(TAG, "onSurfaceChanged() called");
    Resources res = mContext.getResources();
   
    // Flush the texture manager
    TextureManager tm = TextureManager.getInstance();
    tm.flush(); // This should clear the references to the textures, shouldn't it ?

    // Framebuffer setup
    if (mFrameBuffer != null) mFrameBuffer.dispose();
mFrameBuffer = new FrameBuffer(gl, width, height);
mWidth = width;
mHeight = height;

// World setup
if (mWorld != null) mWorld.dispose();
mWorld = new World();
mWorld.setAmbientLight(16, 16, 16);

// Sky box setup
String texPrefix = mPreferences.getString("skybox", null);
for (String sufix : mSkyBoxTexSufix) {  // mSkyBoxTexSufix is an array = {left, top, right....}
String name = texPrefix + "_" + sufix;
int id = res.getIdentifier(name, "drawable", mContext.getPackageName());
Log.d(TAG, "Loading new texture " + name);
Texture t = new Texture(res.getDrawable(id));
t.setMipmap(false);
tm.addTexture(sufix, t);
}
if (mSkyBox != null) mSkyBox.dispose();
mSkyBox = new SkyBox(1000);

        ....

        MemoryHelper.compact();
}




EgonOlsen

I bet the reason is the same as in the other thread about the array index problem: On some devices/Android versions, you start two threads doing the same thing at the same time, most likely du to multiple calls to on something(). That explains why textures are already there even after flush, it explains the memory problems and also the array index problems. Try to track down this behaviour and fix it in your wallpaper setup code.

robert

Quote from: EgonOlsen on November 03, 2012, 08:02:55 AM
I bet the reason is the same as in the other thread about the array index problem: On some devices/Android versions, you start two threads doing the same thing at the same time, most likely du to multiple calls to on something(). That explains why textures are already there even after flush, it explains the memory problems and also the array index problems. Try to track down this behaviour and fix it in your wallpaper setup code.

You were right, I ended up synchronizing onDrawFrame() and onScreenChanged() on the same object, and now the problems are gone. Too bad I couldn't fix the GLWallpaperService coded by Robert Green, but the code is a mess...