Friday, 3 August 2012

Android development, SDL2 and crashes

Two more learning experiences with Android & SDL 2.0.

Crashes in native code

When your native code crashes, you get a PS3-style stacktrace with mystifying anonymous addresses representing each level of the call stack.  This is important because Eclipse cannot debug native code, it just exits debugging.  Much the same as on the PS3, you can run some tool on this stacktrace and get a readable form with file names and line numbers.  But you have to do it line by line manually, feeding addresses and static library paths into the tool.

This is a raw crash from the logcat output (with some duplicate lines trimmed):

08-01 19:36:56.899: I/DEBUG(31): Build fingerprint: 'generic/sdk/generic:2.3.3/GRI34/101070:eng/test-keys'
08-01 19:36:56.899: I/DEBUG(31): pid: 495, tid: 503  >>> org.disinterest.broguex <<<
08-01 19:36:56.899: I/DEBUG(31): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000020
08-01 19:36:56.899: I/DEBUG(31):  r0 00000000  r1 4419ac10  r2 ac411085  r3 00000001
08-01 19:36:56.899: I/DEBUG(31):  r4 0029e528  r5 002bdf60  r6 00000001  r7 43f37f3c
08-01 19:36:56.899: I/DEBUG(31):  r8 4419ac70  r9 43f37f34  10 00000000  fp 419507e8
08-01 19:36:56.899: I/DEBUG(31):  ip 441baf00  sp 4419ac10  lr ac411c93  pc ac411094  cpsr 00000030
08-01 19:36:57.139: I/DEBUG(31):          #00  pc 00011094  /system/lib/egl/libGLES_android.so
08-01 19:36:57.139: I/DEBUG(31):          #01  pc 00011c90  /system/lib/egl/libGLES_android.so
08-01 19:36:57.150: I/DEBUG(31):          #02  pc 00003844  /system/lib/libEGL.so
08-01 19:36:57.150: I/DEBUG(31):          #03  pc 00033004  /system/lib/libandroid_runtime.so
08-01 19:36:57.159: I/DEBUG(31):          #04  pc 00017d74  /system/lib/libdvm.so
08-01 19:36:57.159: I/DEBUG(31):          #05  pc 00048f54  /system/lib/libdvm.so
...
08-01 19:36:57.199: I/DEBUG(31):          #19  pc 000a3518  /data/data/org.disinterest.broguex/lib/libSDL2.so
08-01 19:36:57.199: I/DEBUG(31):          #20  pc 000a348e  /data/data/org.disinterest.broguex/lib/libSDL2.so
08-01 19:36:57.199: I/DEBUG(31):          #21  pc 0003142a  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.199: I/DEBUG(31):          #22  pc 00032e8e  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.209: I/DEBUG(31):          #23  pc 000112be  /data/data/org.disinterest.broguex/lib/libtcod.so
08-01 19:36:57.209: I/DEBUG(31):          #24  pc 0008e066  /data/data/org.disinterest.broguex/lib/libmain.so
...
08-01 19:36:57.219: I/DEBUG(31):          #31  pc 00029582  /data/data/org.disinterest.broguex/lib/libmain.so
08-01 19:36:57.219: I/DEBUG(31): code around pc:

Feeding each line into the tool is somewhat tedious and error prone.  Fortunately Ben van Daele has written a Python script to parse this raw data into a proper call stack.  With a few modifications, it even works on Windows and gives the output similar to the following:
d:/Code/android-ndk-r8/platforms/android-14/arch-arm/usr/include/jni.h:793:_JNIEnv::CallStaticVoidMethod(_jclass*, _jmet
hodID*, ...)
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/core/android/SDL_android.cpp:273:Android_JNI_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/video/android/SDL_androidgl.c:108:Android_GL_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/video/SDL_video.c:2592:SDL_GL_SwapWindow
D:\Code\games\Brogue-v1.6.4-Android/jni/sdl2/src/render/opengles/SDL_render_gles.c:1062:GLES_RenderPresent
...
D:\Code\games\Brogue-v1.6.4-Android/jni/brogue/BrogueCode/RogueMain.c:38:executeEvent
D:\Code\games\Brogue-v1.6.4-Android/jni/brogue/BrogueCode/IO.c:637:mainInputLoop

