The Android Activity and GLSurfaceView from native code

By Zak, February 27, 2010 8:01 am

Recently I had the chance to build a small OpenGL based Activity in Android. This Activity was to be primarily written in C++ with only the most minimal parts left out in Java. I wanted to leverage Android’s GLSurfaceView for managing the OpenGL context, surface and display. Staying in bounds was important. I did not want to have to significantly change my Activity code later on due to the use of undocumented methods. That said, there are some definite difficulties with this approach, not all of which I have solved. I made some compromises but in the end I am satisfied with the result. Here is how I approached the problem and what I learned.

The Android Activity has 7 main methods that the OS calls to manage its lifetime. The Android Developers site describes the basics of the Activity Lifecycle. What they don’t describe is what the OS expects you to do (or not do) during these calls. The first thing I learned was that you should not do anything that takes a long time in any of these methods. Android OS will consider your application hung and terminate it. My solution was to create a separate native thread that did any real work. When the main thread called my activity I would simply queue the request for the worker thread to take care of later. The worker thread mostly just slept on a semaphore. Whenever one of the 7 Activity requests came in it would wake up and handle it. This solution worked well. I could perform long running tasks (like loading a file off the sdcard) without having Android kill my activity.

The next hurdle was to integrate OpenGL via GLSurfaceView into my application. I wanted all my rendering to be done natively. There are 3 methods on the GLSurfaceView.Renderer interface that I stubbed in Java for implementation in C++. The most important thing to realize is that GLSurfaceView creates its own thread for rendering. These 3 interface methods will be called from that thread, not the main activity thread. There is also no way to get at the EGL context and therefore no way to attach that EGL context to the main thread. This is somewhat annoying. I am cool with having the renderer in its own thread, that makes sense. However, I would want to have access to the EGL context from other threads so I could do things like load textures or build shader programs while maintaining a decent frame rate. I have yet to find a solution to this EGL context problem besides not using GLSurfaceView. Maybe that’s a topic for a later post. For now I am trying hard to stay in bounds.

Android suffers from the same lost context problems as programming in DirectX on Windows. Whenever your activity is covered up or hidden Android OS will kill your Renderer, EGL context and all your loaded GL resources. When the Activity is resumed later you are expected to recreate or reload all those resources. The big catch, and I mean big, is that Android doesn’t actually tell the GLSurfaceView.Renderer that the context is being destroyed. It simply tosses the resources and lets all your GL handles become invalid. You do get a clue on the main Activity thread, the onPause method is called around the time that the Renderer is destroyed.

To overcome these problems I had to split my loading code across my worker thread and the GLSurfaceView.Renderer thread. The worker thread loads the vast majority of my resources. It does not load any GL related resources, it simply remembers where on disk they are. Later on, whenever the GLSurfaceView.Renderer starts up, the render thread detects it needs to load some GL resources and does so. This solution works well for both regular Activity startup as well as for the pause/resume cycle.

Ideally, I would have created a third thread that handled loading. Then the renderer thread could have maintained interactive frame rates while GL resources were being loaded. This unfortunately is not possible with the current GLSurfaceView implementation.

I am not sure where I am headed next. These compromises should hold for some time on Android. I have thought about completely tossing GLSurfaceView and implementing my own. That would be a lot of work for not much gain. The current Android provided GLSurfaceView is close to being really cool. All it needs are 2 small features. It needs a way to share the EGL context and attach it to other threads. For example a method called GLSurfaceView.attachContextToCurrentThread could suffice. It also needs to add another method, perhaps GLSurfaceView.Renderer.onSurfaceDestroyed, that signals when the Renderer is being destroyed. Those both seem relatively simple to add and would greatly enhance the ability of Android GL based activities.

Xcode: Why can’t we be friends?

By Zak, February 6, 2010 8:22 am

Spent a solid chunk of hours debugging why Xcode (using GCC 4.2) failed to link my projects this week. For context I am building 5 static libraries and a single application that links against them. The Xcode projects have dual targets in them, one that targets MacOSX 10.6 IA64 and another that targets iPhone 3.1.2 ARM6. I was building my projects for the iPhone target when the linker error occurred. The text is cryptic and tended to move around as I hacked at my source trying better to understand it.

