Author Topic: Android-SDL-Mupen64Plus Lifecycle Mechanics  (Read 2614 times)

Offline littleguy

  • Moderator
  • double
  • *****
  • Posts: 1945
    • View Profile
Android-SDL-Mupen64Plus Lifecycle Mechanics
« on: December 03, 2014, 08:47:06 AM »
I just wanted to get this discussion all in one place for posterity.  It's a complicated design problem with many subtleties.

Last year, for version 3.0 I refactored all the lifecycle mechanics.  The goal was to make the GameActivity robust to the lifecycle that the Android O/S imposes on all apps:

Code: [Select]
(start)
    |
 onCreate <-- (killed) <---------\
    |                            |
 onStart  <-- onRestart <-----\  |
    |                         |  |
 onResume <----------------\  |  |
    |                      |  |  |
 [*onSurfaceCreated*]      |  |  |
    |                      |  |  |
 [*onSurfaceChanged*]      |  |  |
    |                      |  |  |
 [*onWindowFocusChanged*]  |  |  |
    |                      |  |  |
 (running)                 |  |  |
    |                      |  |  |
 [*onWindowFocusChanged*]  |  |  |
    |                      |  |  |
 onPause ------------------/  |  |
    |                         |  |
 [*onSurfaceDestroyed*]       |  |
    |                         |  |
 onStop ----------------------/--/
    |
 onDestroy
    |
 (end)
 
[*non-deterministic sequence*]

The design philosophy that I took was this:
  • First and foremost, we must acknowledge that GameActivity will always be created, started, resumed, paused, stopped, and destroyed on Android's whim.  Whether we like it or not, Android -- not the mupen64plus core or SDL -- is the true dictator here.
  • The Java code in GameActivity should explicitly handle all of Android's lifecycle callbacks.  I.e. follow the "Android way" and be a good Android citizen.
  • The Java callbacks are responsible for keeping mupen64plus-core's state compatible with GameActivity's Android state.  E.g. when the GameActivity stops, the core should be stopped.
  • Mupen64plus-core's state should be managed using the native mupen64plus API, not the SDL API.  I.e. follow the "mupen64plus way" and be a good mupen64plus front-end.  E.g. don't stop the core by issuing an SDL call; use the mupen64plus API instead.
  • The only interaction we should have with SDL is to implement SDL's callbacks.  These are: createGLContext, destroyGLContext, flipGLBuffers, audioInit, audioWrite*Buffer, and audioQuit.
  • We have the option of implementing SDL callbacks in Java or C++.  Since GL state management must also be coordinated with the GameActivity lifecycle and the Java SurfaceView element, I chose to implement the callbacks primarily in Java.
  • The Java SurfaceView has its own lifecycle (create, change, focus, destroy) that interleaves with the GameActivity lifecycle.  Unfortunately, this interleaving varies depending on the Android version, so we cannot assume any lock-step ordering between the lifecycles.
  • For robustness, we should assume the Java surface lifecycle is completely independent of the Java activity lifecycle.  This means that when handling surface lifecycle callbacks, we should condition our response on the activity's current state.  When handling activity lifecycle callbacks, we should condition our response on the surface's current state.
One extra complication is that the mupen64plus native code (particularly ui-console) contains a number of static variables.  On the PC these would normally be reset on launch, as the ui-console process would be completely reloaded, triggering a reload of the other mupen64plus modules.  However, we are using ui-console as a library rather than an executable, so the core modules are only loaded once when the Android app starts.  I.e. the ui-console isn't reloaded automatically when GameActivity starts.  Therefore we must manually force ui-console to reload whenever the GameActivity is created.  Due to vagaries in how JNI loads and (only sometimes unloads) native modules, this requires careful structuring of the module dependencies and careful (un)loading at run-time.

The process of reimplementing the lifecycle mechanics was a bumpy road, as can be seen in this thread:
http://www.paulscode.com/forum/index.php?topic=1199.0

and these commits:
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/3cc146ef37cc26a3f6298ea094a79d3f6c667274
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/f65585de2b27c545412a81b7436960000596ab44
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/d01ca2c4d34774ea061e0f57431e5604afe4fca4
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/49acad8648ed78258caa5f2db2af8ce6f0935df2
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/dcdb3cf65f337e88aeb202247e1761e02e94ca27
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/59ba2bc8f425a2a5c3b9d34e2bfbfa47b83ba870
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/6d0973941f22c498042782f94934cdb9b661a352
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/2ff6c50bb26737fd62f9af9d812bbfd03b21edaf
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/2e4f3c70c477a1ac96745d061f141af2066d8a97

@Gilles With all this in mind, could you please (re)explain the purpose of the changes you made in this branch?
https://github.com/mupen64plus-ae/mupen64plus-ae/commits/Gillou68310/sdl-bridge-front-mods
Which of these changes should be integrated into the master branch?  Which should not?  What bugs do these fix?
2012 Nexus 7, rooted stock Lollipop
Samsung Galaxy Victory, rooted stock Jelly Bean
Xperia PLAY, stock Gingerbread
OUYA, retail version

Offline Gillou68310

  • Developer
  • long
  • *****
  • Posts: 112
    • View Profile
Re: Android-SDL-Mupen64Plus Lifecycle Mechanics
« Reply #1 on: December 03, 2014, 09:54:27 AM »
The purpose of the 3 following commits are to avoid shutting down the core when the app is put in background. This is definitely not the best way to do it because SDL is responsible for recreating the EGL context. Also it has been proven to fail on some devices. Still would be a nice feature to have.
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/4842730a79eb40f597c0541ac0800c939eaa05ca
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/d78ac2e6e582d0cccccfb33c868439bf1c4177e2
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/7dda9b121ecee9cce23bb14fa49d1ca0e336b6d3


This commit fixed crashes happening when launching and exiting games a lot of times without exiting the app. This is caused by the "pthread_key_create" function called without ever calling "pthread_key_delete". This is equivalent as calling a malloc without a free, except that the limit is PTHREAD_KEYS_MAX not the free memory left. ;)
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/398f940e441a68f68372e5fb8993ec677c9302a1

This one is just to avoid exiting because of a bad finger slide.
https://github.com/mupen64plus-ae/mupen64plus-ae/commit/9453eea0e5f7bf3d8ec37028a5b16b5b0421d1cf

Offline littleguy

  • Moderator
  • double
  • *****
  • Posts: 1945
    • View Profile
Re: Android-SDL-Mupen64Plus Lifecycle Mechanics
« Reply #2 on: December 03, 2014, 10:38:59 AM »
Thanks much for the clarity.  I was concerned about the first three since they involve so many changes.  So we'll just treat that as your personal experimental stuff, and in the future just look for a reliable (and hopefully simple) way of adding the feature to master.

The last two make sense in the big picture.  I'll take a closer look and work on integrating them.  I might propose a few tweaks after looking at it closely.

Thanks! :D
2012 Nexus 7, rooted stock Lollipop
Samsung Galaxy Victory, rooted stock Jelly Bean
Xperia PLAY, stock Gingerbread
OUYA, retail version

Offline retroben

  • float
  • ****
  • Posts: 432
    • View Profile
Re: Android-SDL-Mupen64Plus Lifecycle Mechanics
« Reply #3 on: December 03, 2014, 12:19:30 PM »
Maybe this will fix the graphic issue when switching plugins.
I had the text missing in SM64's save menu one time when switching to Glide64 because Rice is less fast for some reason.
Other games did something similar once or twice,but exiting and restarting usually fixes it.