My DOS window compatible version of the script, is available here.

Android rendering crashes

There's a page on the SDL wiki which says (or said, being unable to find it now) that as the SDL_APPACTIVE app state no longer exists, you should instead check the SDL_WINDOW_SHOWN window flag.  Unfortunately, on Android at least this is set even when the SDL_WINDOW_MINIMIZED flag is set.  Which might be when you hit the home button.  And if you try and render while your window is minimised, crashes ensue due to an invalid surface.

Something like this will precede one of those anonymous stacktraces:
09-20 01:59:06.702: ERROR/Surface(20116): surface (identity=...) is invalid, err=-19 (No such device)
This is what I use to check that it is okay to engage in normal rendering-related logic:
#if SDL_VERSION_ATLEAST(2,0,0) && defined(__ANDROID__)
 /* Abort if the window no longer has focus.  Otherwise a later draw operation will crash the app. */
 if ((SDL_GetWindowFlags((SDL_Window*)TCOD_sys_get_sdl_window()) & (SDL_WINDOW_SHOWN | SDL_WINDOW_MINIMIZED)) != SDL_WINDOW_SHOWN)
  return true;
#endif
I still need to properly handle pausing, and of course the special kind of pausing where the application gets killed and needs to persist state. But it's enough for now not to crash if the application is paused and resumed.

Supporting all Android architectures

Charilaos Kalogirou's post about porting your game from iOS to Android, recommends not just building code for the default ARMv5TE architecture, but all supported architectures.  To do this, in your "jni/" directory add the file "Application.mk" if it doesn't already exist.  And within this add the line "APP_ABI := all".  This will build all architectures, which according to the NDK documentation is ARMv5TE, ARMv7-A, x86 and MIPS.

You don't need to do anything else.  The presence of the binaries for the different architectures just works, and the Play store will now offer the app to those whose device was previously unsupported.  It did double the APK size of 1.1M to 2.2M, with MIPS architecture excluded for SDL-specific reasons.


The Play store does allow uploading multiple APK files, and filtering which of these is offered to different potential recipients, based on their differences.  But none of the filters allows you to upload a different APK per-architecture.

Getting more familiar with SDL 2.0

I'm a little confused by SDL 2.0.  On one hand, it has made getting libtcod working on Android very easy.  On the other hand, there is a range of functionality that was present in 1.2, which is.. vaguely alluded to as no longer being considered part of SDL and up to any end user to reimplement themselves.

One of these is key repeat rates.  SDL 1.2 had an API that let you customise this.  SDL 2.0 has a mailing list post suggesting that any end user wanting to modify it, should use the OS facilities.  Android does not have any OS facility to modify this.

Another of these is mapping key events to the character on the key the user actually pressed.  What I get as a developer when I press "~", is the keycode for "`" with a modifier indicating a shift key was being pressed at the time.  I get this on Windows, and I get this on Android.  SDL 1.2 had the unicode field, which was populated with the correct character through platform-specific logic that queried the keyboard mapping or code page, or whatever.  SDL 2.0 has the unicode field, but it never gets a value set to it, so remains 0.  It however has a comment indicating it is obsolete and that the text input API should be used.  Now, the text input API is a completely different non-key press based approach.  If you're writing a text adventure, then it's probably what you're looking for.  And if you are writing an arcade game, then the current SDL key press support is probably okay.  But if you want to have more than the limited SDL key press support, you need to (on Android at least) completely bypass the SDL event system and it's KeyEvent.  This is easy to do, but it feels like SDL is incomplete and that you're violating proper procedure.

No comments:

Post a Comment