Caching textures and other data

Ector of Medieval

In this tutorial, I'm going to tell you how a software cache works, and how it can be used for different types of data. For simplicity, I'll talk about a texture cache as an example, but the same technology can be applied to many kinds of data, such as sound samples etc. Also, all code is going to be (pseudo) C, even though I'm a C++ guy.

I'm going to begin by telling you what caches can be used for. Let's say several different parts of you demo/game/whatever want the same bitmap. Each of them will have to load it and keep a copy of it:

Bitmap *pic = load_png("my_bmp.png");

This means that the bitmap will be loaded into several places in memory (which of course is a waste, and may trash processor cache performance), and that your program will waste time decompressing the same image several times. Or you will have to use lots of globals, which is not good as we all know. :)

So what do we do about it? We create a cache. Then, instead of loading the bitmap from every function or class that needs it, we request a pointer to it from the cache. If the image isn't already loaded in the cache, it loads it and puts it in a free slot. This is what a cache could look like:

    struct TexCacheEntry {
      char filename[64];
      Bitmap *texture;
    };

    TexCacheEntry texcache[MAX_TEXTURES];
    int texcachesize=0;

    Bitmap *RequestTexture(char *filename) {
      for (int i=0; i<texcachesize; i++) {
        if (strcmp(filename,texcache[i].filename)==0) { //if the names are equal...
          //we got it!
          return texcache[i].texture;
        }
      }
      //haven't got it yet? Then load it and put it in the list
      if (texcachesize<MAX_TEXTURES) {
        Bitmap *tex;
        strcpy(texcache[texcachesize].filename,filename);
        tex = load_png(filename);
        texcache[texcachesize].texture = tex;
        texcachesize++;
        return tex;
      }
      else {
        return 0;
      }
    }

That's it!

Then, whenever something wants a texture, for example:

blit(RequestTexture("mytexture.png"),buffer);

You may argue that searching the list every frame can take time, but it's usually negligable as it's not called very often. You're better off optimizing your fillers instead if it's speed you want.

If some textures are used seldom, you could throw them away if they haven't been requested for say 20 seconds. You could also write an Unload() function so that this can be done manually when you know that a texture isn't needed anymore. And this isn't dangerous at all, because should it be needed again, the texture cache manager ensures that it'll be loaded back into the list! Beware of loading/unloading too often though, it can kill performance.

The cache approach is also good for sound effects in games for example. With it you can do stuff like this:

playsound("crash.wav");

without it wasting any memory space at all! (apart from the tiny list of course).

Well, I guess that's about it.

-Ector of Medieval (Ector^Mdl)
henrik_83_@hotmail.com