BKMs: Gameloft and Intel Discuss Game Development and Porting to Android*
Today, mobile game developers design for the most widely accepted platforms to maximize market penetration and, with the growing proliferation of platforms, the strategy for planning application development is more important than ever. Intel shares highlights on this topic from a recent conversation with Gameloft, a leading publisher of digital and social games. They agreed that there is a lot to be said for building for multiple targets from the beginning, as part of the development strategy. However, building apps simultaneously for today’s multiple targets comes with potential risks, and developers should try to address/mitigate those risks.
In this article, Gameloft provides a number of tips for enabling simultaneous development for mobile games on iOS* and Android platforms.
The Practice (Price?) of Porting
With iOS and Android as today’s primary mobile OSs, the usual approach taken to write a game for both platforms is to write for one and then port to the other because the stacks are very similar. This practice can lead to duplication of effort and generates needless porting problems, which often means that a game is considered better on one platform than the other. Interestingly, though, since both iOS and Android platforms execute compiled C++ code that uses a version of OpenGL ES* for graphics, it’s easy to see that if your core game fits this profile, it can be called from either Java* on Android or Objective-C* on iOS with few or even no changes. Then, the only remaining task is to write an OS-specific layer on each device to handle I/O networking, controllers, etc. If you write this layer as thinly as possible using a generic interface, then you minimize the amount of platform-specific work you need to do on each platform, thus maximizing your cross-platform development.
Another advantage to this approach is that the standardized interface allows you to develop a layer for Windows* OS as well. Of course, there’s no native OpenGL ES interface on Windows, but it’s fairly easy to link to one of the freely available OpenGL ES emulators. Once you have this built, you can take advantage of the wide range of debug tools that Windows offers to further minimize your development time.
Target Rich Environment
You have to do more than two builds to support Android and iOS because Android and iOS have a range of targets: x86, v5, v7, MIPS, etc. While you may not target all of these, it’s important to consider the advantages of each, along with its share of the market.
We all want the best of both worlds: to build for the widest range of targets in the shortest development cycle (fastest build speed). Generally, there is a direct relationship between the quality of a game and how many build/edit/install/run/test cycles you can fit into the available development time. To maximize quality, you need to make the step from edit to run as quick as possible, yet recognize that building for a host of targets significantly increases the time it takes to get from edit to run.
Gameloft’s Perspective on Building for Multiple Targets
Recently, the author spoke with Gameloft’s senior developers, and they agreed that there is a lot to be said for building for multiple targets from the beginning as part of the development strategy. Building simultaneously for multiple targets also has potential risks. A fast edit-to-run cycle is crucial, but Gameloft pointed out that it’s very important in this environment to ensure that a coder who adds a feature or fixes a bug on one platform doesn’t break the build on another. Its solution to keeping the build cycle fast is to use distributed compiling and spend time on the build environment to ensure it is as close to a one-click build as possible. Gameloft solves the accidental breakage risk by performing full builds for all platforms as often as possible, using dedicated build machines. Its build machines run unit tests after each build to ensure that the latest build is at least as stable as the previous build. Thus, if something breaks, it’s easier to identify which code changes were responsible.
Avoid Platform-Specific Code and Fixes
Naturally, some aspects of a solution cannot be fully platform-independent, such as sound, I/O, and disk access. OS message handling can also be very different. However, you can use third-party libraries to handle most of these areas and, with careful selection, these libraries can save a great deal of time and effort developing cross-platform code.
Whichever solution you choose, it is possible to hide most of the platform-specific differences between iOS and Android. The advantage of using this method means that game coders can be more efficient and free to innovate without having to be particularly mindful of the platform requirements.
Intel: How do you avoid platform-specific code in cross-platform development at Gameloft?
Gameloft: As you said, we try to write platform-independent code as often as possible. When this is not possible for a certain implementation, we have libraries that handle the platform-specific parts and expose a platform-independent API. There are some special cases that don’t fit into a specific library and, for those cases, we keep a knowledge base with Android-specific code. For example, we don't have a library that handles video playing, but the intro movie is added directly into the game code by the game team.
A painful aspect of platform-specific coding is caused by certain features that are Android-specific and non-existent on other platforms, for example, controllers. For these kinds of features, the Android team needs to create special designs and platform-dependent implementation, which will be separated by pre-processing directives.
It’s Never Perfect the First Time
Even with the best development strategy, sometimes something that appears to be cross-platform, isn’t. Sometimes, you can take generic solutions developed on one platform where they behave perfectly, run them on a different platform, and find that they under-perform and/or have significant visual issues on that platform. There are many reasons for these issues including memory fragmentation, varied fault tolerance of the different platforms, and alternate HW implementations leading to fast techniques on one platform running slowly on another. It’s inherent in app development. Sometimes, things just don’t work the way they should. When that happens, your best friend is a graphics profiling tool.
There are a number of free profilers that are useful, but that is a deep subject and beyond the scope of this paper. Suffice it to say that the best profiler for this situation should give you access to deep timing information and GPU active/idle/stall times. You should be able to investigate constant data and textures, as well as all the standard features you expect like call timing and state change statistics. Above all, a profiler should make low-hanging fruit immediately obvious in the way it presents its data.
Consider the following screen shot from Intel® Graphics Performance Analyzers (Intel® GPA). The vertical blue bars represent draw calls in a particular game. The size of the bars represents the time taken by that particular draw call, and it’s fairly easy to see which draw call to investigate further in the code.
Figure 1: Graph showing render events in a frame (from Intel® Graphics Performance Analyzers,
Frame Analyzer). Time moves from left to right, and block size represents duration of call.
Intel: How do you avoid platform-specific code in cross-platform development at Gameloft?
Gameloft: As you said, we try to write platform-independent code as often as possible.
Intel: Do you see code under-perform or even cause significant visual issues on only one platform? Can you give any examples and describe what you did to fix it?
Gameloft: Yes, there are differences not only between platforms but also between devices. Let's look at the following examples:
- Code used for reading resources is underperforming on Android, even between devices. Look at the table below, and you can understand what kind of performance impact this might have:
- Games that use a streaming resource usually have a really high CPU load, while games that load resources whole will have longer load times. One solution is that loading compressed resources and then decompressing them into memory might result in better performance than reading the data raw from the SD card. However, this solution may require fine-tuning between devices.
- Also, shaders behave differently, again not only between platforms but even between devices. For example, this code works with no problems on iOS, while on Android it has a heavy FPS impact or even crashes on some GPUs:
When the for instruction is unwrapped into three sequential instructions, Android regains FPS while iOS devices have no FPS change whatsoever. Also for shaders, there is a known issue that the precisions (highp mostly) perform differently between GPUs.
Intel: Do you use profilers onsite and, if so, which ones do you use?
Gameloft: As often as we can, we use Dedicated Profilers provided by hardware vendors for GPU profiling. We recently found the Graphics Performance Analyzer from Intel to be indispensable and a good addition to existing tools. These tools are critical when trying to optimize not only specific GPUs, but the 3D performance overall, as some optimizations apply to the whole project, not only to a specific GPU. For game profiling as a whole (CPU, memory, etc.), we use internal profiling tools that come integrated in our engine.
The screen shot below is taken from a real-life scenario using our internal engine profiler. It is from a game that had menus moving very slowly for low-end devices (single-core, 512 MB). You can see in the screen shot that the menu was taking almost half of the frame time. We found through debugging that we were getting a lot of cache misses because numerous font sizes were used, and the fonts were always reloaded. The "fix" for low-ends was to reduce and approximate the size of fonts to fit the font cache, and the font effects (shadow and blur) were removed from low-end devices.
Figure 2: Screen shot of profiler results
GPU vs. CPU—Which is Slowing Down Game Performance?
Sometimes we see innocuous pieces of code perform radically different on apparently similar HW, even to the point where the app is GPU-bound on one platform and on the other it’s CPU-bound. Just to clarify, if an app is GPU-bound, it means that the CPU code for a frame finishes more quickly than the GPU code, so GPU content governs the frame rate. CPU-bound is the opposite of GPU-bound, where the CPU code takes longer.
The main reasons for code behaving differently across platforms are many and varied, but let’s briefly discuss a couple:
- Cache Issues. It always pays to know the cache alignment and size on the platform you are working on. It’s not uncommon to see algorithms fit in the cache on one system but fall out on the other. Tools like the Intel VTune™ analyzer are a great place to start if you see small pieces of code behaving badly. Often you can fix them by changing alignment on structures or changing the order in which arrays are accessed.
- Function Call Overheads. This can be a compiler issue or it may be platform-specific. Typical examples might be when a function is in-lined on one system and not on the other. It’s best to be a little careful of in-lining as it can lead to severe code bloat, but it can really help when you have function calls in tight loops of code. It’s important to know and understand the flags for the compiler for each platform you are using.
Intel: Have you ever run into issues like the ones mentioned above or any other issues where the compiler was producing under-performing code on one platform?
Gameloft: We’ve never found code to perform noticeably different between platforms as a result of compiler output. However, it is good to notice that it is quite hard to get benchmark results for compiler optimization, as long as the platforms always come with hardware differences.
Intel: Supporting Android means supporting a wide range of devices with very different capabilities. How do you manage to achieve playable performance on low-power machines while at the same time produce rendering quality on top-end machines?
Gameloft: We aim for each game to implement a performance profile system that can handle effect selection based on generic hardware statistics (GPU type, CPU frequency, number of cores, RAM), instead of targeting each device. Even this kind of system isn't bullet proof, as the same GPUs will behave differently, considering there is no way to detect the GPU frequency on Android. We are currently experimenting with a configuration section in our new games where we allow the user to select a higher or lower performance profile.
Intel: Lastly, supporting Android means a lot more screen resolutions to support. What tips do you have for keeping good, clean front-end graphics with so many different sizes to consider?
Gameloft: We always aim to create UI in a scalable way from the beginning by using constraints, propositional positioning, and sizing of elements. We try to avoid hardcode size settings as much as possible.
About the Author
Steve Hughes is a Senior Application Engineer working at Intel in Great Britain. He has focused on support for game development on x86 platforms ranging from desktops to tablets to phones for the last six years. Prior to Intel, Steve worked for 12 years as a game developer for a number of companies where he worked on most aspects of game development.
Intel, the Intel logo, and VTune are trademarks of Intel Corporation in the U.S. and/or other countries.
*Other names and brands may be claimed as the property of others.
Copyright ©2014 Intel Corporation. All rights reserved.