Game Design, Programming and running a one-man games business…

Basic principles of game optimisation

Making games run faster is a pet topic of mine. Code samples are too specific to help many people, so here are some general principles I’ve learned.

1) Design to avoid the code.

Sounds horrid doesn’t it, but people do it all the time. ‘Skinned’ animation at points like shoulders and knees is CPU intensive and tricky. Ever noticed how many characters in games have big shoulder-pads? Makes life much easier. GSB is a 2D game, mainly because I prefer 2D gameplay, but I can’t ignore the fact that it would be horrendously more draining on the CPU/GPU to be in 3D

2) Run the code offline.

If the output of your code is a one-off, only ever do it once, and before you ship the game. The GSB campaign game code has some really inefficient slow brute force stuff for calculating planet-travel distances. It’s done as a one-off, and the results loaded in at run time. If there are calculations your game makes at run time that are based on data that never changes, just pre-calc them and load in a table, assuming the table is small enough

3) Run the code once, and cache it.

This is the ultimate big win. In the old days, people used tables of cosine/sine lookups, rather than calculating them. Hold on! GSB does that for some stuff too (nothing gameplay critical). Never make a calculation twice in a  function if you can do it once and reference it later. It’s amazing what a difference this makes. even simple stuff like STL list end() calls can mount up with really large containers

4) Don’t run the code every frame.

Not much code has to be run every frame. The minimap in GSB isn’t updated every frame. Can you notice? I can’t, because most frames a ships movement would be ‘sub-pixel’ anyway. If stuff like this seems to jitter, update half the code one frame, the other half the next frame. A frame should be 1/60th of a second. Nobody will notice.

5) Don’t process what isn’t seen.

GSB does some cunning code for all kinds of cosmetic things that only applies if that activity happens onscreen. In theory you can pause the game, scroll around and spot very minor inconsistencies (good luck). With strategy games, most of the time, most of the action isn’t being viewed, so you can skip allsorts of stuff. I don’t update timers for running lights offscreen, for example.

6) Re-order and group stuff.

Graphics cards like to do things in big clumps. give them a single texture, and 50 sprites to draw and they are happy. Give them 50 sprites with different textures and they are not. This is why all the GSB debris is clumped into a single texture, ditto the laser bolts. I also re-order ships in the game code so that similar ones are drawn one after the other, even when drawn as icons. This minimizes texture swaps. Because GSB doesn’t use a z-buffer(for blending reasons), that makes for some spaghetti like code, but it’s worth it. The easiest system I’ve found is to maintain spritelists for different textures. You still draw sprites as normal, but when they draw they just get thrown into the relevant list, and the list gets drawn later.

7) Save stuff for when you are not busy

GSB does this. It’s very tricky, but you can have code that only runs when the CPU is idle. If a laser bolt hits some debris in GSB, the debris explodes. This ONLY happens if the CPU is idling. People spot it occasionally and think it’s l33t. They never spot that it doesn’t happen all the time.

8) Optimize the UI

People get a fast engine then slap some super-slow UI on top. Madness! With a lot of text, icons, dialog box backgrounds and multi-part buttons, you can have crazy overdraw, crazy texture-swaps and huge inefficiency. Keep an eye on it.

Don’t forget that you can also optimize textures too. Some textures are ripe for compression, others not, but you can also do crazy tricks with some stuff, like chop a circle texture into one quarter, and raw it as 4 flipped and mirrored sprites (I do that a lot). Not everyone has 512 MB video cards, and its quicker to load a small texture than a big one anyway.

Most coders probably know all this already, but it doesn’t hurt to recap. It’s easy to forget about optimising and just worry about features, but gamers will thank you for it. I get a lot of people remarking how surprised they are at how well GSB runs on their machine. This is no accident :D.


15 thoughts on Basic principles of game optimisation

  1. Nice list. 7 is a cool idea. Shame my engine always eats 100% of resources. :(

    Can I add one?

    Avoid memory allocation or deallocation. Apart from at the start and end of the program. I’m not talking about a l33t memory manager. Free-lists and pools of memory preallocated in manager classes save me a lot of time and effort. Its perhaps wasteful having it all sit there unused, but asking the OS for memory over and over can murder the speed of anything. Its not like we have many Ram starved systems nowadays.

  2. Yes, well said. I’m very poor at that to be honest, except for the obvious stuff like debris or particles. I should take a closer look at GSBs mid-battle allocation.

  3. Good list, but point 3 is not the whole story however, since cache misses can be so expensive that often it’s actually faster to re-calculate something rather than to cache it ..

    So another point in the list should be; design your data structures to avoid cache misses.

  4. Hi Cliffski,

    What about the 80:20 rule; Only 20% of your code will need optimizing and only optimize when you have a bottleneck and always profile first to find out where the slowdown is.

    Do you use profiling tools for the gpu and cpu?

    Do you use seperate processes or threads on multi-core systems?

    Kind Regards

    Al
    Arowx.com

  5. I don’t do much in the way of multithreading, because of the complete chaos it can bring to debugging, especially across multiple hardware configurations.
    I always think that just because you can spot the slow 20% on your machine, it doesn’t mean its true for all machines. A PC with half my RAM but a faster GPU may behave very differently, ditto one with a massively fragmented hard drive. I carry out minimal optimising practically everywhere, just in case.
    I just hate slow inefficient code :D

  6. good list – thanks for sharing your experiences with those.
    ‘Dont do stuff every frame’ is one I use lately. The idea that every tiny thing needs to be checked/updated/redrawn every frame is not required.

  7. Yeah, that UI one is really mean. Usually you add your UI piece by piece, everytime you finish some new feature, you add a button or graphic for it. And each of them is terribly slow, but you never realize, since each only makes the game a bit slower than before. But added up, half your fps goes down the drain. Extreme example: Text. The most trivial way to do it is to have a single texture per letter. But that often means that you have to send 50 chunks of tiny data to the graphics card, just to render 50 letters. That takes longer than rendering a couple thousand polygons (or probably more) with the same texture on modern cards.

    As for C++ in MFC: Try to avoid allocating tons of tiny objects. Each one burns around 80 bytes extra in overhead. If your objects are only 4 bytes in size, that’s 95% wasted space. Which means 95% cache goes unused. And allocating those 80 bytes takes a few cycles too. You’re better off by allocating a big block and then subdividing it. Though that is not very simple and probably not something you want to do first.

Comments are currently closed.