Working on an app that uses a MainActivity->RelativeLayout->GLSurfaceView.
When exiting the app (OK, per contractor requirement) via Activity.finish(), on next launch it will start in its initial state (OK, per contractor requirement) but all textures will be white.
Closing the application via System.exit(0) fixes this, so it's pretty obvious that some form of
OpenGL resource is not being freed.
The textures should all be freed, since there's a manager for them, and none seem to leak.
I'm looking to replace System.exit(0) with something that flushes all OpenGL resources (if possible) ?
Is there something like a context destroyer exposed on Android ?
Related
I am working on an Android app that performs OpenCL/OpenGL interops on the camera perview. I am using GLSurfaceView.Renderer. Naturally the code to create and initialize the OpenCL running environment (from OpenGL) is called from onSurfaceCreated, and the actual processing of each preview frame happens in onDrawFrame.
All work well, except when I am finished, I want to clean up the OpenCL stuff. Ideally an onSurfaceDestroyed method would be the perfect place to clean up, but there is no such method in GLSurfaceView.Renderer. So the cleanup code has nowhere to go, and there is probably memory leak in my app.
Here are my questions:
Why is there no onSurfaceDestroyed method in GLSurfaceView.Renderer? There are onSurfaceCreated and onSurfaceChanged. One would expect onSurfaceDestroyed to be there.
Given the fact that no onSurfaceDestroyed exists in GLSurfaceView.Renderer, where should my cleanup code go, and why?
GLSurfaceView is a collection of helper code that simplifies the use of OpenGL ES with a SurfaceView. You're not required to use it to use GLES, and if you've got a bunch of other stuff going on at the same time I recommend that you don't.
If you compare the complexity of Grafika's "show + capture camera", which uses GLSurfaceView, to "continuous capture", which uses plain SurfaceView, you can see that the latter requires a bunch of extra code to manage EGL and the renderer thread, but it also has fewer hoops to jump through because it doesn't have to fight with GLSurfaceView's EGL and thread management. (Just read the comments at the top of the CameraCaptureActivity class.)
As one of the commenters noted, I suspect there's no "on destroyed" callback because the class aggressively destroys its EGL context, so there's no GLES cleanup needed. It would certainly have been useful for the renderer thread to have an opportunity to clean up non-GLES resources, but it doesn't, so you have to handle that through the Activity lifecycle callbacks. (At one point in development, CameraCaptureActivity handled the camera on the renderer thread, but the lack of a reliable shutdown callback made that difficult.)
Your cleanup code should probably be based on the Activity lifecycle callbacks. Note these are somewhat dissociated from the SurfaceView callbacks. A full explanation can be found in an architecture doc appendix.
I finally found why on some Samsung devices all of the textures are appearing white.
It appears that when the user opens the game, sometimes the textures are not loading and the user has to minimize and restore the game so the onResume() method gets called and load again the textures. My code is just a huge mess so I don't really what to find the root of the problem and fix it (the game is in retirement stage, not worth spending days), I'm thinking of a work around.
So my question is: how can I check if OpenGL has lost context? Is any variable changing its value? Can I check if a specific texture exists in the context?
I know it's losing it when the app goes to the background but in my case this is not the case.
I don't quite get how you are loading your textures. onResume() should be a good place to do so if you disposed them in onPause()... just to free some memory when the user leaves the activity. Anyway, I’ll stick to your question.
In a simple case, you should initialize your textures inside onSurfaceCreated(). Also, don't do things like this:
if (texture != null)
texture = initializeTexture();
because that doesn't mean it's loaded into the OpenGL memory, that's just a reference to a Java object which many times lives longer than the OpenGL context (especially if it's static).
Some offical doc:
http://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer.html
EGL Context Lost
There are situations where the EGL rendering context will be lost.
This typically happens when device wakes up after going to sleep. When
the EGL context is lost, all OpenGL resources (such as textures) that
are associated with that context will be automatically deleted. In
order to keep rendering correctly, a renderer must recreate any lost
resources that it still needs. The onSurfaceCreated(GL10, EGLConfig)
method is a convenient place to do this.
I've used System.exit(0) before to quit my game. But as this is a no-no in Android I tried calling just activity.finish(). Now if I start the game again right after quitting it, all textures are messed up (white, stretched, or otherwise messed up).
I'm using both managed and unmanaged textures in AndEngine. And AndEngine version 1 (so no OpenGL ES 2.0).
What are all the unloading I should do manually before quitting the game to avoid this from happening? What do normally unload with OpenGL based Android games? Any tips and tricks are very welcome.
Well this is a really old question. But my problem really was that I had static references to (actually Scala objects) that would hold on to the textures even after finishing the activity and only killing the process would help. Really bad design. Be careful with your references on Android.
Question:
Considering the hello-gl2 example from Android NDK r6b, is this example correct when user repeatedly invokes pause/resume actions of the GLSurfaceView? I'm asking because each time a GL2JNIView.Renderer.onSurfaceChanged() is called, it creates all OpenGL resources (shaders, textures) but it never destroys them.
Background:
I'm trying to debug some OpenGL ES 2.0 game implemented using C++ and JNI which behaves incorrectly during pause/resume. I was trying to find some reference example for this but I haven't found any except of the mentioned hello2-gl example from NDK, which may IMHO create memory leaks, and this more complex version of it, which I'm currently using, but where it seems like OpenGL context is destroyed before GLSurfaceView finishes with its onPause() method where I'm destroying all the OpengGL shaders and textures bound to the context. Could you point me to some truly correct example of OpenGL ES 2.0 on Android using NDK concerning the correct way of pause/resume OpenGL resource handling?
Thank you for your help.
That's not quite the right lifecycle, but it's close. Any time a GL surface becomes completely obscured its EGL context will be destroyed, and when that happens any resources that you have allocated through OpenGL calls will be freed. As such, there is no need to manually free those resources unless your application requires active management during runtime (e.g. too many textures to cache in memory, etc).
Note that this only applies to resources and memory allocated by OpenGL via OpenGL calls. Any buffers that you allocate outside of OpenGL do need to be freed, as usual.
I have a an app whose Main activity shows a GLSurfaceView. Every time that a new activity is launched, say for example Settings Activity, the OpenGl surface is destroyed, and a new one is created when the user returns to the main activity.
This is VERY slow, as I need to regenerate textures each time so that they can be bound to the new Surface. (Caching the textures is possible, but it would be my second choice, because of limited availability of memory.)
Is there a way to prevent the Surface being recreated every time?
My own analysis by looking at the code is:
There are two triggers for the Surface to be destroyed:
GLSurfaceView.onPause() is called by the activity
The view gets detached from the window
Is there a way to prevent #2 from happening when launching a new activity?
If you're targeting 3.0 or later, take a look at GLSurfaceView.setPreserveEGLContextOnPause(). Keep in mind that devices might still support only one OpenGL context at a time, so if you're switching to another activity that uses OpenGL and back to yours, you will have to reupload anyway – so I'd recommend keeping a cache and dropping it when your Activity's onLowMemory() is called.
Caching the textures is possible, by
it would be my second choice, because
of limited availability of memory
If there's a lot of textures, the only solution is to cache them AFAIK, there's no way to keep the surface and textures alive across activity restart. By the way, you can use a lot more memory in native code than in Java.
There has been a long discussion about this on android-ndk. Especially, I posted some benchmarks, which show that bitmap decoding can take up to 85% of texture loading time, texImage2D taking only the remaining 15%.
So caching decoded image data is likely to be a great performance booster, and by coupling this with memory mapped files (mmap), you may even cache a real lot of image data while staying memory-friendly.
Short answer: No
Long Answer:
You could cache/save all the textures when you get OnPause(), and restore them at OnResume(). But otherwise the android Activity lifecycle demands the view to be restored from scratch onResume()
From activity cycle
If an activity is paused or stopped, the system can drop the activity from memory by either asking it to finish, or simply killing its process. When it is displayed again to the user, it must be completely restarted and restored to its previous state.
One way to get around it thought is that if you are within you own Application, instead of starting a new activity, create a Overlay/Dialog over the current activity and put the SettingsView in there.