Maybe a bug? Blank screen after Home button pressed

Started by nimo, August 23, 2010, 11:53:25 PM

Previous topic - Next topic

nimo

Hi,
first of all, thank you very much for this amazing library for Android.
I noticed a strange behaviour in my 3D application, and I noticed that the same problem is present in your demo downloaded from the site, so you'll be able to verify directly.
To reproduce the problem:

1 - Open the demo application: it works fine
2 - Press the 'Home' button to return to Android home screen
3 - Open again the demo application: now I see only a blank screen  ???

In my application, this problem occurs also when pressing the 'back' button.
Same behaviour both on my HTC Desire with Froyo 2.2 and on the Android emulator.

Thank you very much for your support
Paolo

P.S.: Sorry for my bad english (I'm Italian  ;D)


EgonOlsen

#1
No, that's no a bug. If you press home or back or a call comes in or a thunder storm approaches, i.e. in almost every situation, either the surface or the whole activity can be paused/stopped/destroyed. Each of these actions destroys the open gl context. The demo doesn't really deal with this (as stated in its docs). It's up to you to handle this case, it's not part of the engine. However, it offers some implicit and explicit help to ease this.

Here's what i'm doing following the suggestions that raft gave me. It might not be perfect...anyway:


  • create almost all of your attributes as attributes of the Activity, not of the renderer.
  • set Config.glAvoidTextureCopies to false (or as an alternative call keepPixelData(true) on each texture). This will make jPCT upload the proper texture content again after a context change.
  • add a static attribute to your Activity to store the Activity itself.
  • once the activity has been fully created, store this into that attribute. Watch out that at least Android 2.2 likes to create and destroy the Activity one time before it actually does anything real with it, so don't do this directly in onCreate() but later or otherwise you might store an almost empty Activity.
  • in the onCreate(...)-method, check this attribute. If it's not null, use reflection to copy all the context of the stored activity into the newly created one
  • design the onSurfaceChanged() and onSurfaceCreated() methods in a way that they don't do unneeded or even harmful reinitialization work when creating a surfaces for the second or the third time. All i do then is to create a new framebuffer. This is mandatory, because the gl instance has changed.
  • ...anything else...i'm pretty sure, i just can't remember it. jPCT-AE does some internal things to recover from a context change automatically. You'll notice this from the log when using VBOs for example.

Or another solution that i used in my benchmark application: onPause(), onStop(),...all do a System.exit()...




raft

Quote from: EgonOlsen
once the activity has been fully created, store this into that attribute. Watch out that at least Android 2.2 likes to create and destroy the Activity one time before it actually does anything real with it, so don't do this directly in onCreate() but later or otherwise you might store an almost empty Activity.
i wasnt aware of this 2.2 thing. but i suppose we can still store our Activity in onCreate() method. for example onCreate() is still a good candidate for loading textures. this way, next call to onCreate() on next instance wont reload textures.

Quote
in the onCreate(...)-method, check this attribute. If it's not null, use reflection to copy all the context of the stored activity into the newly created one
is reflection really necessary here ? i copy necessary fields manually.

EgonOlsen

Quote from: raft on August 24, 2010, 05:31:23 PM
i wasnt aware of this 2.2 thing. but i suppose we can still store our Activity in onCreate() method. for example onCreate() is still a good candidate for loading textures. this way, next call to onCreate() on next instance wont reload textures.
That should work fine too. I don't know if it's common behaviour, but my 2.2. emulator does behave that way.
Quote
is reflection really necessary here ? i copy necessary fields manually.
No, of course not. You can copy them manually as well. But using reflection, i don't have to care about adding new fields. And the code is really simple:


       private void copy(AlienRunner src) {
try {
Logger.log("Copying data from master Activity!");
Field[] fs = src.getClass().getDeclaredFields();
for (Field f : fs) {
f.setAccessible(true);
f.set(this, f.get(src));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}

smither

What's wrong about this? I can't click the home screen and go back becuase i lose the cube.


public class Liberable extends Activity {
   private GLSurfaceView mGLView;
   private Renderer renderer = null;
   private FrameBuffer fb = null;
   private Light sun=null;
   private World world = null;
   private Object3D cube=null;
   private boolean paused = false;
   public Liberable _instance;
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      if(_instance==null)
      {
         mGLView = new GLSurfaceView(getApplication());
         mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
            public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
               // Ensure that we get a 16bit framebuffer. Otherwise, we'll fall
               // back to Pixelflinger on some device (read: Samsung I7500)
               int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
               EGLConfig[] configs = new EGLConfig[1];
               int[] result = new int[1];
               egl.eglChooseConfig(display, attributes, configs, 1, result);
               return configs[0];
            }
         });
         renderer = new Renderer();
         mGLView.setRenderer(renderer);
         setContentView(mGLView);
         _instance=this;
         Log.d("Liberable","0");
      }
      else
      {
         Log.d("Liberable","1");
         copy(_instance);
         renderer.reset();
      }
   }
   
   @Override
   protected void onPause() {
      _instance.paused = true;
      super.onPause();
      mGLView.onPause();
   }

   @Override
   protected void onResume() {
      if(_instance.paused)
      {
         Log.d("Liberable","2");
         copy(_instance);
         renderer.reset();
      }
      _instance.paused = false;
      super.onResume();
      mGLView.onResume();
   }

   protected void onStop() {
      renderer.stop();
      super.onStop();
   }

   private void copy(Liberable src) {
      try {
         Logger.log("Copying data from master Activity!");
         Field[] fs = src.getClass().getDeclaredFields();
         for (Field f : fs) {
            f.setAccessible(true);
            f.set(this, f.get(src));
         }
      } catch (Exception e) {
         throw new RuntimeException(e);
      }
   }
   
   class Renderer implements GLSurfaceView.Renderer {
      private boolean resetFB=false;
      private boolean stop = false;
      int w;
      int h;
      
      public Renderer()
      {
         Config.glAvoidTextureCopies=false;
      }
      @Override
      public void onDrawFrame(GL10 gl) {
         try {
            if (!stop) {
               if (paused) {
                  Thread.sleep(500);
               } else {
                  if(resetFB)
                  {
                     fb = new FrameBuffer(gl, w, h);
                     resetFB=false;
                  }
                  fb.clear();
                  world.renderScene(fb);
                  world.draw(fb);
                  fb.display();
               }
            }
            else {
               if (fb != null) {
                  fb.dispose();
                  fb = null;
               }
            }
         } catch (Exception e) {
            Logger.log("Drawing thread terminated!", Logger.MESSAGE);
         }
      }

      @Override
      public void onSurfaceChanged(GL10 gl, int w, int h) {
         if (fb != null) {
            fb.dispose();
         }
         fb = new FrameBuffer(gl, w, h);
         Log.d("Pepe", "w:"+w+",h:"+h);
         this.h=h;
         this.w=w;
         Log.d("Liberable","Changed");
      }

      @Override
      public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//         if (fb != null) {
//            fb.dispose();
//         }
//         fb = new FrameBuffer(gl, w, h);
         Log.d("Liberable","Created");
         TextureManager.getInstance().flush();
         world = new World();
         Resources res = getResources();
         TextureManager tm = TextureManager.getInstance();
         Texture lineasT=new Texture(res.openRawResource(R.raw.blanco),true);
         tm.addTexture("white", lineasT);
         cube=Primitives.getCube(3);
         cube.setTexture("white");
         cube.build();
         world.addObject(cube);
         sun = new Light(world);
         Camera cam = world.getCamera();
         cam.moveCamera(Camera.CAMERA_MOVEOUT, 10);
         cam.lookAt(cube.getTransformedCenter());
         cam.setFOV(1.5f);
         sun.setIntensity(250, 250, 250);
         SimpleVector sv = new SimpleVector();
         sv.set(cube.getTransformedCenter());
         sv.y -= 300;
         sv.x -= 100;
         sv.z += 200;
         sun.setPosition(sv);
      }
   
      public void stop() {
         stop = true;
         if (fb != null) {
            fb.dispose();
            fb = null;
         }
      }
      public void reset()
      {
         resetFB=true;
         
      }
   }
}

