www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Using referenceCounters

reply Begah <mathieu.roux222 gmail.com> writes:
I started using reference counters for my assets in my 
application :
  - Images
  - Models
  - ....

Such as :

alias ModelType = RefCounted!Model;

struct Model
{
	static ModelType create(Mesh mesh, Shader shader) {
		ModelType model = ModelType();

		model.mesh = mesh;
		model.shader = shader;

		return model;
	}
}

I worked fine and compiled, but now i also want to keep a 
reference of the created models :
static ModelType[string] loadedModels;

static ModelType load(string modelName) {
	ModelType model = null;
		
	switch(extensions) {
		case "obj":
			model = loadObjModel(modelName, shader);
			break;
		case "dae":
			model = loadColladaModel(modelName);
			break;
		default: abort(extensions ~ " not supported model file format");
	}
		
	loadedModels[modelName] = model; // This creates a reference, so 
i need to get rid of it
	model.refCountedStore()._store._count--; // Something like that
	model.modelName = modelName;
	
	return model;
}

So i create load a model, and the reference counter is at one ( 
normal ).
Now i store this model in the associative array but i do not want 
it to count in the reference count. Unfortunatly _store is 
private.
How can i go about making the reference in the associative array 
be canceled?
Jun 01 2016
next sibling parent Begah <mathieu.roux222 gmail.com> writes:
I can see two option but neither of them is really portable :

I can set _store public in std.typecons or i could create a 
setter method.
Neither of these options is portable because i need to directly 
edit the librarie's source code so i can't jump from one computer 
to the next without having those changes.
Jun 01 2016
prev sibling parent Rene Zwanenburg <renezwanenburg gmail.com> writes:
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.
Jun 01 2016