I'm working on an Android OpenGL ES 2.0 program coded natively. In my Activity's onPause method I call the GLSurfaceView's onPause method, then call a native function which glDeletes all textures and shader programs. In the Activity's onResume method I call the GLSurfaceView's onResume method then call a native function which recreates all of the textures and shader programs. The GLSurfaceView methods I mentioned are not overridden. The textures seem to recreate without hassle (no glGetErrors after each call) but glCreateProgram is giving me zero and glGetProgramiv(program, GL_LINK_STATUS, &linkResults) crashes the app. This happens only with onPause/onResume, before then the shaders are created without a problem. Any insight you could offer would be greatly appreciated.
Related
I'm successfully drawing simple shapes on my GLSurfaceView using OpenGL ES 2.0. So far i'm not using any textures. The problem is that when i re-run for the second time the activity with GLSurfaceView the screen is empty (black), shapes are not being drawn again. I have to reinstall the whole app to make it work again.
I don't have any special methods onPause & onResume so i assume the new instance of GLSurfaceView should redraw the screen each time i re-run the application.
Does anyone have any clue what am i missing here?
I don't have any special methods onPause & onResume
That's likely your problem. You need to follow the documentation of GLSurfaceView, specifically what it says under "Activity Life-cycle":
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.
What is different between this:
queueEvent(new Runnable(){
#Override
public void run() {
mRenderer.method();
}});
And this:
mRenderer.method();
And what is better for OpenGL FPS?
GLSurfaceView creates a separate rendering thread. In OpenGL, you need a current context for making any OpenGL calls. The "current context" state is per thread. GLSurfaceView creates an OpenGL context for you, and makes it current for any of the GLSurfaceView.Renderer overrides you implement. So as long as you make OpenGL calls in those methods, you don't have to worry about any of that, it just works like pure magic (well, it's not really magic, but hides a lot of complexity).
Based on this, you can't make OpenGL calls from the UI thread without jumping through hoops. So simply calling a method on the Renderer in something that is e.g. triggered by user input, and then making OpenGL calls in that method, will fail. Beyond that, even if you don't make OpenGL calls in the method, you have to worry about thread safety if the method accesses/modifies member variables of the Renderer that are also used by the rendering thread.
Using queueEvent() provides a convenient way of executing a method in the rendering thread. So you don't have to worry about thread safety of Renderer member variables, because all access will happen in the rendering thread.
I believe you might also be able to make OpenGL calls in that method if you submit it through queueEvent(). But I'm not totally sure if the OpenGL context is always current in the rendering thread, or if that's only guaranteed while the Renderer method overrides are called. It's much more typically to just change state in the Renderer in response to user input, and then use that new state in your override of Renderer.onDrawFrame().
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.
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 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.