raft

try making _instance a static field.
private static Liberable _instance;

you want that field survive among Liberable instances but making it non-static does not help that

smither

Yes, sorry about that, i'd tried with the static approach before posting and with the same results, then I tried with the non-static approach (just to test)  and that's why is posted that way  ;D.

Somehow making the _instance attribute static doesn't prevent me from getting a black screen when I return to the view.

raft

i see. looking again to your code, i'm not sure you can share renderer this way. when a second Liberable is created setContentView is not called on that instance. that's possibly why you get black screen: if there is no view there is nothing to display.

smither

#8
Looking at the Logcat there's no second call to the method onCreate()  ???, that's weird...

Edit: It's being called now, but i wonder what view to use to setContenView() because if i try to use the same one I have in the instance I get an exception because the view belong to it's parent,however, i try on the onPause() to remove the view from the parent but the result is the same.

EgonOlsen

I think i wasn't clear on this, but i'm not sharing renderer and view and all that stuff from oncreate(). It might get copied too, but will be replaced afterwards by creating new instances. I.e. u'm doing the copying first, then i'm doing the 'normal' init work of oncreate and i'm doing this each time that the method is being called. If a renderer already exists, i'm stopping it in addition...just to be sure.

smither

I solved the problem, but i have to create a new GLSurfaceView instance and set the content with it on the onResume() method for the second time, Don't know if this is the properly approach but it's the only way I've found to do it.


