Resource management is a subject i'm quite interested in as it seems to be one aspect that is often neglected in games.
Here are some of my experiences with resouce management.
One of the most important parts of resource management is how you read the files from disk into ram/vram, most big games wrap multiple files into a single file, this is a great method to avoid some overhead but it is not suitable for use during development when files often change, and can be cumbersome in say for example when doing resource hot swapping.
I enjoy having my files directly on the disk and hot swapping everything i can. To prevent some of the overhead and conflicts when multithreading i use a resource manager that starts of by silently building a manifest of every file under the executable, and takes note of their size. This has a few benefits from analyzing a file on the fly, consider for example loading a mesh, the thread doing the request can simply request the meta data of a file and directly allocate enough video memory for staging, then hand over the pointer along with an optional lambda function with the request. All file loading operations are then concentrated in a specific thread managed by the resource manager. In another thread the type of resource is monitored and detected for changes, a change is detected, and the thread runs the lambda function in a proper / safe context. This way you can localize specific and unique loading code for a resource, nothing else needs to have access to file reading operations and deal with blocking issues.
Here's a demonstration snippet for a convenience function that loads simple shaders:
Resource<> is wrapper around a pointer that is initialized using an identifier, in this case it's the path of the shaders. The identifier is used by the resource manager to bind the identifier with a point in memory inhabited by two objects, one is the ResourceMetaData, a struct keeping track of data relating to the file and operational status, the other is an object of the type passed as a template argument. All future resource requests using the same identifier will also point to this memory.
Resource hot-swapping is very powerful for rapid iteration, something i'm a big fan of:
Over the years i've had many methods of detecting file changes, on windows the by far the best method was using ReadDirectoryChangesW, changing resources can be tricky depending on the type, but usually it's very simple to just replace it:
Another aspect of resource management is adapting individual resources to better formats that cannot be evaluated in advance, one obvious example is a dynamic texture atlas, when rendering gui/hud it can sometimes be tricky, if not impossible, to know what you need in advance, texture arrays have limitations, bindless is not always available, and one draw call per element is out of the question. A dynamic texture atlas can reduce the entire problem into a simple and automatic formulation, avoiding the need for tools/artists to bother with generating/rebuilding texture atlases.
A texture atlas for interface purposes allow us to treat everything the same, here symbols are seemlessly inserted into text without any additional effort (apart from some routine initializations):
Different fonts works too:
Dynamic texture atlases is something i would personally recommend implementing as it removes a lot of potential work and worry: