Overview
With Android x86-based Intel® architecture devices increasing in prominence in the marketplace, the libGDX team set out to ensure developers could seamlessly deploy their games and apps with the use of a single cross-platform framework. This case study gives a brief introduction to libGDX and then shows how little effort it takes to port a huge existing code base to x86-based Android devices!
What is libGDX?
libGDX is an open-source, cross-platform game development framework for Windows*, Linux*, Mac OS X*, Android, iOS*, and Blackberry* platforms and WebGL-enabled browsers. You can use libGDX to write their games in any JVM (Java* Virtual Machine) language of their choice (Java, Scala, Clojure, Kotlin*) and can then deploy the same code base to all supported platforms. Across the various platforms, libGDX supports all IDEs commonly used for JVM development, like Eclipse*, NetBeans, and Intellij IDEA*. Testing and development can largely be done in a desktop environment, thereby speeding up development and iteration times as time-consuming device deployments are minimized.
Hobbyists, small indie developers, and big studios alike use libGDX in creating desktop and mobile games such as Google’s Ingress, Robotality’s Halfway (Figure 1), or Kiwi Inc.’s lineup of top grossing social games. Non-gaming applications also use libGDX such as Esoteric Software’s Spine 2D skeletal animation software.
Figure 1. Screenshot from Robotality’s Halfway
Since it’s 1.0 release in Q2 2014, libGDX has seen around 250,000 monthly downloads from Maven Central, making it one of the most widely used, open-source, cross-platform game development frameworks currently available.
Figure 2. libGDX downloads over the last year
Intel, Android, and libGDX
With such a huge user base we quickly realized that libGDX had to support Intel® x86-based Android devices seamlessly and deliver maximum performance. We implement all performance-critical parts of libGDX in native C/C++ code, which is then exposed to developers via JNI bindings so that they can stay within the comfort of their chosen managed language.
libGDX features a wide range of platform-specific native components that help developers create high-performance games:
- CPU-side graphics: while libGDX relies heavily on GPU-side rendering through OpenGL ES*, there is still a need for CPU-side image composition and manipulation. Therefore, libGDX implements a full-fledged 2D rendering library and integrates FreeType for font rendering.
- Manual memory management: Java applications feature a garbage-collected, runtime environment method of memory management. While this is convenient in many cases, native memory resources for textures or meshes need to be managed manually. We enable manual management by piping all related operations through native C++ code.
- Linear algebra: many areas of game development rely on matrix, vector, and quaternion mathematics. Some games experience a bottleneck with matrix multiplications. To help with such issues , libGDX implements common linear algebra operations in C++ and assembler.
- Physics: most games require some level of simulated physics to generate believable interactive worlds. libGDX integrates Box2D and Bullet* for 2D and 3D physics. Both 3rd-party libraries are written in native C++ with CPU-specific assembler extensions such as Intel® Streaming SIMD Extensions 3 or NEON*.
Since its inception, libGDX supported x86 and x86 64-bit on Windows, Linux, and Mac OS X. Therefore, we were optimistic that the porting effort for Android on x86 would be minimal. Here is what needed to be done!
Setting up Android builds for x86
Each of libGDX’s native components has its own subproject with build files for every platform. For desktop systems and iOS we rely on GCC/Clang and a custom made Apache Ant*-based build system. The Android version of libGDX uses the Android Native Development Kit. NDK builds are usually described by two files: Android.mk and Application.mk. In most cases, it was sufficient to add x86 as a build target in the Application.mk file (as shown below). Easy!
APP_ABI := armeabi armeabi-v7a x86
APP_PLATFORM := android-8
APP_STL := stlport_static
Some of our components have CPU-architecture-specific build flags, e.g., to enable NEON, to use inline assembly, and so on. To get a first operational build, we disabled all of these flags.
Testing
To ensure code stability, libGDX comes with a huge set of tests that we can execute when expanding to new platforms. After the first NDK build for x86 Android, we fired up the Bullet physics test collection. It was a great success! Our tests ran flawlessly on the ASUS MeMO Pad* FHD 10 that Intel provided us for testing (Figure 3).
Figure 3. Example of Bullet* Physics Test run on an ASUS MeMO Pad* FHD 10
All other tests also ran as expected on the CPU as well as the GPU, giving us confidence that the Android build for x86 was ready for developers. But could we do better?
Employing Vectoring
Most of libGDX’s native components deal with math of one kind or the other, much of which can be vectorized by GCC automatically. All currently available x86-based Android devices support Intel SSE3, so let’s make sure we get the most out of our CPUs!
ifeq ($(TARGET_ARCH),x86)
LOCAL_CFLAGS := $(LOCAL_C_FLAGS) -mfpmath=sse -msse3
LOCAL_CPPFLAGS := $(LOCAL_CPPFLAGS) -mfpmath=sse -msse3
endif
With the simple tweak above, all our native code now exploits SIMD instructions as long as the compiler was able to vectorize our math code. Note that we can’t do the same for ARM as not all ARM chips support NEON. A definite win for the x86 Android!
Making developers happy
Starting with the 1.0 release, libGDX projects automatically support x86-based Android devices as seamlessly as they support any other platform. The added support is the final piece of the puzzle to help make libGDX developers happy. No manual copying of files or setting flags, it just works! If you are interested in libGDX, we recommend reading our extensive documentation and view our video tutorials.
Conclusion
Getting libGDX and its huge native code base to work with x86-based Android was a breeze. We simply had to tweak 2 files (5 lines) per subproject. Supporting x86-based devices with Android and libGDX is now a reality. Additionally, developers can now exploit tools like Intel® Hardware Accelerated Execution Manager to further improve the development cycle and Graphics Performance Analyzers for Android to profile and benchmark their games. This is a big win for the libGDX community.
About the Author
Mario Zechner is a technologist with 8+ years of professional experience in a wide range of domains, from game development to data analytics. He is the creator of the open-source game development framework libGDX, author of the book "Beginning Android Games" and is currently working on RoboVM, an ahead-of-time compiler for Java Virtual Machine bytecode. You can find Mario on Twitter (@badlogicgames) and his blog (http://www.badlogicgames.com).