APP_CMD_PAUSE in native_app_glue

704 views
Skip to first unread message

Brian

unread,
Oct 12, 2011, 11:16:58 AM10/12/11
to android-ndk
When using android_native_app_glue I have found the APP_CMD_PAUSE does
not seem to be handled properly. In the app_glue a second thread
handles the commands and a mutex/condvar syncs the 2 threads. When a
command comes in this is run:

android_app_pre_exec_cmd(app, cmd);
if (app->onAppCmd != NULL) app->onAppCmd(app, cmd);
android_app_post_exec_cmd(app, cmd);

In the case of the APP_CMD_PAUSE the pre_exec signals the condvar and
the main thread continues before my activities onAppCmd function gets
called. This does not give me a chance to handle the PAUSE. I have
found that once a PAUSE comes in you can not call eglSwapBuffers
without crashing so I need to handle the PAUSE before giving control
back to the main thread. I have changed the PAUSE so that the condvar
is not signalled until the post_exec and things work as expected.

Has anyone encountered this before?

Brian

Crank Software Inc.
Message has been deleted

Ratamovic

unread,
Oct 13, 2011, 9:47:23 AM10/13/11
to android-ndk
Hi,

I never noticed such a thing. I am not sure about it but I think I was
calling a last glClear() and eglSwapBuffers() and that was working.

Nick I am surprised by what you say. As far as I know you need to
return from android_main() only when the flag destroyRequested is set.
If your activity is just resumed, it shouldn't be recreated (your
native thread too).
I stop any "processing" when onPause() occur and only poll events
(setting flag -1 on pollAll() to make sure thread does not work
uselessly).




On 13 oct, 13:11, Nick <nicolas.jincher...@gmail.com> wrote:
> I was having this issue before too. The cause in my case is that I was
> overlooking the fact that android expects android_main() to return
> completely each time you pause/resume. So if you don't return, you
> will actually end up with two copies of your app running, and when the
> second one finally gets to eglSwapBuffers(), it will deadlock.
>
> As a test, you could put log prints for every CMD in your app, and
> also output the android_app pointer as an unsigned int. Then, you will
> be able to see how you end up with two apps running, receiving
> interlaced CMDs from both apps in the same handler. Or better yet, use
> the san-angeles example for this test, which already handles this case
> properly.
>
> Nick
Message has been deleted

Brian Edmond

unread,
Oct 13, 2011, 6:35:00 PM10/13/11
to andro...@googlegroups.com
I agree that pause/resume just tell you to stop rendering and you only need to return from main when APP_CMD_DESTROY is called.  My understanding from debugging the code is this:

AP_CMD_INIT_WINDOW:  you can start rendering and using the window in your eglWindow
APP_CMD_PAUSE:  you should stop rendering immediately and only return from the onPause callback once stopped otherwise the eglSwapBuffers will crash
APP_CMD_RESUME:  start rendering again
APP_CMD_TERM_WINDOW:  the eglContext is no longer valid (usually called after a pause)
APP_CMD_DESTROY:  your app thread should exit, however it may be started again without the actual process restarting.

The issue I had was that you must stop rendering before returning from onPause however in the native glue code they return from that callback before calling the apps input handler.  I have all of this working now with the change to onPause.  I was curious is others had the same issue when rendering in a second async thread.

Brian

On Thu, Oct 13, 2011 at 10:40 AM, Nick <nicolas.j...@gmail.com> wrote:
I am aware of the that...but the OP was saying that his app was
crashing on eglSwapBuffers(), and in my experience, that can happen
when the app sets the destroyRequested flag to request destruction,
and you don't destroy your app.


On Oct 13, 9:47 am, Ratamovic <ratamo...@gmail.com> wrote:
 As far as I know you need to
> return from android_main() only when the flag destroyRequested is set.

>
--
You received this message because you are subscribed to the Google Groups "android-ndk" group.
To post to this group, send email to andro...@googlegroups.com.
To unsubscribe from this group, send email to android-ndk...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/android-ndk?hl=en.


