The documentation for the setPreserveEGLContextOnPause Android function states the following:
"...If set to true, then the EGL context may be preserved when the GLSurfaceView is paused..."
How do I check if the EGLContext was preserved or not upon the resumption of my activity?
If the EGLContext was not preserved I need to reload all of my textures, that is why it is important to me.
The only thing I was able to find that I though might be related to this question was the getPreserveEGLContextOnPause() method. But this method only returns true if the EGLContext will be saved, and even if it is saved it may be deleted by the system later, so this doesn't really seem to help.
According to this text, you don't need to. The context loss is implicit and cannot be observed. When onSurfaceCreated() is called, you know the context (has been lost|was never created) and must be recreated.
Alternatively, a possible workaround is to create some small object that is actually never used, but indicates that the context was not lost in the meantime. I'm not sure if this will work, though.
Related
I have a GLSurfaceView renderer and method onSurfaceCreated is called only once (basically only when activity is created or re-created).
I need to test behavior when EGL context is lost and surface is recreated during normal activity life cycle, however I am unable to put app into such situation no matter what I do.
Is there anything that I can do to simulate situation in which is onSurfaceCreated called again? How to make my app lose EGL context?
Edit: After a few days I noticed that the problem was gone and the app started to behave properly as said in specs:
public abstract void onSurfaceCreated (GL10 gl, EGLConfig config)
Called when the surface is created or recreated.
Called when the rendering thread starts and whenever the EGL context is lost.
The EGL context will typically be lost when the Android device awakes
after going to sleep
Before that, every time the device went to sleep, the activity was destroyed and recreated on awake which was preventing me to test if the code in OnSurfaceCreated worked well.
Now it behaves much better when sleep/awake occurs - it usually does not destroy the activity and just calls OnSurfaceCreated method which is the situation I was aiming for.
I'm not sure but the only thing that i can think of is to switch the GLSUrfaceView with a new one using the same GLSurfaceView.Renderer.
That should create a new EGL context and call the onSurfaceCreated() a second time.
According to the documentation, GLSurfaceView.Renderer.onSurfaceCreated is called when the surface is created or recreated.
Called when the rendering thread starts and whenever the EGL context is lost. The EGL context will typically be lost when the Android device awakes after going to sleep.
Have you tried locking your device's screen and unlocking it?
Maybe you can look at onScreenStateChanged method but it's available for API 16 or higher. If you mean "pausing and resuming the activity" by "normal activity life ciycle" maybe you can use onPause or onResume to track this.
What exactly do you want to do with onSurfaceCreated() ? You can force the rendering by requestRender() method of SurfaceView and do the necessary calculations inside onDrawFrame() of GLSurfaceView.Renderer
If your view is in a fragment, you can detach and re-attach the fragment.
I am trying to make my game multitask friendly, however whenever the screen is turned off and turned back on, or the game is navigated away from and back to, I get a null pointer exception pointing to this part of the code:
GLES20.glUniformMatrix3fv(mTextureMatrixHandle, 1, false, render.mTexMatrix, 0);
I don't have anything apart from super.onResume(); and mGLSurfaceView.onResume(); for the onResume() method and the same for onPause(), except with onPause rather than onResume. Any idea how to fix this?
You must make sure that the objects are re-created when the activity is started again. Where are those variables initialized? Typical case for this kind error is when you store objects in a global variables. When the activity is created again, they are null.
So: check which of mTextureMatrixHandle, render, or render.mTexMatrix is null. Re-create it if it is null.
In general, you should not rely on any data being restored at activity creation. So don't use global variables, and rely only on data passed via the intent. If you really want to use global data, you should be aware that it may be reset, and re-create it when needed.
On android, the GLSurfaceView documentation says this:
A GLSurfaceView must be notified when the activity is paused and
resumed. GLSurfaceView clients are required to call onPause() when the
activity pauses and onResume() when the activity resumes. These calls
allow GLSurfaceView to pause and resume the rendering thread, and also
allow GLSurfaceView to release and recreate the OpenGL display.
So I'm supposed to do something like this in my activity:
public void onPause() {
myGlSurfaceView.onPause();
}
public void onResume() {
myGlSurfaceView.onResume();
}
I'm observing in my code that if I don't call onPause() and onResume() then the context is not lost when I press the home button, so I can switch between applications and then go back to my game and everything is working. What I see is that if I close the game using the back button then the screen is black when I open it again, but I can change the back button behaviour to totally close the game and avoid this problem.
So my question is: when is the OpenGL context destroyed? If I don't call onPause() and onResume() can I assume that it will never be destroyed?
EDIT:
I'm targeting Android 2.2, so setPreserveEGLContextOnPause() is not an option to me.
The OpenGL might be lost only after Actvity::onPause() is called, and only in this case. See the setPreserveEGLContextOnPause documentation :
Whether the EGL context is actually preserved or not depends upon whether the Android device that the program is running on can support an arbitrary number of EGL contexts or not. Devices that can only support a limited number of EGL contexts must release the EGL context in order to allow multiple applications to share the GPU.
[...] the EGL context [can be] released when the GLSurfaceView is paused, and recreated when the GLSurfaceView is resumed.
EDIT : The situation described in the documentation is valid on all Android version. Not matter you have access to setPreserveEGLContextOnPause
In my opinion, this is one major drawback is Android OGLES implementation : you can't be certain.
The documentation itself is vague (EGL Context Lost note) :
There are situations where the EGL rendering context will be lost. This typically happens when device wakes up after going to sleep
I noticed the same behavior as you about the Home and Back button. Calls are not exactly the sames (but can't remember them precisely).
The only way to be sure that the OpenGL context is available is to create all OpenGL resources in onSurfaceCreated
Note about setPreserveEGLContextOnPause. Once again, this documentation comment demonstrates the "random" behavior of context destruction :
If set to true, then the EGL context may be preserved when the GLSurfaceView is paused. [...]
setPreserveEGLContextOnPause is an option for you, you just have to implement the GlSurfaceView yourself.
See my answer here to a similar question:
Prevent onPause from trashing OpenGL Context
In order to initialize preferences with default values from XML file describing the preferences, I can call PreferenceManager.setDefaultValues(this, R.xml.preference, false). Sounds simple, but I'm not quite sure when exactly should I call this?
As I understand from the docs, the above call is only needed once, in situation when no preferences are set yet. As a result of this call, preferences residing in /data/data/<myapp>/shared_prefs are going to be set, so all subsequent attempts to read preferences will get me the default values. Logically, setDefaultValues should be called in every single code path that might be executed without preferences being already initialized. Over time, this turned out to be multiple places - main activity, another activity, background service, small BroadcastReceiver handling system messages... Right now I've put call to setDefaultValues in onCreate() for my Application object, as I'm already using it as convenient singleton for other things.
Questions:
Do I have a guarantee that every time my code executes, Application object will be created and onCreate will run?
How are you dealing with this problem? One other way would be to hardcode default values into getFoo(key, defValue) calls, but that effectively scatters your default settings across whole code.
EDIT: Essentially, I don't know which solution is worse: calling setDefaultValues every time I access prefs in given code path, or calling it in some common place (like app's onCreate) every time, no matter whether I need it or not.
I'm going to delete my original answer and answer the questions that you actually asked.
Yes, the Application object's onCreate will be executed at the start of every process. Keep in mind that doesn't guarantee it will be run each time you start your main activity. If Android still has your process running it will use that again (e.g. you still have a service running). So yes what you're doing will work and you're correct in observing it won't blow up.
I'm dealing with this problem by subclassing SharedPreferences (let's call it MyPrefs -- that's not what I call it but that's not important). The key features of MyPrefs are:
encapsulation of get/set methods instead of directly accessing the key names
Handling code for loading defaults. I'm being a little lazy by using a static boolean instead of an AtomicBoolean to tell me if the defaults have been loaded.
Having said that... it works for me, but if you're almost certain you'll be calling the SharedPreferences every time your code runs where you're at works as good as any.
Hope this helps more than my previous answer.
My game is working correctly except in the case where I press the HOME button then resume. What needs to be done to use the textures again? I have tried calling onPause and onResume on the GLSurfaceView (when the activity's onPause and onResume are called).
Any ideas what I could be doing wrong?
If all else fails, reload the textures:
Pseudocode
for tex in textures:
if glIsTexture(tex.opengl_name) == false:
glGenTextures(1, &tex.opengl_name)
glBindTexture(tex.texture_target);
glTexImage(..., texture.image);
Even if you fixed your problem, just to give a bit of explanation that might help others.
Android does not guaranty to keep the OpenGL context alive when the activity is paused.
You have to recreate every OpenGL-resources on resume (texture in you case, but also VBOs etc etc).
Since API 11, you can ask kindly Android to keep the context, but there is no guaranty it would.
After trying:
do not call GLSurfaceView#onPause/onResume in Activity's onPause/onResume
call GLSurfaceView#onPause/onResume, but also set GLSurfaceView#setPreserveEGLContextOnPause(true)
Both of cases fix the HOME-resume-black-texture issue.
Guess Android implementation failed to re-create the EGL context when resume. Since onPause/onResume are required to call, should always set setPreserveEGLContextOnPause to true.