How to save OpenGL textures when lose context on Android - android

Our game requires that several large textures are built up dynamically from small images, and we don't keep a data copy of the textures, we just use OpenGL with glTexSubImage2d to update the actual textures.
When Back is pressed, or the game loses focus, we want to save these textures to ram, so if the user then goes back to the game, we can recreate/reload them in onSurfaceCreated. We already reload from 'disk' all static textures.
However, its proving hard to find out where we can get a message to know that we are about to lose the EGL context. If we set a flag in the main activity's onBackPressed then check that flag in the main render loop, we can save the textures there, but we can't access the textures from the main activity, so putting the call in the main activity's onStop callback is too late. There is no subsequent render loop to save the textures, as, well, its stopped by then. We need to do this as there will be other ways where onStop is called other than pressing the Back button.
Supposedly I can't delay onStop to get one more "render" in either. Any other ideas ?

Since you already generate this textures dynamically during the cold start of your application, it may be a good point just to part this textures dirty and regenerate them just as if your application was starting cold.
If you want to save those textures then don't wait for onStop() to be called. Just save them once you have generated their content.

It sounds as a problem of synchronising threads. The button pressed event or the onPause event (in case another app goes to the front) runs on the main (UI) thread, while the opengl render has his own thread. When the events on UI thread are triggered, you set a flag and wait on a condition_variable. the render thread checks always the flag, and when it is set then saves the textures, changes the condition of the condition_variable and notifies the UI thread.

Related

Should the renderingThread of a SurfaceView have the same life-cycle as the view or the activity?

When creating a SurfaceView it's normal to also create a separate thread to draw onto the surface. Is it better programming practice to have the thread be created and destroyed at the same time the activity is, or at the same time the surface is?
What are some of the advantages/pitfalls of either way?
The Activity and the View are created at essentially the same time. The Surface is created later, and that's what the SufaceHolder callbacks are for.
You can't render on the Surface before it exists or after it's destroyed, so there's no point in starting your rendering thread before then or leaving it running after. The tricky part is that the callbacks happen on the main UI thread (since that's where you set it up), so the surfaceDestroyed() callback could be called while your render thread is doing work.
EDIT:
Some notes about the SurfaceView / Activity lifecycle are included below. These are now part of the official Android documentation; see Appendix B in the System-Level Graphics doc. The original post is available below for historical purposes.
You can see examples of both approaches in Grafika. Approach #1 (create/destroy thread in onResume/onPause) can be seen in TextureFromCameraActivity, approach #2 (create/destroy thread in surfaceCreated/surfaceDestroyed) can be seen in HardwareScalerActivity and RecordFBOActivity.
A few thoughts about app life cycle and SurfaceView.
There are two somewhat independent things going on:
Application onCreate / onResume / onPause
Surface created / changed / destroyed
When the Activity starts, you get callbacks in this order:
onCreate
onResume
surfaceCreated
surfaceChanged
If you hit "back", you get:
onPause
surfaceDestroyed (called just before the Surface goes away)
If you rotate the screen, the Activity is torn down and recreated, so you get
the full cycle. (You can tell it's a "quick" restart by checking isFinishing().) It might be possible to start / stop an activity so quickly that surfaceCreated() might happen after onPause(), but I'm not sure about that.
If you tap the power button to blank the screen, however, you only get onPause() --
no surfaceDestroyed(). The Surface remains alive, and rendering can continue (you
even keep getting Choreographer events if you continue to request them). If you have
a lock screen that forces a specific orientation your Activity can get kicked, but
if not you can come out of screen-blank with the same Surface you had before.
This raises a fundamental question when using a separate renderer thread with
SurfaceView: should the lifespan of the thread be tied to the Surface or to the
Activity? The answer is: it depends on what you want to have happen when the screen
goes blank. There are two basic approaches: (1) start/stop the thread on Activity
start/stop; (2) start/stop the thread on Surface create/destroy.
#1 interacts well with the app lifecycle. We start the renderer thread in onResume() and
stop it in onPause(). It gets a bit awkward when creating and configuring the thread
because sometimes the Surface will already exist and sometimes it won't. We can't simply
forward the Surface callbacks to the thread, because they won't fire again if the
Surface already exists. So we need to query or cache the Surface state, and forward it
to the renderer thread. Note we have to be a little careful here passing objects between
threads -- best to pass the Surface or SurfaceHolder through a Handler message, rather
than just stuffing it into the thread, to avoid issues on multi-core systems (cf.
Android SMP Primer).
#2 has a certain appeal because the Surface and the renderer are logically intertwined.
We start the thread after the Surface has been created, which avoids the inter-thread
communication concerns. Surface created / changed messages are simply forwarded. We
need to make sure rendering stops when the screen goes blank, and resumes when it
un-blanks; this could be a simple matter of telling Choreographer to stop invoking the
frame draw callback. Our onResume() will need to resume the callbacks if and only if
the renderer thread is running. It may not be so trivial though -- if we animate based
on elapsed time between frames, we could have a very large gap when the next event
arrives, so an explicit pause/resume message may be desirable.
The above is primarily concerned with how the renderer thread is configured and whether
it's executing. A related concern is extracting state from the thread when the
Activity is killed (in onPause() or onSaveInstanceState()). Approach #1 will work
best for that, because once the renderer thread has been joined its state can be
accessed without synchronization primitives.

Android Activity lifecycle and OpenGL ES 2.0 (VBOs, Shaders,..)

Could you describe me when I should recreate vbos, shaders, textures? I know that OpenGL functions are called in separated thread. I call GLSurfaceView's method onResume in Activity's onResume (the same with onPause method).
When the GLContext is lost?
Let's assume that the user touch home button. Activity's onPause method is called. Should I delete vbo, shaders, textures? Or should I delete those resources when onDestroy is called (resume GL Thread to delete them?)? What if user touch back button?
The another question. Should I keep bitmaps, vertices attributes in Java to reinit VBOs, Textures?
Now I delete all shaders, vbos & textures each time the Activity's onPaused method is called. I send a special event to event queue. In onSurfaceCreated I reinit all resources. It's working but it's not fast & it consumes a lot of memory. Is there a better solution?
As long as you are using GLSurfaceView , dont worry about deletion of OpenGL resources. Everytime user clicks home button / back button the EGL context and all the resources associated with that context (textures,VBOs,shaders) will be deleted. So it is your responsibility to create them in onSurfaceCreated() method.
Moreover I dont think deleting resources in onDestroy() will work because there wont be a valid EGL context available there.

Andengine get pixels from RenderTexture when game pauses

Here is the situation, I need to save the pixels from a RenderTexture and store them into a Bitmap when the game activity pauses (home button was pressed), and restore the render texture with the Bitmap that was saved.
Currently I'm doing the following:
In onPause register a Draw Handler to the engine that calls getBtimap() on the render texture, then call RenderSurfaceView.reqestRender() method, to force redraw and then Thread sleep for 1 second, to ensure that drawing will happen, and then finally call super.onPause().
I think this is not a good method :).
The problem is that RenderTexture.getBitmap() have to be called in the render thread because is calling glReadPixels() which needs a GL context to be executed properly.
So is there anyway to get notified before the GL context is destroyed ?
Or any suggestions on how this could be done in a different way ?

