Using referenceCounters
Rene Zwanenburg via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Wed Jun 1 13:36:15 PDT 2016
On Wednesday, 1 June 2016 at 18:14:33 UTC, Begah wrote:
> I started using reference counters for my assets in my
> application :
> - Images
> - Models
> - ....
For my resource manager I started out with something similar to
what you're describing, but I eventually changed the design which
turned out to be an immense improvement so I'll just describe
that ^^
- Differentiate between the type implementing a resource, and
handles to a resource.
- Split up the resource manager, which stores the 'real' resource
for a given handle, and the cache where you look up resources by
name.
A stripped down version of the resource manager looks something
like this:
============
template ResourceManager(ResourceType)
{
struct HandleImpl
{
~this()
{
// Destroy the resource, make sure you mark the
corresponding slot in the array as free
}
size_t slot;
}
alias Handle = RefCounted!HandleImpl;
private ResourceType[] resources;
// When creating a resource add it to the manager and pass
around the opaque handle instead.
Handle add(ResourceType resource)
{
// Find an empty slot in the resources array, put the
resource there, and return a handle pointing to that slot.
}
// This should be used sparingly. Most code shouldn't care
about resource implementation. The parts that do can get at it
via this function, but shouldn't hold on to the reference longer
than necessary.
ResourceType getActualResource(Handle handle) { ... }
// This allows you to hot-swap resources while keeping the
resource implementation logically const. Hot-swapping is
extremely valuable, I've always found myself wanting to support
it sooner or later. I've tried to do it in different ways, but it
always ended up in a mess. Using handles on the other hand turned
out to be clean and easy.
void update(Handle handle, ResourceType newResource) { ... }
}
============
Note that it doesn't allow you to search for resource by name or
anything, this is just a simple mapping between handles and the
resource implementation. It doesn't store instances of the
RefCounted handles itself, so there are no issues with resources
not being cleaned up.
Now when you have this in place you can build a caching mechanism
on top of it:
============
template ResourceCache(ResourceType, alias loadByName)
{
alias Manager = ResourceManager!ResourceType;
Manager.Handle[string] cache;
Manager.Handle get(string name)
{
// Check if the resource is already in the cache, if not load
it using the loadByName function and store it.
}
void purge()
{
// the keys property allocates a new array with the AA keys,
which is important since we're modifying the AA.
foreach(key; cache.keys)
{
// This is the part you were actually interested in. It's
important to take a pointer to the handle here, and not reference
it directly. I had some issues with that in the past, I'm not
sure if that's still the case.
auto ptr = key in cache;
if(ptr.refCountedStore.refCount == 1) // If this is the
last reference
{
destroy(*ptr); // I'm not sure if this is still
necessary, there was some issue with AAs in the past. It may be
fixed today. Destroying the handle doesn't hurt either way so I
left it in.
cache.remove(key);
}
}
}
}
============
So cached resources don't get cleaned automatically if you don't
call purge, but that's usually the right thing to do anyways. For
example when you're changing scenes (I assume this is for some
kind of game?) you can simply destroy the current scene, load
another, then purge the cache, without having to reload resources
used by both scene 1 and 2.
Another upside is that this doesn't require every resource to be
named / backed by file. Resources can be dynamically generated at
runtime without special casing anywhere.
More information about the Digitalmars-d-learn
mailing list