I wanted to save battery life time. My app only needs to be drawn sometimes. So I added this code to my Renderer in the onDraw method:
boolean dirty = true; public void onDrawFrame(GL10 arg0) { if (!dirty) return; dirty = false; ..... draw images .... }
So my app only gets drawn when I want it. But what happens is that if I dont draw my app on every frame it flickers really fast. It looks like it will be drawn every 2. frame or so and in all the other frames only a black screen will be drawn.
I know that i could set the render mode to RENDERMODE_WHEN_DIRTY
. But I dont want to create another thread to check if its dirty or not.
My question is why does it flicker? i dont call any methods or GLES20 calls before i do my check: if (!dirty) return;
and i am sure that the boolean dirty
doesnt change and is always false except for the first frame.
EDIT:
I changed my code to this:
int dirty = 0; public void onDrawFrame(GL10 arg0) { if (dirty > 1) return; dirty++; ..... draw images .... }
This stops the flickering! Looks like you have to draw atleast 2 times so you dont have this wierd screen flickering. Anyways I will now try to use the more clean way and create a thread that calls requestRender()
when i want to draw something and set my render mode to RENDERMODE_WHEN_DIRTY
When onDrawFrame()
is called, it's too late to decide that you don't want to draw a frame. Whatever you draw in that method will be presented. If you don't draw anything, whatever happened to be in the render target will be presented. There's no guarantee about what that is. It could be an older frame, or random garbage.
The workaround you found of rendering the frame at least twice may work for your current system, but I wouldn't count on it working everywhere. It will also waste battery power. While you skip the actual rendering, the frames will still be presented.
The best solution is really that you use RENDERMODE_WHEN_DIRTY
, and trigger updates when you actually need them. I'm not sure why you would need an extra thread for that. At some point in your program logic, you will change data/state that requires a redraw. You can directly call requestRender()
when that happens, instead of setting a flag that has to be checked by another thread.
Another option is that you call:
eglSurfaceAttrib(display, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED);
while setting up the context/surface. This requests that the buffer content is preserved after eglSwapBuffers()
. It comes with a big caveat, though: This is not supported on all devices. You can test if it's supported with:
EGLint surfType = 0; eglGetConfigAttrib(display, config, EGL_SURFACE_TYPE, &surfType); if (surfType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) { // supported! }
You can also request this functionality as part of choosing the config. As part of the attributes passed to eglChooseConfig()
, add:
... EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_SWAP_BEHAVIOR_PRESERVED_BIT, ...
But again, this is not supported on all devices. So it's only really an option if you're targeting specific devices, or have a functional fallback if it's not supported.
I would guess that windowing system will still assume you've updated the frame, and put the buffer to screen - i.e. how is it supposed to know that you've not drawn anything? What ends up on screen will be whatever happens to be in that memory buffer (typically the frame from N-2 frames ago).
Use a separate thread to sleep the rendering until the state changes to dirty, so you only send frames where you have actually rendered something to the OS. This way you not only save GPU load, you also stop the CPU thread spinning around wasting power doing nothing polling the dirty state.