Towards Reference-Counted Resource Management in Vortex

Now that we have advanced support for a dual rendering pipeline in Vortex, we are now shifting our attention into a completely different topic. This time we are looking into overhauling the engine’s inner resource management.

To this purpose, we are conducting experiments shifting resource and scene management from raw pointers to smart pointers. The idea of using smart pointers is to enable each resource to be automatically deleted once it is no longer referenced by any component or object. This is not to be confused with Garbage Collection. The main difference consists in that smart pointers allow us to simplify memory management without sacrificing deterministic object destruction and without incurring into the memory and CPU overhead of having the Garbage Collector kicking in to rearrange the application’s Heap at any time.

So far, this has proven a very interesting experience. We’ve bumped with some unexpected issues along the way, but we’ve also run into a couple of nice surprises. Among the problems encountered, there’s the fact that since C++ does not support out-of-the-box covariance/contravariance (in the sense C# does), exposing the polymorphic behavior of the scene objects became a little more involved that what it used to be when using raw pointers.

On the bright side, using smart pointers dramatically improves some of the roughest edges of the API. Just to pick a simple example, let’s consider image lifecycle management. Without smart pointers, Texture creation from images in Vortex might look like this:

void loadTexture()
{
  Vortex::Image* pimg = Vortex::Image::loadFromFile("./resources/texture.png");
  
  // Create Texture from image data...
  
  // loadImageFromFile transfers pointer ownership, 
  // we must therefore not forget to delete the image:
  delete pimg;
}

Using Vortex’s smart pointers, however, we can forget about the explicit delete operation:

void loadTexture()
{
  ref_ptr<Vortex::Image> pimg = Vortex::Image::loadFromFile("./resources/texture.png");
  
  // Create Texture from image data...
  
  // Image object ownership is now managed by the 
  // smart pointer. The pointer releases the data 
  // upon exiting this scope (unless the pointer is copied)
}

ref_ptr<T> is the typename we’ve given to our in-house smart pointer implementation. These allow us to easily cast inside type hierarchies as well as dropping into the raw pointer when needed, allowing us to dodge the performance tax in performance-critical code.

So far the results seem promising. We will continue experimenting moving the rest of the codebase to smart pointers and hopefully we will be able to include this along with the dual rendering pipeline in the very next version of Vortex: Vortex 2.0.