Ratamovic

unread,
Oct 14, 2011, 11:31:11 AM10/14/11
to android-ndk
Hi Brian,

Indeed I confirm what you say if I try to render in onPause() except
that on my HTC, it emits (only sometimes because of concurrency I
guess) an error log but it doesn't crash.
I was confused because when I made some tests a while ago, I thought
Activity could not leave while onPause() was not processed whereas in
fact it was blocking on an Input event (the back button!). And while
it was not processed, indeed application could not leave even if an
onPause is waiting behind :)...

However in my final application code, I don't have such a problem or
warning because I never draw after an onPause() occur in the native
thread. In that case I just stop OpenGL and releases resources
immediately.
I am curious to know why do you need to draw when onPause happen?

Christopher Van Kirk

unread,
Oct 14, 2011, 12:58:40 PM10/14/11
to andro...@googlegroups.com
What is the equivalent event handler for APP_CMD_TERM_WINDOW in the Java
activity framework? Is it onStop?

Brian Edmond

unread,
Oct 14, 2011, 1:11:31 PM10/14/11
to andro...@googlegroups.com
I do not need to draw after onPause but there is a syncronization issue.  Here is my senerio:

There are 3 threads:
GLUE: This is the main thread in app_glue
MAIN: This is the thread android_main runs in (handles the looper) started by GLUE thread
RENDER: This is my render thread which is started by MAIN

Here is what happens:
 My RENDER thread is carrying along drawing ...
 GLUE thread gets a onPause and send the APP_CMD_PAUSE to MAIN and returns (this is the problem, because it returns and the rest of the framework   can start destroying things)
 MAIN thread gets the APP_CMD_PAUSE and signals the RENDER thread to stop drawing and waits for it to complete, however based on timing this may be too late since the framework is doing things in the other threads.

So what happens is that the render thread is in the middle of a render cycle when pause happens but does not get told to stop until it is too late.  So my change to the order in the native_app_glue causes it to wait on the MAIN thread.

Brian

Christopher Van Kirk

unread,
Oct 14, 2011, 1:20:56 PM10/14/11
to andro...@googlegroups.com
Can you join the render thread and wait for it to finish in the onPause?

Brian Edmond

unread,
Oct 14, 2011, 1:27:31 PM10/14/11
to andro...@googlegroups.com
Yes I can and actually that is what my MAIN thread does, however by using the native_app_glue library you have another thread in the mix which it creates internally and gets the onPause.  So the sync on the threads needs to happen in the onPause callback which is in the native_app_glue code and not my app.  The thread in native_app_glue handles all the callbacks which you never see unless you edit that code (which I did).

I have it all working my mail was mainly to tell others of this or see if there was some life cycle in Android which I did not understand.

brian

Ratamovic

unread,
Oct 14, 2011, 2:04:15 PM10/14/11
to android-ndk
Okay! there is a third one... I understand now. Indeed I think native
glue was designed with rendering happening on the MAIN thread.
So if one wants to use the "original" native app glue only, one
solution coulg be to render and loop over events on the same thread
MAIN and everything else in RENDER (which would need another name in
that case :)).

Thanks a lot for posting. That helped me understanding native glue
better.

Nicholas Panayis

unread,
Oct 14, 2011, 10:12:32 AM10/14/11
to andro...@googlegroups.com
Hi,

