Android: check if OpenGL context has been lost - android

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.

Related

How do you efficiently manage texture memory in OpenGLES on Android?

I am loading textures into OpenGLES on Android and maintaining a reference to the generated id in a HashMap.
At a given point in time not all textures that have been loaded will be in use, but may be used at a later point in time, therefore if the device has enough free memory I'd like to keep the textures loaded.
However, if the device starts running low on memory I would like to delete any textures that are not in use since they can always be reloaded later if they're required.
I've tried a few methodologies for handling this scenario.
1. Respond to system memory warnings
If the application receives a memory warning, then it will identify which textures are not in use and schedule for those textures to be deleted.
This method did work reasonably well.
2. Use Soft References
In this approach the application would maintain a List<SoftReference<Texture>> where the Texture class is a wrapper around the id that was returned when a given texture was loaded into OpenGLES.
If a given Texture is not in use at a given point in time then only a SoftReference would exist to this Texture and thus if the garbage collector deemed it necessary it could reclaim this memory and the finalize method on the Texture class would schedule for this texture to be deleted.
This approach seemed ideal based upon the description of SoftReference in the Java documentation, since they would only be reclaimed when more memory was required.
Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches.
However, the Android implementation of SoftReference does not work like this: since Android 9 as the garbage collector is more aggressive and the soft references are reclaimed almost immediately regardless of whether the device is low on memory.
3. Use LruCache
The Android documentation advises against using SoftReference in a cache implementation and to use a LruCache instead. However, the LruCache poses some drawbacks.
Firstly, you have to specifying the maximum size of the cache. It isn’t obvious what to set the cache size to: ideally it’d just automatically be set as high as possible while still being a good citizen. If it’s set too small, it might be constantly reloading textures unnecessarily.
Secondly, a Texture may be removed from the cache which is currently in use and thus may be deleted from OpenGLES and then display as a missing texture to the user.
Is there a better way to maintain a cache of textures in OpenGL and be responsive to low memory scenarios (besides just deleting textures upon memory warnings)?
Talking about best practices, large caches (that can take all the memory) with OpenGL ids for textures are rarely used. Actually it's worth to have only a few textures to render in the scene at one moment. More textures you have in the scene, more texture switches you need to do in frame. It costs. Texture atlases were developed many years ago to reduce number of texture switches. However, atlases still can take a lot of memory.
With development of hardware capabilities and user expectations, texture atlases evolved into virtual/mega/sparse textures + texture data streaming for the cases of high memory usage. The idea is to use virtual memory approach and load/unload blocks of one very large texture in real-time. It has some drawbacks, good discussion about it can be found here. LRU cache can be built on top of it to tell what blocks are required at the moment.
Of course, engines can preload many textures (sparse or usual ones) and unload it after usage, e.g. as a part of dynamic loading of open world in a game. These textures aren't used for rendering simultaneously, and no one waits until they take all the memory to start unloading. The eviction algorithm here is highly dependant on particular app, even though maximum cache size in Mb is widely used practice here.

Where is the best place to delete OpenGL ES program object in Android native code [duplicate]

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.

On android (glsurfaceview) textures are white after MainActivity.finish() and MainActivity.onDestroy()

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 ?

why And-engine is completely blackout on onResume overrided function?

I am using AndEngine GLES 2.0. sometime when I resume my activity there is a complete blackout in the game. Although update and touch events of sprites are working but there is a complete blackout on screen. what is the problem?
AndEngine assumes the EGL context was invalidated, and thus must reload all your resources. This typically takes a non-negligible amount of time, causing a black screen.
Efforts are underway to eliminate this problem on Android 3.0+ where possible, but it will likely always affect Android 2.x
My problem is solved now. Blackout is due to the memory management. I was loading engine every time user plays the new Map. One should load engine for only one time and use Layers.

How to prevent GLSurfaceView from recreating the the OpenGL surface frequently (on Android)?

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.

Categories

Resources