Due to another new project I’ve been working on, I was able to test out different methods on rendering sprites on the Android platform.
- Method #1: Use OpenGL ES to render to a buffer in C via JNI. This method turned out to be the fastest if polygons are batched via lists in a single (or a few) draw calls, and if using fixed instead of floating point numbers. It also opens up a lot of other effects natively not easily implemented through the other two methods (native 3D and matrices). However, it does not support antialiasing natively as is advertised and as I expected. The OpenGL ES 1.0 specs report “antialiased polygon rendering (with alpha border fragments, not multisample)”, but I could not find any information on this at all, and no antialiasing calls that I tried actually worked. There are methods that would allow antialiasing, but none efficient enough for a phone.
- Method #2: Drawing directly to a pixel buffer in C via JNI. This method came in a very close second place if using memory copying functions to blit images, but that also removes a lot of important effects like translucency. Results were less than optimal when not using direct memory copies (I didn’t even worry about including them below) :-\.
- Method #3: Rendering through the Android API in Java. While this method is the most robust in terms of feature set, it is also by far the slowest.
For my personal goals, I’ve come to the conclusion it would be best to use Method #1 for my heavy rendering tasks, and used baked translucent pixels around the borders in the images to emulate 2D antialiasing. The translucent borders could also be added at load time. I think I may also use Method #3 for some other special effects and overlays, though that will also make it harder to port, but I think worth it for now.
The following are the approximate average FPS
results to the speed tests I performed (preceded by column descriptions):
- The Test: 45*45 quads (2*45*45=4050 polygons) drawn to cover the whole screen each frame, none overlapping
- Emulator/Droid: The tests were run on 2 separate systems. An Android emulator running v2.2 on my Intel core i5 2.53ghz laptop, and my Motorola Droid also running Android v2.2.
- FLOAT/FIXED: This is only used on Method #1. OpenGL ES supports (in different functions) both floating point numbers and fixed point numbers. Since phones usually have no FPU, and the ones that do might have very slow ones, it’s much more safe efficiency wise to not use floating point numbers. The OpenGL ES solution was to have non-floating (fixed) point decimal arithmetic numbers that act virtually the exact same as normal integers when used with arithmetic functions, but do not have near the range as either floating point numbers or normal integers. The gain by going from floating to fixed was marginal in my tests. 16 bit integers (shorts), which I prefer, can also be used and showed near identical results to fixed point numbers.
- INDIVIDUAL/ALL: This is only used on Method #1. It means whether all the quads were drawn individually in a different call for each (which draws 2 triangles to make a quad), or in 1 call from a list of all the triangles. The gain by going from INDIVIDUAL to ALL was ~1fps on the emulator and ~12fps on the Droid in my tests.
| ||Emulator ||Droid|
|METHOD1+FLOAT+ALL ||11.2 ||43.4|
|METHOD1+FIXED+ALL ||11.5 ||44.2|
|METHOD1+FLOAT+INDIVIDUAL ||9.7 ||30.4|
|METHOD1+FIXED+INDIVIDUAL ||10.7 ||32.5|
|METHOD2 ||25.5 ||43.3|
|METHOD3 ||6.4 ||22.8|