Since you guys are my boss, in a way, I wanted to report to you what I spent the last few days doing.
Early on in the development of Leadwerks 3, I had to figure out a way to handle the management of assets that are shared across many objects. Materials, textures, shaders, and a few other things can be used by many different objects, but they consume resources, so its important for the engine to clean them up when they are no longer needed.
I decided to implement an "Asset" and "AssetReference" class. The AssetReference object contains the actual data, and the Asset object is just a handle to the AssetReference. ("AssetBase" probably would have been a more appropriate name). Each AssetReference can have multiple instances (Assets). When a new Asset is created, the AssetReference's instance count is incremented. When an Asset is deleted, its AssetReference's instance counter is decremented. When the AssetReference instance counter reaches zero, the AssetReference object itself is deleted.
Each different kind of Asset had a class that extended each of these classes. For example, there was the Texture and TextureReference classes. The real OpenGL texture handle was stored in the TextureReference class.
Normal usage would involve deleting extra instances of the object, as follows:
Material* material = Material::Create(); Texture* texture = Texture::Load("myimage.tex"); material->SetTexture(texture);// This will create a new instance of the texture delete texture;
This isn't a bad setup, but it creates a lot of extra classes. Remember, each of the AssetReference classes actually get extended for the graphics module, so we have the following classes:
Asset Texture AssetReference TextureReference OpenGL2TextureReference OpenGLES2TextureReference
The "Texture" class is the only class the programmer (you) needs to see or deal with, though.
Anyways, this struck me as a bad design. Several months ago I decided I would redo this before the final release. I got rid of all the weird "Reference" classes and instead used reference counting built into the base Object class, from which these are all derived.
The usage for you, the end user, isn't drastically different:
Material* material = Material::Create(); Texture* texture = Texture::Load("myimage.tex"); material->SetTexture(texture); texture->Release();// Decrements the reference counter and deletes the object when it reaches zero
In the Material's destructor, it will actually decrement each of its texture objects, so they will automatically get cleaned up if they are no longer needed. In the example below, the texture will be deleted from memory at the end of the code:
Material* material = Material::Create(); Texture* texture = Texture::Load("myimage.tex"); material->SetTexture(texture); texture->Release(); material->Release();// texture's ref count will be decremented to zero and it will be deleted
If you want to make an extra "instance" (not really) of the Asset, just increment the reference counter:
Material* material = Material::Create(); Texture* texture = Texture::Load("myimage.tex"); texture->IncRefCount();// increments ref count from 1 to 2 Texture* tex2 = texture; texture->Release();// decrements ref count from 2 to 1
This makes the code smaller, gets rid of a lot of classes, and I think it will be easier to explain to game studios when I am trying to sell them a source code license.
Naturally any time you make systemic changes to the engine there will be some bugs here and there, but I have the engine and editor running, and mistakes are easy to find when I debug the static library.
I also learned that operation overloading doesn't work with pointers, which i did not know, but had never had a need to try before. Originally, I was going to use operation overloads for the inc/dec ref count commands:
Texture* tex = Texture::Create();//refcount=1 tex++;//refcount=2 tex--;//refcount=1 tex--;// texture would get deleted here
But that just messes with the pointer! So don't do things like that.