Last time we had Scarfy cheerfully running around collecting coins. Clink clink clink! But, a problem was looming. Every coin/object was loading its own copy of the textures & sounds that it used. That's a massive waste, especially when you've got a few hundred identical looking coins.

It's increasing loading times, massively inflating memory usage, and also the memory bandwidth needed to draw everything. The game is well on its way to becoming such a resource hog that it'll choke; especially on low-end machines.

What we need is a resource manager...

What is a Resource Manager, & How does it Help Us?

A game engine's resource manager enables multiple objects to share access to resources such as textures and sound files. This allows us to have one copy of each texture, regardless of how many objects use it. So, the 200 copies of the coin, gets whittled down to one. That's huge savings!

How Does the Resource Manager Work?

It starts with external code asking for a particular resource, like the coin texture. The resource manager will check if it has a copy of the resource in its cache. If it does, then it returns a pointer to that copy. Otherwise, it loads the resource from disk, adds it to the cache, and returns a pointer to the calling code.

The resource manager will also deallocate resources once they're no longer needed (either immediately, or when instructed to do so).

Our Simple Resource Manager

We're going to build a simple resource manager. It won't be highly optimized. nor will it be thread-safe. Instead, it:

  • Can share textures & sounds between multiple objects
  • Automatically deallocates resources once they're no longer used
  • Is easy to understand

This is achieved using three C++ STL classes:

  • std::unordered_map<> - forms the resource cache. It maps filenames to the loaded resource
    std::unordered_map<std::string, std::weak_ptr<T>>
  • std::shared_ptr<> - a "smart" pointer that keeps track of how many objects are pointing to a resource
    std::shared_ptr<T>
  • std::weak_ptr<> - points to the resource, but without adding to the usage count. This enables the resource manager to store a pointer to the resource, and automatically deallocate a resource when no more external objects are using it (used in the std::unordered_map<> above)

These are combined into a ResourceManager template class:

template <class T>
class ResourceManager {

Using a template class allows us to have separate resource managers for textures, & sounds without having to write separate code for each.

The resource type-specific managers are created with simple typedefs:

/** Manages loading of textures.
 */
typedef ResourceManager<raylib::Texture> TextureManager;

/** Manages loading of sounds. 
 */
typedef ResourceManager<raylib::Sound> SoundManager;

/** Manages loading of music. 
 */
typedef ResourceManager<raylib::Music> MusicManager;

Download the code below to see the full resource manager.

With the resource manager written, the next step is to rework the rest of Scarfy's code to load textures, sounds, & music via the resource manager instead of loading them directly. This is relatively easy, and is mostly a search and replace, plus adding #include "ResourceManager.h" where needed.

With all modifications made, Scarfy now loads only one copy for each resource, drastically reducing the amount of memory required down to a sane level. Mission accomplished!

Download the Source Code

Click here to download the source code.