For the sake of this article I define a resource as an abstract object that cannot or should not be created, destroyed or manipulated directly like you would access a variable. Usual examples are objects that are associated with “external memory” like files or GPU-textures.
Many game engine do not allow access to resources by (raw/ref-counted/etc.) pointers to class interfaces, but through special resource handles that don’t have any special member functions at all. A resource handle is usually an identifier which is used together with a resource manager to either:
- perform operations on or with it
- temporarily retrieve an interface to the resource (pointer to a class instance) that represents or even holds the actual data
I think these two two possibilities are important to distinguish. I call them indirect and direct resource access (beware! I made these names just up). Note that you can easily map both access types to each other, so in the end it is more about programming interfaces!
The indirect resource access is fairly common, especially in non-object-oriented languages like C, where it is not possible to give pointers to resource-interfaces. A good example for such indirect access handles are C’s file-handles.
Either way, why should one use resource-handles in object oriented languages at all?
- The resource system can change how a handle is resolved. This is useful for:
- resource fallbacks
- uncertain memory location
- might even be used for detail levels
- They act similar to weak pointer
- Resource can be destroyed explicitly:
Subsequent queries (direct access) or function calls (indirect access) at the resource manager notify the user that the resource does not exist
It is all about moving power from away from user handled “resource instances” to a resource system which has global knowledge.
If you cannot make use of the mentioned advantages, just go with the normal object oriented approach where you create/destroy/manipulate at any time without special supervision. The disadvantages of a handle system are rather obvious:
- Additional indirection
- [only direct] Resource system can be circumvented by just asking only once for the resource and keeping it then
Oh so there is a disadvantage for the direct resource system but none for the indirect? Not so fast: Giving a resource object/interface to the user allows much nicer usage and can leverage polymorphism. In contrast, the indirect system needs to have all functionality in the manager which can be very unnatural and inflexible.
Of course all this is very simplified and general. In some situations a hybrid might be useful as well!
More infos about “indirect” handle-based resource managers can be found for example here. An example for a “direct” handle-based resource manager can be found in the ezEngine.
Now to the practical usage in ClearSight, my current C# rendering framework (as a precaution I stopped calling it “Engine”😉 ). So far I have identified two places which are in need for some kind of resource system with different properties.
- (low-level-ish) Renderer
- ensure that resources that are still in use by the GPU (!) are not deleted immediately
- few different resource operations
- resource provides very few operations itself
- creation can be assumed to be very fast; only states are thus either existing or not existing
- higher level asset system
- load by lookup (do not reload loaded resources)
- many and complex resource operations
- possible multi-threaded loading processes, different loading states
Since in C# any object that is still referenced somewhere cannot be deallocated, the advantage of a weak-pointer-like behaviour is irrelevant. Needless to say that all resources should be deallocated explicitly and implement thus IDisposable. Therefore, the Dispose method can decide if a resource is actually allowed to be destroyed.
This ultimately means that, while I need a thin resource management in the renderer, I do not need resource handles in that place. However, I will likely use a direct handle-based resource system later on top, where a single resource might compromise one or more renderer resource and additional data.