As mentioned in the previous post (here), the immediate challenge we faced when we started work on porting, was that we needed to re-code all the internals of our engine. This was necessary because the engine was originally dependant on Directx and all the low level code - such as creating a window, handling events, opening sound devices etc. were beholden to the Windows and Directx API. Unsurprisingly, neither iOS nor Android had support for the proprietary API.
We chose SDL to replace the engine innards. The choice was made because it saved us the trouble of writing all the low-level code ourselves, yet SDL itself was sufficiently low level that we could plug it in our engine. This worked admirably well, and soon we had our games running smoothly on Windows, with the promise of more to come.
Porting to Mac
Porting the game from one desktop OS to another desktop OS was the logical next step. The idea was that it would be much easier to get the game running on another desktop OS, and running our code through another compiler will help us in fixing those porting issues. We didn’t want those issues popping up when porting to mobile devices. From our experience, the smaller and the less powerful a device is, the more painful it is to debug code on it.
The port went as smoothly as expected, and soon we had both the games running on a Mac! (The games are now released on Mac Appstore, and can be found here.)
Pahelika: Revelations running on a Mac.
We then commenced work on the iOS port. This is where we faced the really hard challenges. The issues we faced, and the solutions we came up with might be of interest of some of the readers, so I will explore them in detail.
For those who have developed games on iOS, this will not be a surprise. Mobile devices tend to have low amounts of RAM, and the little RAM there is, is shared between the applications, the OS, and the graphics data. On a desktop, this is not an issue because, VRAM (video memory) is separate from the system memory.
An iPod Touch 4 has 256 MB RAM, which needs to be shared by all three (OS, application, and textures). In practical terms, this means that the memory consumption of a game, including the graphics data, should be kept below 100 MB. That is the target we chose.
Our game, after being ported to SDL, was using around 500 MB of RAM, plus 250 MB of textures, and we needed to make it all fit in 100 MB!
In the end, we managed it. Here’s how:
1. We were maintaining a cache of texture data in RAM as well. This allowed us to respond to a player pressing ALT-TAB or changing resolution nearly instantly, because we did not have to reload all textures on a DirectX device loss. However, OpenGL has a different design and OpenGL drivers maintain the texture data in such cases. This suited us just fine, since both iOS and Android use OpenGL. We removed the texture cache, and promptly saved 250MB RAM. This was the single biggest saving that we got.
2. Pahelika: Revelations and Pahelika: Secret Legends both had several animated sequences. These were displayed by displaying a sequence of images in quick succession. We reduced the number of images for the animations - drastically. This made the animated sequences less impressive, of course, but the memory savings were BIG.
3. Lot of images were being loaded once at the start of the game, and then were kept around in VRAM. This helped in propping up, say, the Jigsaw Puzzle instantly, but was increasing the memory pressure. We switched to loading the puzzle data only when needed. This meant that the puzzles loaded after a slight delay, but it saved us some precious memory.
4. Another big win came from reducing the default number of particles in the particle system. Our particle system, by default, allocated a set number of particles. Those particles were then kept around till the particle system was in existence. Lowering the default number did affect some particle systems visually, but overall worked very well.
Lots of Particles!
5. We downsampled the audio data on loading. If the audio data is played at 22K samples/sec instead of the CD quality 44.1K, we could reduce the memory taken by sounds & music in half. Given that mobile devices have tiny speakers anyway, we could even go down to 11K on devices with low RAM. Going down to 11K does affect the sound quality appreciably, though.
6. Even after all of this, we were still looking at 250 MB of memory usage. Our last trick: Compress all the completely non-transparent textures with PVRTC on iOS (ETC1 on Android).
TADA! From 250 MB to 100 MB! The battle is won! Yay! No, not really. You see, there were other issues.
Another issue we faced was with playing video. Being an indie, we could not afford to play for the extremely overpriced video playing middleware, so we chose to use the theora video codec for which the source code was available (and the license allowed us to use it in a closed source software).
This was really the only option available to us at the time, and it wasn’t without its problems. It turns out that after decoding, the theora output is delivered in YUV format. Because SDL didn’t support directly displaying a YUV texture, in order to display it, we needed to convert it to RGB.
It wasn’t so bad on the PC, but on the iPod Touch, it was killing.
The obvious idea was to reduce the video frame size and then scale the output video. However, if we reduced the size too much, the video became blurry. Then we used a not so obvious trick: We reduced the video FPS by half. Viola: Video running without any issues on iPod Touch 4!
Even after all this, there was one big, hairy, extremely annoying problem remain. You see, on the PC, our game ran on a big screen and we simply stretched the game output to fit whatever monitor size was. This was ok, even with some letterboxing, gamers could easily play the game without any problems.
This wasn’t going to work on smartphones, though. The screen was so small, if we just scaled the game output, in-game objects would be too small to interact with, and the text would become invisible. Additionally, while the iPad Touch 4 had a pixel resolution of 900x640, the just released iPad 3 had a pixel resolution of 2048x1536, BIGGER than the graphics resolution of our PC original, which was 1024x768.
This wasn’t looking good, but having come this far, we weren’t going to turn back now. Stay tuned for the final article in the series for how we tackled this problem.