Note: this article was originally found at Tip Of The Day in Flipcode.
Sebastian Wagner started an interesting thread on this topic, but my response got long enough that I think a totd is more appropriate.
I will explain some of the things we did in Commandos 2 and Praetorians in order to speed up load times. Notice that in Praetorians, some levels will load in one or two seconds, with five seconds maybe for the largest, so for the most part the load time.
Have a correct mindset: Windows’ memory allocation or file handling are not a problem, the slow DVD reads and seek times are not a problem. The way YOU use those things can be a problem, however. They are just part of the environment you have to work with.
Design the file formats that your game loads with engine speed in mind. This usually means, lay out in a piece of paper what the engine needs and the order in which it needs it, and then turn that into a file format. NEVER use your tool’s file format for the actual game. We’re using XML for simple data files in our next engine, but I’m curious to see if we will run into trouble with speed.
Avoid any processing during loads, that process can probably be done beforehand and stored.
Have a centralized FileSystem module in your core engine, and use that to access files. NEVER allow direct access to files using other means.
Ensure that you fseek() as little as possible, and don’t have to seek backwards at all. Issue a warning on your textlog (you have one, don’t you?) every time a seek goes backwards.
Group and compress your datafiles into a pack file; if a file exists outside the packfile, it is used instead, to ease development. Praetorians’ packfiles are just ZIP files, like Quake’s, with (optional) encryption on top. Since you never seek backwards, you can decompress on the fly with a small buffer (we used 32K) without worries. Of course, decompressing must be faster than loading, so don’t use a compressor with long decompression times. For example, some of our graphics are DXTC compressed even if we don’t actually use DXTC textures.
Ensure that your pack files support uncompressed, unencrypted files with as little processing and memcpy’ing around as possible. This will allow you to do seamless loading on the background. In Praetorians, we do not load any sounds until the level is loaded and playable, at that point we start a background sound loading process with priorities, requests and all that stuff (this is a topic on its own).
Avoid loading from several files at the same time. For example, if you have a file that contains a list of sounds, load the file, then iterate the list to load the actual sounds, and then drop the list. This helps with hard drives, but it is absolutely crucial for DVDs.
Add a little filename logger so you can see in which order are files being loaded, so you can place them consecutively in the packfile and reduce seek times even further.
Consider redundancy in your data files. For the PSX research port of Commandos 2 we started, we needed to load the scenery bitmaps on the fly, so we did a background process to load tiles as the player was scrolling. We quickly saw that for 8-way scrolling, we would typically need to load a new row and a new column of tiles, so we stored the maps twice, one in row-major order and another in column-major order, so we only needed to issue two seeks and two reads. The PSX port of the game was obviously stopped at some point (it was hard enough to cram it into the PS2!!), but this optimization made it into the final game.
Make quick loading speeds a matter of pride. Despite our lighting fast load times, we considered optimizing the load when the level or civilizations you’re reloading are already in memory. This was low priority and never got done, but the important thing is that we wanted to go that far. Someone mentioned to me once that long load times helped extend the play hours a game gives you, and reduced the “quicksave-try-die-retry” dumbness that some players went into and forced them to actually plan their game actions, but I think this is NOT a good mindset. 🙂
Profile, profile, profile. Profile both in Release and Debug mode, load from the network, burn your installed game into a CD and load from there as if it was the hard drive. Try the absolutely worst cases: you will be surprised what you find:
In Praetorians, after putting out the singleplayer game, we noticed that a good 30-50% of our load times were spent searching for the file in the packfile directories. We hadn’t noticed this before because our dev machines had most of the data files outside of packfiles, and even with packfiles, the load time was fairly fast in Release mode. However, when one of the guys started running with the final packfiles, he noticed the slower times and did a little profile. With a couple hours’ work adding a little tree structure to the filename lists, this search time disappeared.
In Commandos: Beyond The Call Of Duty, my last two weeks were spent optimizing code around (I didn’t work on the game itself). The two major pieces were save and load times: I was able to cut them in more than half just by profiling and applying common sense! The funkiest point was to find that the progress bar was being updated too frequently, and adding a plain if() saved a ton of time.
Try to do a little work so your loading progress bars actually reflect the time spent: seeing a progress bar jump from 50% to 90% and then staying there for a long time is annoying.
Don’t load your levels twice, like the game Sin did. 😉
All this work will not turn a bad game into a good one, of course, but people will perceive it as more polished, and you will be more ready for console work, where it DOES matter.