As most people know I pretty much dig Android. I also have been a friend of OpenGL. As I come from the Java side (we have cookies ^^’) and there are two good wrapper implementations with JOGL and LWJGL, I found the perfect combination in OpenGL on Android, hence the tutorial ports.
Over the time I did some tests on Android. Not only with OpenGL but mainly overall to see what could work and how. Unfortunately Android is far from perfect (what ever is?) and has some issues also affecting OpenGL development on Android.
3D Games on Android…
Games are probably the main market working with 3D on Mobile Phones nowadays. On the iPhone we have some great examples of cool and nice looking 3D games such as Ravensword and many others. They look good and also run very smoothly (especially on a 3GS, loading times are most of the times much better).
Now, for Android we also have some good Game examples with the just announced winner of the Android Developer Challenge 2 in the category of games: Speed Forge 3D. The really beautiful WipeOut clone gives you a nice 3D game example on Android. Another example is Mystique by Bendroid. The 3D Ego-Survival-Horror in the style of Silent Hill and The Ring is sure to give you some creeps if you play it in a dark room, at night, feeling lonely.
…and the issues!
Now if you compare Speed Forge 3D or Mystique with Ravensword or Gameloft’s upcoming N.O.V.A you probably will get jealous and drool all over the videos. No question: SF3D and Mystique are great, Kudos to the developers, but compared to the iPhone they are far behind.
The big question should be: Why? And the answer is neither simple nor a single one in my opinion.
1. Heterogeneous Phone Market
One big thing coming up with the release of new Android handhelds is the inconsistency throughout the phones. Android itself is a specification and a referential implementation. Now, most phone manufacturer alter some things or add their own implementations of something such as OpenGL. Therefore, it is already the case that with every new release of a mobile phone based on Android you see updates of many Apps popping up because of problems or even crashes on the new device.
Let’s have a look at Speed Forge 3D: I have a HTC Magic and it runs… OK. Not absolutely fluently but playable. Now, a friend of mine has a Samsung Galaxy (or i7500) and he can barely play it until it crashes. Now the Droid from Motorola entered the market with another graphics chip and a larger screen. Many new “properties” to test against and to deal with. The iPhone on the other side may have been updated over the time but the environment stayed nearly the same. And something that runs on the 3GS will also run on the 3G, just load a little longer. So, as time goes by more and more Android devices will come out and will further diverse the market.
2. The Dalvik VM
As always when it comes to Java you will hear people say:
It’s slow because it’s Java!
Java will always be slow!
Forget Java, use the NDK!
(just saw the same posts pop up on the newsgroups and forums again)
They will argue that a Virtual Machine can never give the speed you need (some also tend to forget that .net is a VM interpreted environment). They will argue that the Garbage Collector slows everything down. And if we look back about 10 years they really had a point, but nowadays that may not be total crap but is just not that easy to say.
First of all let’s have a look around at Java-based 3D games, based on the very popular, updated and fast libraries JOGL and/or LWJGL. There would be the experiment Jake2: A Quake2 clone made in Java based on JOGL and LWJGL to have a comparison. If you have a look at the benchmarks you will see it is hard to argue that Java is that slower over the native implementation. And engines such as jMonkeyEngine support the Java faction. The demos over there such as Spirits and other games like Trible Trouble and Poisonville on the other side show that there are very good possibilities to use this object-oriented programming language as your development base.
The thing is that not Java per sé is the problem but as with every programming language the code optimization and the execution environment, in this case the Virtual Machine. Even Tim Sweeney from Epic Games suggests that functional programming and Garbage Collection is the future. The newest Sun Java VM and other VM implementations show what is possible to do with optimization within a managed environment. A good Garbage Collector (GC) and a very good Just-in-Time (JIT) Compiler such as in Suns current Java implementation speed up not only the application but also development itself because of less time spending for the memory management.
But why still the speed problems? As already said, even if Java is interpreted and helps with the memory management you still have to think about what is happening beneath. Most applications tend to just create one instance after the other and do not really care about memory management. This may work on a desktop system but will bring problems to you on Mobile Phones. Things like weak references, caches, volatile and transient are forgotten but highly important terms in Java development. Especially Mobile Applications still have to really care about memory even with all this technically highly fledged phones. Care about the memory more than about computation on modern smartphones.
Nevertheless, even with these more or less “simple” tricks Android still has some problems that you need to remember during development. Most of these result from the Dalvik VM. The current Dalvik implementation on Android phones does not support JIT nor is the Garbage Collector any good.
As no JIT is available all code is interpreted on time and not pre-compiled or anything. On a limited device such as a mobile phone this results in slowdowns. Therefore, it is important to pre-load everything you need and cache as much as you can to have a fluent gaming experience during e.g. a level.
Another thing is the current GC in Dalvik: Avoid it ^^’ Sure, you cannot really avoid the GC directly but try to not release too many objects during gameplay (or fire it by yourself) as the GC will slow your phone so down that even the OS will need time to react. This is also something that can be managed with caching and a good manual memory management. Currently, up to Android 1.6 an application may use up to 12MB of memory. With Android 2.0 this has been upped above 20MB, but as a game should support many platform versions (the issue why I am posting this), 12MB will probably be the limit for a while.
3. The Android OpenGL ES implementations
Another thing that bothers the development of OpenGL ES applications on Android is the again heterogeneous availability of specifications. Android itself by SDK supports OpenGL ES 1.0 and 1.1. The official current OpenGL ES version is already at 2.0. While 1.1 is backwards compatible to 1.0, 2.0 has no backwards compatibility. OK, its to note that 2.0 is somehow pretty fresh and you cannot ask for the newest technology in Mobile Phones. But most Android phones at the moment only support 1.0, for example the HTC Magic whereas the Motorola Droid should be able (hardware-based) to support 2.0.
The support/portability problem is one of the issues. But more relevant is the functionality available in 1.0 and 1.1. The iPhone is based on OpenGL ES 1.1 and by that it has a big advantage over most Android phones: Vertex Buffer Objects (VBO)! As posted before my tutorial ports, OpenGL ES does not support primitive rendering (glBegin/glEnd). This is good as it will nevertheless be dropped out of the OpenGL standard. In OpenGL ES you have to use Vertex Arrays and/or Vertex Buffer Objects, which are faster anyway. BUT 1.0 only supports Vertex Arrays and no VBOs. This is one big disadvantage not only in speed but in development. You can argue that it is possible to program an application that supports both with tagging the sections but as it comes to mobile development you always try to comprehend everything as much as possible without acting on too many specifics.
Two other important things when it comes to game development are that 1.0 does not support multi-texturing on its own (there are ways, but those are tricks) and it does not support the automatic mipmap generation. Both are necessities if you want to do such nice things like Ravensword or N.O.V.A.
Another thing are the phone specific OpenGL ES implementations. Not only versions but also manufacturers provide different implementations with their phones. For example the Samsung Galaxy has a known very weak OpenGL implementation. There are already several published articles on how to add the HTC implementation to a rooted Galaxy. And as more and more manufactures will enter the Android market in the coming month this will probably get worse.
So, what can be concluded from this? Actually not much but 1.0 <= 1.1 < 2.0! There are some tricks here and there to deal with some things under 1.0 but overall you just have to accept the fact and optimize around what is missing. Or you limit your development to 1.1 (at least for now). But by that you will loose many phones such as the Magic or G1, which probably are the most widespread Android phones at the moment.
4. General and Specific OpenGL (ES) Paradigms and Tactics
As with every programming language or library there are several Best Practices that should be remembered during development. The same thing applies to OpenGL. There are many posts in the net on how to optimize your OpenGL code and you should definitely use Google to keep you up-to-date. I just want to point out two important things (in my opinion) for mobile development.
The first thing is to reduce your draw calls! This is not limited to mobile devices but OpenGL in general. But because of the limited hardware versus a Desktop system, this is even more important. Each draw call is not necessarily bad but in sum are slow. Now, most people do enormous, cool architectures with self-drawing objects that are instanced over and over again. This is nice from an object-oriented point of view but is bad on a mobile device. If you want to do something good and fast for Android work with overall handlers. This relates to what I said about caching. To have super classes, handlers or factories that collect all changed elements or all objects that need to be drawn and only that Object fires the draw call is the way to go. This may look like a limitation but this is not only good for the speed of your application but also reduces the possibility of asynchronous errors when instances of deep objects are computed while the rest already handles another request.
The second thing correlates with what I said about memory handling and caching: Reduce your texture binding calls! To rebind your textures again and again and switch them back and forth is also bad for the overall performance. I know that it is tempting and Android with its nice resource loader somehow invites you to have hundreds of single texture files and load them again and again. But this will get you into trouble really fast. First of all, reduce the calls by utilizing again a super class, a texture handler with some kind of caching mechanism. Another possibility is to merge several textures into one large texture and offset the texture mapping coordinates accordingly. This is easy to do and just requires a small asset pipeline but the advantage is huge.
Both things suggest some very important points that have to be taken into account in the architecture and early development. Therefore, always tend to fulfil the mentioned points already while you are planing.
5. General Mobile Development Paradigms
As well as for OpenGL there are some rules regarding Mobile Development in general. There are also several posts on the net about this topic but just to sum up what I already coped in the above text: A Mobile Device is no Desktop!
Many people come from the Desktop development and are real geniuses there. They make the best applications, fast, functionality perfect and have wonderful graphical user interfaces. BUT what works on a Desktop does not necessarily work like that on a mobile device.
JavaME made it necessary to go back to Java 1.3.1 programming style; To forget about Generics; To forget about Enumerations and just use the “old ways”. Now, with something like the Android SDK, which allows to use nearly the full Java5 specification people just copy their desktop applications into the Android projects and this will make problems. All the things I pointed out above result from that premise as the desktop is the more forgiving (because more powerful) platform.
Things like creating hundreds of instances, using hundreds of different files, not working with in-memory data but reloading everything over and over again are bad… bad… and even worse, really bad! Do not get fooled by all the technical specifications of the mobile devices. You cannot compare the CPU and the RAM with the desktop pendent. I recommend to have a look “back” at the JME development and the experience people made there. Do not copy it one-to-one but use it as a guideline and convert it into a comprehensible Android project. Most problems, most errors, most failures that are posted and reported result from the unused experience made in that mobile development area. Reuse that knowledge, reuse their principles, their ideas and you will avoid many problems. And as there are enough “new” problems do not trip into the old ones.
Please do not get me wrong: My opinion about Android is high, I have one on my own and I love it. Love developing on it, love the SDK updates. The only thing I want to point out is that development on Android phones and Android itself has to start to keep these five things in mind. The next three to six month mark an important state with Android where to go and with at least the noted points you should be good to go.
So, go out there and really work on it to do the best f***in’ 3D Android game of them all (and comment about it here ^^’)!