System.gc() causing slowdown from the second start of Activity

I am experiencing a very strange phenomenon (test device: HTC Desire HD, Android 2.3.5). I know that System.gc() is needless and discouraged, and I don't try to suggest otherwise, but the point is that it shouldn't cause issues either (i.e. it should be useless at most).
I have an application which contains a GLSurfaceView in its view hierarchy. The GLSurfaceView is instantiated and added in the Activity.onCreate(). Normally, the application works like this:
User starts the app and goes to mainmenu
User chooses a mainmenu item which sets the GLSurfaceView to View.VISIBLE
User plays with the in-built game on GLSurfaceView
User goes to mainmenu and exits the activity (=> Activity.finish() is called)
My Activity.onPause() looks like this:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
So far so good, everything works fine. However, issues appear after I add the following code to onPause() (for the case when the user exits the game from the mainmenu):
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
System.gc();
}
In details: if the Activity is started for the first time (= i.e. the app process didn't exist before), everything works fine. However, starting from the 2nd start of the activity (= after the first exit from the mainmenu, i.e. after the first Activity.finish()), the framerate of GLSurfaceView is reduced by 40-50%, the in-built game becomes slow.
If I remove the System.gc() call, the problem disappears. Moreover, if I do the following, it also gets rid of the problem:
mGameThread.pause(); // gameThread is my custom thread class for the in-built game
mGLView.onPause(); // pause the renderer thread
if (isFinishing()) {
// 1. get layout root of View hierarchy
// 2. recursively remove (detach) all Views
// 3. call GC
System.gc();
}
I didn't add concrete code because it's complex, so I used comments. If I just detach the GLSurfaceView via removeView(), it is not enough. The entire view hierarchy needs to be cleared.
Note that I couldn't find any memory leaks (no Activity leak via drawables/statics etc.). Moreover, of course, the gameThread properly exits when the app is closed (I just didn't include its source code).
Any ideas, guesses? Apparently, System.gc() seems to cause some issues for the Activity/layout destroying mechanism of Android. Again, as I said, if I remove System.gc(), the problem disappears.
I have experience of Android Game Programming. I used to clear all the view in hierarchy because when running threads if you call System.gc() sometimes it happens that your thread has a reference to some of your view, even if you call system.gc() this view won't get removed and if you keep playing again and again this game you will notice that your heap memory is started growing.
It depends upon the memory leak, if you are leaking some KB memory it will take more time to crash your game. The best way it to use Eclipse Memory Anlyser (Eclipse MAT) and compare your stacks.
Step1:
take memory snap shot when you start your game for first time
Step2:
take memory snap shot when you start your game second time
Step3:
Now compare your both stacks of snapshots it will tell you the difference.
It is a very useful tool. I was having huge memory issues in my game Apache Attack. I fixed them using this awesome tool.
Follow this ECLIPSE MAT TUTORIAL

Opengl-es calling onDrawFrame manually

I am creating a game loop and I need to be able to call onDrawFrame (from inside the renderer manually) in order to "skip frames" if I find that I am falling behind on processes.
Currently I have a GLSurfaceView class that calls
setRenderer(glSurfaceRenderer);
With this set up I understand that onDrawFrame is called every tick.
I tried putting the above call inside a method so that I could call it from inside my game loop but on the second run of the game loop I crash with the message saying
setRenderer has already been called for this instance
Is there a way to call the renderer manually every frame
Will just calling the
onDrawFrame
method work properly. Or is it not good practice to control the renderer in such a way when using openGL
I don't see how calling setRenderer() nor manually calling onDrawFrame() in the game loop will solve anything. You should refactor your rendering calls to take into account the time it takes draw so in other words time based animation. In any case manually calling onDrawFrame() without a call to eglMakeCurrent() before any drawing calls won't work as you have both the renderer and game loop thread drawing.
Edit: Since you are using GLSurfaceView, you can call requestRender() from your game loop to trigger a render if you feel RENDERMODE_CONTINUOUSLY is too slow for you.

Categories

Resources