Error in live wallpaper

Started by kkl, April 29, 2013, 06:27:12 PM

Previous topic - Next topic

kkl

Hi Egon, I got strange error after I viewed my live wallpaper in live wallpaper settings. It seems like some objects are bound statically. After disposing frame and world (after quiting the settings), the running live wallpaper received an error like the following.


04-30 00:07:20.167: E/AndroidRuntime(13968): FATAL EXCEPTION: GLThread 31292
04-30 00:07:20.167: E/AndroidRuntime(13968): java.lang.NullPointerException
04-30 00:07:20.167: E/AndroidRuntime(13968): at com.threed.jpct.GLRenderer.drawVertexArray(GLRenderer.java:2169)
04-30 00:07:20.167: E/AndroidRuntime(13968): at com.threed.jpct.World.draw(World.java:1348)
04-30 00:07:20.167: E/AndroidRuntime(13968): at com.threed.jpct.World.draw(World.java:1089)
04-30 00:07:20.167: E/AndroidRuntime(13968): at com.kision.firefliesforest.MainRenderer.draw(MainRenderer.java:130)
04-30 00:07:20.167: E/AndroidRuntime(13968): at com.kision.firefliesforest.BasicRenderer.onDrawFrame(BasicRenderer.java:104)
04-30 00:07:20.167: E/AndroidRuntime(13968): at net.rbgrn.android.glwallpaperservice.GLThread.guardedRun(GLWallpaperService.java:669)
04-30 00:07:20.167: E/AndroidRuntime(13968): at net.rbgrn.android.glwallpaperservice.GLThread.run(GLWallpaperService.java:534)


Steps to reproduce:
1. Set my live wallpaper to phone.
2. Goto Live Wallpaper settings (the place where you select your live wallpaper), view the preview of my live wallpaper.
3. Quit the preview and then Live Wallpaper settings.
4. When screen goes back to home screen, the running live wallpaper in phone receives error.

Is it a bug or I did it wrong? Are JPCT objects created and disposed statically? 

EgonOlsen

No, they aren't static in any way. Might be related to the problem described in the other current thread about wallpapers...IIRC, the wallpaper runs in two different threads while switching between normal view and back. Try to synchronize both render methods so that only one can be active at a time and see if that helps.

kkl

I'm not sure how to synchronize both render as they are totally different instance, but I did find out what happened to the error after debugging for few days. The textures uploaded are static in gpu (I assume). If I don't flush the textures, it works perfectly. Probably because I'm re-using the textures from current wallpaper for the preview in settings (to avoid out-of-memory issue). So I can't flush the textures in wallpaper preview if my wallpaper's already running at home screen (but I do dispose frame buffer and world). However, if I open the wallpaper preview and set the wallpaper to the phone repeatedly (around 5 times), it shows out of memory error. I suspect the textures may be still referencing to frame buffer and world, even when disposing them, they are not collected by gc. Any idea how to solve it?


//GLEngine
                @Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
renderer.init();
setTouchEventsEnabled(true);
}

                @Override
public void onDestroy() {
WallpaperManager wpm = (WallpaperManager) getSystemService(WALLPAPER_SERVICE);
WallpaperInfo info = wpm.getWallpaperInfo();
if (info != null && info.getComponent().equals(new ComponentName(FireFliesForestWallpaperService.this,
FireFliesForestWallpaperService.this.getClass()))) {
    renderer.clearObjects();
} else {
    renderer.dispose();
}
Log.v("kkl", "Destroyed!");
setTouchEventsEnabled(false);
super.onDestroy();
}

//renderer
          public void init(){
world = new World();
world.setAmbientLight(50, 50, 220);

Camera cam = world.getCamera();
cam.moveCamera(Camera.CAMERA_MOVEOUT, 50f);
cam.lookAt(new SimpleVector(0, 0, 0));

safeCreateAllTextures();
createAllObjects(world); //creates all 3D objects and add them to world

world.setFogging(World.FOGGING_ENABLED);
world.setFogParameters(50f, 120f, 0, 0, 0);
MemoryHelper.compact();
}

public void safeCreateAllTextures() {
if (TextureManager.getInstance().containsTexture(TextureId.TREE_LEAF_1)) {
Log.v("kkl", "Textures exist. Not creating textures..");
return;
}
createAllTextures(); //adds textures to TextureManager and disposes the bitmaps
}

        //When renderer.dispose() is called
        public void onDisposed() {
clearObjects();
clearTextures();
System.gc();
}

public void clearObjects() {
if (world != null) {
world.dispose();
world = null;
}
if (frame != null) {
frame.dispose();
frame = null;
}
}

public void clearTextures(){
TextureManager.getInstance().flush();
}


EgonOlsen

Textures are not bound to world or framebuffer, but their copy on the gpu is bound to the GL context. flush() removes textures from the manager, it doesn't unload them from the gpu. In the normal case, there's no need to call flush. What you might want instead is unload().

K24A3

You should limit the number of renderers to 1 max, which means if the routine that creates the renderer is called, destroy the existing one and null the reference before creating the new one.

You should also create everything 3D related on the same thread, the main activity or wallpaper service thread.

kkl

#5
Thanx Egon. I think unload() should help alot. However, I still hope that I could reuse the texture without unloading them, so the live wallpaper does not have to upload the texture again (Uploading texture might cause the livewallpaper freezes for awhile). Anyway, I'm suspecting if I don't unload the texture, the gl context might be still pointing to the texture and gl context cannot be collected by gc, then causes memory leak. Will find better solution if possible.

K24A3, thanx for the suggestion. BTW, if we destroy the existing one before creating one, the existing one might resume ( onResume() ) its own wallpaper after quitting the preview livewallpaper. Presumably, It might cause error as the existing one is destroyed after viewing the preview. Perhaps I still don't quite know very much about android wallpaper service behavior yet about destroying the previous livewallpaper. Hope you would explain more ;)