ld: absolute addressing (perhaps -mdynamic-no-pic) used in __isctype(int, unsigned long)from /.../lib.a(file.o) not allowed in slidable image. Use '-read_only_relocs suppress' to enable text relocs

The Internet, searched via Google, does not have a direct answer to this problem. The ‘-mdynamic-no-pic’ linker option is intended for use on PowerPC architecture CPUs. I am building for ARM6 and IA64; so this clearly does not apply to my situation. The second option offered is an old linker flag intended for ld_classic not for ld. Either way, adding ‘-read_only_relocs suppress’ option to the linker has no effect. There are lots of other ‘ideas’ on the Internet on how to fix this but none of them offered the real solution to my problem.

The first clue was that ‘__ctype’ is an inline function provided as part of the standard C libraries. I could poke this function around in my CPP files and thereby cause the linker error to show up in a different object file. Questioning my ability to program, I invoked the help of my buddy at work. He insisted that I must have changed some Xcode project setting that caused this to happen. I did just do a pass over the project settings to clean up and standardize everything. This idea was just what we needed to figure it out. I had been over eager and unchecked a critical option in Xcode’s project settings. Under the ‘GCC 4.2 Language’ section in the build settings you will find:

Enable Linking With Shared Libraries
Enabling this option allows linking with the shared libraries.  This is the default for most product types.  [GCC_LINK_WITH_DYNAMIC_LIBRARIES, -static]

This option should be checked ON. If it is not then static libraries will attempt to pull other static libraries into their archive files. The linker then becomes confused and barfs up the cryptic error. Fixed! Awesome, back to making forward progress. Thanks goes to Jeremy for insisting that I messed up a project setting. This information is now on the Internet for others to find and learn from. Hopefully, it can save someone else some time. What was I working on again?

Android Development Week 1

By Zak, January 29, 2010 10:02 pm

Just wrapped up my first week of Android development. Here is what I discovered and adapted to.

Initially, I tried using Eclipse and the ADT plugin to develop Java code. Eclipse is not so hot. I know any one can contribute to Eclipse, that’s a good thing. What would be even cooler if someone removed a feature from Eclipse. Too complicated. I quickly discarded Eclipse and build a custom project under Xcode that leveraged Ant. At this point I gave up my right to an easy debugging experience, more on that later.

Java is slow. If you are attempting to do any sort of intensive work Java will quickly eat up the entire CPU. Rendering anything using OpenGLES is near impossible. Make that OpenGLES 1.0 from a distant land before we had vertex buffers, Java plus massive memory copying. Time to check out the NDK. I already gave up my right to an easy debugging experience, might as well get some performance back.

The Android NDK is cool. Nice and simple JNI along with some basic C libraries. Unfortunately no STL support is included in version 1.6 of the NDK. Luckily, our source uses STL everywhere but in a very simple manner. I have written STL substitutes before for other compliers, an exercise in typing. My company, DoBL, is working on a licensing agreement so that I can post my STL-lite to github for all to use. Especially for myself at my future place of employment using a future compiler on a future platform that does not support the STL of yesteryear.

The NDK has access to the complete OpenGLES 1.0 implementation. Very nice. Our frame rate has just gone up tremendously. Still no easy access to a debugger and crashes do not even attempt to resolve symbols for the stack functions. Blind as a bat. I leveraged the NDK’s ddms application and some liberal log statements to find the few problems I had during the port.

The only complaint I have about the Android NDK is that the root most make file for your application must live in the NDK’s install directory. That’s not cool for version control. I have an item on the TODO list to dig through the pile of make files and see if this is easily remedied. That actually irks me more than the debugging experience.

In summary, I started this week thinking that I would be writing a second version of our company’s source code in pure Java. By Tuesday I determined that our code would be too slow in Java. The NDK is meant for people to leverage existing C/C++ code. This also has the significant advantage of not duplicating my maintenance efforts in the future. Yeah Android NDK!

Panorama Theme by Themocracy