You see, something occured to me about the class 'shared_ptr in a container in the manager' way of doing things; even when all the client classes have been destroyed our texture handles are still going to be laying around and taking up memory; be it system memory with classes and texture data or both system and VRAM memory in the case of OpenGL and DX.
The problem is, the manager is still going to be holding a reference to the texturehandle classes when everything else has released it, which of course is what keeps it alive, so in order to clean up properly we need to do something so either this is cleaned up as textures are released or is handled automagically.
Now, we could have our client code tell the manager we are done with a texture, but this is underdesireable because it places a burden on all the client code to do this and somewhat goes against the automagic ideas behind RAII. If one piece of client code fails to do this, or even does it at the wrong point, then we have resources hanging around and we have failed.
So, we need another option and this is presented to us in the form of a boost::weak_ptr. This is a class which observes and allows us to retieve a boost::shared_ptr to work with or a NULL if the shared_ptr we are observing is no longer valid.
So, how do we use it? Well, instead of maintain a map of std::string to boost::shared_ptr we maintain a map of std::string to boost::weak_ptr. When we load a texture we perform the lookup as before, however if we find a match we try to get a boost::shared_ptr from the stored boost::weak_ptr, if we succeed then we pass that back to the caller, if not we reload as normal and replace the entry in the map.
This does leave one problem, as we don't know when texture handles die we could end up with a map full of invalid boost::weak_ptr. The solution to this is a 'cleanUp()' function which the client calls to remove all invalid entries.
"But surely this places a burden on the client code?" You might say, and yes you'd be right it does, however its reduced to once place in the code, not having to be duplicated in all classes which use the manager. Also, these objects will be light, so the cost in memory isn't as bad as leaving around potentially large resources as before.
The function its self doesn't need to be called often, in tradional "level" based games either before or after reload (after might be better, depending on how much textures are reused between levels). In a "streaming" game it would form part of a garbage collection pass maybe?
With these thoughts in mind our code changes slightly from the previous version;
handle_t TextureManager::LoadTexture(const std::string &filename)
texturemap_t::iterator it = texturemap.find(filename);
if(it != texturemap.end())
if(handle_t tmp = it->second.lock())
handle_t texture(new TextureHandle(id));
if(img->getFormat() == GameTextureLoader::FORMAT_RGBA)
for(texturemap_t::iterator it = texturemap.begin(); it != texturemap.end();)
So, there you have it, texturemanager v0.2 is complete, now with, in my opinion, a much improved clean up system.