Im also struggling with the 'home' button. When I bring my app back into focus I get a black window :(
I see it destroying/creating window/surface/context

Not sure where to look

(another Nick :)

Brian Edmond

unread,
Oct 14, 2011, 5:47:02 PM10/14/11
to andro...@googlegroups.com
So I get confused on the buttons but when you hit the button and the far left your app thread actually exits but the app remains running.  For all intensive purposes you need to restart your app from scratch.  When you hit the button to the right of that one your app pauses.  What I have found is this:

APP_CMD_PAUSE:  stop all drawing
APP_CMD_TERM_WINDOW:  release all GL resources, call eglTerminate 
APP_CMD_INIT_WINDOW: re-initialize the rendering (eglGetDisplay, window surface, ...) then draw a frame and swap
APP_CMD_RESUME: start drawing again, but keep in mind you need to draw at least one frame after this with a eglSwapBuffers or things won't show up

I hope this helps

Brian

Crank Software Inc.

Ratamovic

unread,
Oct 17, 2011, 5:08:05 AM10/17/11
to android-ndk
An interesting document on the subject:
http://developer.download.nvidia.com/assets/mobile/files/AndroidLifecycleAppNote_v100.pdf

On 14 oct, 23:47, Brian Edmond <bedm...@gmail.com> wrote:
> So I get confused on the buttons but when you hit the button and the far
> left your app thread actually exits but the app remains running.  For all
> intensive purposes you need to restart your app from scratch.  When you hit
> the button to the right of that one your app pauses.  What I have found is
> this:
>
> APP_CMD_PAUSE:  stop all drawing
> APP_CMD_TERM_WINDOW:  release all GL resources, call eglTerminate
> APP_CMD_INIT_WINDOW: re-initialize the rendering (eglGetDisplay, window
> surface, ...) then draw a frame and swap
> APP_CMD_RESUME: start drawing again, but keep in mind you need to draw at
> least one frame after this with a eglSwapBuffers or things won't show up
>
> I hope this helps
>
> Brian
>
> Crank Software Inc.
>
Message has been deleted

Brian Edmond

unread,
Oct 17, 2011, 10:13:09 AM10/17/11
to andro...@googlegroups.com
I would disagree.  I would and do use separate threads to handle events, blocking waiting for an event to happen instead of polling something on an interval.  The looper is a blocking event thread in my application.  My real app is the render thread which handles internal events for animations, timers and so on and renders based on these events.  I consider the android_main thread with the looper the Android life cycle thread which does not have a lot to do with the overall function of my application.  In my case I have an existing framework and application which runs on many other systems.  For Android this runs as what I have described as my RENDER thread.

The native_app_glue actually does as I describe (for APP_CMD_PAUSE) for other events such as TERM_WINDOW, hence the need the lib for the per and post command processing, so I assume the differences in PAUSE is an oversight.

Cheers,
Brian

On Mon, Oct 17, 2011 at 9:44 AM, Nick <nicolas.j...@gmail.com> wrote:
I would encourage you to use Ratamovic's suggestion and just put the
rendering in the main thread. I think you are causing yourself a lot
of headaches for nothing. Threading is better suited for blocking or
long running tasks, like path-finding, planning that may takes several
frames to complete, or continuously playing a song in the background
that won't likely be interrupted until it has finished playing. When
threading, it's usually best to stay away from "Race Conditions"
completely. Trying to offload the rendering of a constantly changing
environment to a separate thread is usually a good way of creating
one. Such a thing would make more sense when decoding video, as the
content is not constantly changing. When two threads are constantly
racing along head to head, keeping them in sync is usually much more
trouble then it's worth, and you would probably be better off just
dealing with the limitations of your device.

@Nicholas
When you push the home button, all of your graphics memory is
destroyed automatically by the operating system. This means that the
next time your app is shown to the user, you will have to completely
reinitialize everything, including the opengl context, all textures,
all shaders, all vertex buffers, all frame buffers, ect. If you have
done that without errors, and are still getting a black screen, your
app could be freezing at eglSwapBuffers, as described in my first
post, because you have not properly responded to the operating
system's request to destroy the previous instance of your app.


On Oct 17, 5:08 am, Ratamovic <ratamo...@gmail.com> wrote:
> An interesting document on the subject:http://developer.download.nvidia.com/assets/mobile/files/AndroidLifec...
Reply all
Reply to author
Forward
Message has been deleted
0 new messages