Tilt To LiveIt’s been a while since my last post. As things started entering crunch mode and the whole holiday rush was happening I unfortunately let my blogging schedule slip as a result. It’s still going to be irregular for the next month or so as I’m trying to finish up Tilt To Live. The official Tilt To Live site has launched with a few screenies and more info though! It’s a basic info site at the moment, but I’ll be sprucing it up as time goes on. Starting to ramp up some pre-release hype around the game but it’s proving to be challenging :].

A second take on Multi-threaded asset loading

FrustrationAfter much reading, experimenting, more reading, and just plain frustration I’ve toned down the multithreading goodness on asset loading for tilt to live. The main cause for this decision was speed. I seem to have miscalculated the amount of time it takes to load images. It was somewhat of a compound problem. Firstly, loading textures with libpng was on average 4 times slower than the standard UIImage/CGContextDrawImage! I’m not entirely sure if my implementation of libpng was inefficient in some way, but it was taken from several samples and documentations on loading pngs. Secondly, loading textures on a background reduces the speed by another 2 fold!

I’ve finally implemented texture atlases, so I got a bit of a speed boost on rendering. Although, I now have a 1024X1024 textures. Using libpng on a background thread took ~8 seconds to load a single 1024×1024 png image. I put the image loading on a main thread and it reduced it to 4 seconds on average. A great improvement, but with 2-3 large textures this was still unreasonable. After doing some profiling with the basic GLSprite sample on Apple’s iPhone developer site, I decided to revert back to the UIImage/CGContextDrawImage approach and it allowed me to load 1024×1024 textures in a little less than a second. Alright, progress! I’ve reduced a 20 second load time down to about 4-5.

As a result, I removed all background texture loading from the game. To further reduce the initial launch time I see that I can defer one of the 1024×1024 atlas textures til later as it’s only needed when the main game is up. That’ll be another second shaved. This is definitely one of those times where a supposed ‘good’ thing that was implemented has come back to bite me in the ass….

unlock_lightning

Lessons Painfully Learned

The slickness of having background loading while the game is giving the user some feedback is nice, but ultimately not worth a larger load time. The discrepancy here is that on a 3G iPhone the load time is huge with background threads, but on a 3Gs it’s very fluid and barely noticeable. So if I was making a 3Gs-specific app then this is definitely something to look further into and optimize a bit more. But with the goal of trying to reach as many different gen devices as possible, I needed to compromise and give up some of the ‘flare’ for speed. Right now the target release date (or at least submitting to Apple for approval) is in January 2010.

In the meantime, follow us on Twitter and Facebook to get the latest news on the game!

I’ve had a rather productive weekend! Implemented a handful of new art assets from Adam, finished some more gameplay code, optimized a bit, and even released a new build to testers tonight. It wasn’t a smooth process, but when is it ever? I was having an issue with loading the assets of the game at app launch and it caused the game to crash if it ever took more than 20 seconds to load the assets.

Admittedly, the asset loading code could use some optimization. Namely, I need to start using texture atlases for the art assets to minimize the state changes in OpenGLES. The reason I haven’t done this yet is art assets are still in flux and changing almost daily. I haven’t found any decent tools to pack textures into single images yet (haven’t looked too hard yet) and I haven’t taken the time to re-write the resource management in the game to be aware of multiple resources in the same texture.

So I’m putting it off for now and in the meantime wanted to redo the overall asset loading the ‘right way’ with a proper load screen and what not. I was faced with:

  • The Easy Way: Just draw up a loading screen and then load the assets right after drawing. The game essentially is blocked and there’s no user feedback that the game is running until the assets are all loaded
  • The Difficult Way: Whip out some multi-threading API’s and have the assets load on another thread and have some animation play on the main thread.

Naturally, I chose the ‘difficult way’. After doing some reading up on multi-threading assets on the iPhone I figured it wouldn’t be too difficult to get running and I could implement it for tonight’s build. Wow was I wrong. I took the 20 minutes to implement the correct EAGLShareGroups and EAGLContexts and the supporting code to play the load screen and have the background thread load the assets.

loader

It all worked beautifully and having the circles spin while the “loading” words doing some idle animation worked surprisingly well with little stuttering. I tested it on the Simulator and it worked fine, but to get accurate time results I began testing on the device itself. With everything in order I kept testing the loading screen and nothing was amiss. I was minutes away from calling it done and about to start making final test builds when I tried it one more time unhooked from my mac mini… I was looking at the circles spin when suddenly they locked up. The screen turned black, and the next second I was staring at my home screen.

After debugging the build I was still at a loss as to what was going on. Each time the application crashed it was an EXC_BAD_ACCESS error in the CGContextDrawImage function. I’m using this function to load a texture into OpenGL and it’s worked just fine in a single-threaded environment. Unfortunately, I haven’t found any definitive info on whether it’s possible to call this function from a 2nd thread or not. This code listing has a very similar process inside the ‘textureWithName’ method (single threaded) in case you wish to see how it works.

Unless I come across something that shows how to use CGContextDrawImage correctly in a multi-threaded application I’m probably going to go the route of libpng. The portions of code I added have worked in regards to openGL so I believe I have that part solved, it’s just the data conversion from a file to raw bytes before openGL gets it’s hands on it is the problem.

Some Data You May Find Useful

My decision to use multiple threads for asset loading wasn’t based on any conceptions of a speed boost. If it loaded faster due to multi-threading that’d be great but it was secondary. I was doing it from a polish point of view and to make the whole experience on the user more seamless. So here’s a list of some data I’m working with:

  • 68 png images totaling 609kb. There is huge performance gain potential here when I implement texture atlases.
  • 22 audio (caf) files totaling 10.9Mb. Audio is certainly the majority of the game’s data, and I hope to reduce this by tweaking the compression on these files later.

Some load time data, don’t have the log files off hand at the moment so these are approximates:

[table id=1 /]

So the single threaded time is pretty straightforward. In the 2nd column it was my attempt to do both audio+graphics loading in a single background thread. After realizing I wouldn’t be able to fix this in time for the build I opted for a different multi-thread approach, which is shown in the 3rd column.

I load all the graphics assets on the main thread but just before I do that I spin off a background thread to load the audio assets. The result? Pretty much no performance gain! The graphics load in pretty much the same speed but audio loading takes twice as long while doing it at the same time as graphics loading. The net time is still around 7-8 seconds, which is no different than a single-threaded approach.

I’m sure I’m probably doing something dumb in the loading of both assets since both have to hit the file system at the same time and there may be some thread contention there, causing it to lock the audio thread. I’ll need to look into it further because if I could do both simultaneously with minimal blocking then this approach would be the way to go.

The visual result? The load screen animation freezes for 3-4 seconds at the beginning while loading the graphics, but once only the audio is left it starts to animate again. Not ideal, but it doesn’t crash. I’ll be working on getting it all on the background thread in the coming weeks.