INeedMySpace

I found following solution to resolve same situation. If my JPCT-AE application will be game, I don't really need several instances of my application running (read activities). Thus I made my gl renderer and its data (3d objects, textures etc) static and add check for initialization of data. That helps then it comes to activity start/stop, pause/resume and also to device orientation change (since android restarts activity on orientation change). Hope such approach can help you.
Right here, right now ...or later in some other place

K24A3

I have a similar problem in my Wallpaper.

I was getting intermittent crashes when going in and out of the wallpaper settings. But the wallpaper was still running in the background. Objects would intermittently disappear and reappear.

So as suggested in this thread to re-create the GLViewSurface, I added this to onResume() in my WallpaperService class:


if (renderer != null)
{
renderer.release();
renderer = null;

// New renderer
renderer = new WallpaperRenderer(getApplicationContext());
//setRenderer(renderer); // Causes crash, "setRenderer is already set"
//setRenderMode(RENDERMODE_CONTINUOUSLY);
}


..which basically re-creates my WallpaperRenderer class, which implements GLSurfaceView.Renderer.

Now that actually fixed the problem on my Gingerbread phone completely, but on my HoneyComb tablet I'm now seeing a texture problem.

If I have some other wallpaper running, then go to Settings->Wallpaper->[select my wallpaper], everything is fine.

But if I have my own wallpaper running in the background and then go to Settings->Wallpaper->[select my wallpaper], all textures are missing in the wallpaper preview screen. I still see the objects but they are blank objects with no texture. But if I press "Set Wallpaper", everything is fine and the wallpaper gets all its textures back. Then if I go back into the wallpaper selection screen, I get a crash (see below), but the wallpaper is still running behind the error.

02-01 00:02:23.670: E/AndroidRuntime(13380): FATAL EXCEPTION: GLThread 10
02-01 00:02:23.670: E/AndroidRuntime(13380): java.lang.NullPointerException
02-01 00:02:23.670: E/AndroidRuntime(13380):    at com.threed.jpct.World.draw(World.java:1333)
02-01 00:02:23.670: E/AndroidRuntime(13380):    at com.threed.jpct.World.draw(World.java:1135)


01-31 23:44:32.060: I/jPCT-AE(13336): Visibility lists disposed!
01-31 23:44:32.060: I/jPCT-AE(13336): All texture data unloaded from gpu!
01-31 23:44:32.060: I/jPCT-AE(13336): Disposing VBOs!
01-31 23:44:32.060: I/jPCT-AE(13336): Renderer disposed!
01-31 23:44:32.060: I/jPCT-AE(13336): Static references cleared...
01-31 23:44:32.060: I/jPCT-AE(13336): OpenGL vendor:     NVIDIA Corporation
...and the rest of it..

and then a huge amount of:
01-31 23:44:32.480: I/jPCT-AE(13336): [ 1328013872491 ] - WARNING: OpenGL context has changed...trying to recover...
(I was seeing this earlier as well)

I tried to use temp.keepPixelData(true) on each texture, but that made no difference.
I have a static class that saves the instance of my wallpaper.java class so I'm not reloading all the textures and objects when onSurfaceCreated is called.

I'm using Robert Green's GLWallpaperService btw.
If someone has a working wallpaper using GLWallpaperService, can you please send me the WallpaperService.java file and WallpaperRender.java file?

EgonOlsen

Try to set the Logger to debug mode and post the log output for each individual step. Maybe that helps to find the problem. Which renderer are you using? 1.x or 2.0?

K24A3

I'm using 1.x. It seems that somehow two instances of the wallpaper are causing a conflict. Perhaps jPCT is identifying things based on the package name?

It's getting late here so I'll get onto it tomorrow. cheers.