Resource Manager - (Game Resource Management)

Resource Manager - (Game Development Resource Management)


#include "ResLoader.cpp"


Definition: MANAGER:

class Manager
{
public:
    
    virtual void Destroy() = 0;
    virtual ~ Manager(){}
    
public:
    
    ds::astring name;
};

Manager member variables:


ds::astring name;

name is the name of the  resource manager.



Definition: RESOURCE-MANAGER:

template< class Asset > class ResourceManager : public Manager
{
    typedef bool (*DeleteAssetFuncPtr)(Asset * asset);
    DeleteAssetFuncPtr DeleteAssetFunc;
    
public:
    
    typedef ds::dlist< ResourceLoader< Asset >* > ResourceLoaders;
    typedef std::map<ds::astring, ResourceLoaders*> ResourceLoaderMap;
    
    typedef ds::dlist< Resource< Asset >* > AssetList;
    typedef std::map<ds::astring, AssetList*> AssetMap;
    
public:
    
    void AddLoader(ResourceLoader< Asset > * resource_loader);
    ResourceLoader< Asset > * GetLoader(ds::astring const& file_ext);
    
    AssetList * GetResourceList(ds::astring const& name);
    
    Asset * Add(ds::astring const& name, Asset * asset);
    Asset * Create(ds::astring const& name);
    Asset * Remove(ds::astring const& name);
    Asset * Get(ds::astring const& name);
    
    virtual Asset * Load(ds::astring const& filename);
    virtual void Delete(Resource< Asset > const* resource);
    virtual void Delete(ds::astring const& name);
    virtual void Delete(Asset const* asset);
    
    void SetAutoDelete(bool value);
    bool GetAutoDelete()const;
    
    virtual void Destroy();
    
    
    static void CreateSingleton(res::ResourceLoader< Asset > * loader);
    static ResourceManager * GetSingleton();
    
    virtual ~ ResourceManager();
    ResourceManager(res::ResourceLoader< Asset > * loader);
    
private:
    
    Resource< Asset > * CreateNewAsset(ds::astring const& name);
    Resource< Asset > * CreateNewListWithAsset(ds::astring const& name);
    Resource< Asset > * GetAssetFrmList(Asset * asset, AssetList * list);
    Resource< Asset > * GetAssetFrmList(ds::astring const& name, AssetList * list);
    
    void InsertAssetList(ds::astring const& name, AssetList * list);
    void Add(AssetList * assetlist, Resource< Asset > * asset);
    
protected:
 
    ResourceLoaderMap resource_loaders;
    AssetMap assets;
    bool auto_delete;
};

Resource Manager member variables:


ResourceLoaderMap resource_loaders;

resource_loaders is an std::map list that store resource loaders using the loaders’ ext name as key. 

@Note  that the name ResourceLoaderMap is a typedef alias of std::map and ResourceLoaders is a typedef alias of ds::dlist< ResourceLoader< Asset >*> thus ResourceLoaderMap is equivalent to std::map*> *>.


AssetMap assets;

assets is the std::map list where all the resource data structure is stored, using the name of the resource as key. 

@Note that the name AssetMap is a typedef alias of std::map and AssetList is a typedef alias equivalent to ds::dlist< Resource< Asset >*> which is a double linked list. Thus AssetMap is equivalent to std::map*> *>.


bool auto_delete;

auto_delete is a Boolean variable use to specify whether the manager should class delete on the resource object it manages. There maybe some cases where you want to manually delete resource objects like when using it to manage scene nodes.


Resource Manager member function:


void AddLoader(ResourceLoader< Asset > * loader)

This function is use to add a loader to the resource manager. The function does the following. First check if a loader already exist that can load the the specified file the loader being added can load. If such a loader already exist, delete it and add the new loader in its place.
  • @param  (ResourceLoader< Asset > * loader) is the resource loader to add to this Resource Manager.
template< class Asset > void ResourceManager< Asset >::AddLoader(ResourceLoader< Asset > * new_loader)
{
    if ( !new_loader ) return;
    new_loader->resmgr = this;
    
    typename ResourceLoaderMap::iterator rlmi =  resource_loaders.find(new_loader->ext);
    if (rlmi == resource_loaders.end())
    {
        ResourceLoaders * reslist = new ResourceLoaders;
        reslist->push_back(new_loader);
        resource_loaders.insert( std::pair<ds::astring, ResourceLoaders*>(new_loader->ext,reslist) );
    }
    else
    {
        rlmi->second->push_back(new_loader);
    }
}


ResourceLoader< Asset > * GetLoader(ds::astring const& file_ext);

This function is use to fetch a specific loader from the ResourceManager, the parameter “string ext” is the key and the name of the file extension.
  • @param  (ds::astring const& ext) is file extension and the key of the resource loader which is used to query for a specific loader.
example: image.jpg. “jpg” would be the file ext.

template< class Asset >
    res::ResourceLoader< Asset > * ResourceManager< Asset >::GetLoader(ds::astring const& ext)
    {
        // find the list of loaders at key ext name.
typename ResourceLoaderMap::iterator rlmi =  resource_loaders.find(ext);
        // if no such list of loaders exist return null.
if (rlmi == resource_loaders.end()) { return nullptr; } else {
            // otherwise search the list for the load that correspond to ext
typename ResourceLoaders::iterator l; for (l=rlmi->second->begin(); l!=nullptr; l++) { if (l->ext == ext) return l.element(); } } return nullptr; }


AssetList * GetResourceList(ds::astring const& name);

This function get a list of resources whose key index is the string name.
  • @param  (ds::astring const& ext) is file extension and the key of the resource loader which is used to query for a specific loader.
  • @return  AssetList aka ds::dlist*>*>; which is a double linked list of Resources  who's contained template variable type is Asset.
@note the follow function is used so that i don't have to keep writing the following repetitive code.

template< class Asset > typename ResourceManager< Asset >::AssetList * ResourceManager< Asset >::GetResourceList(ds::astring const& name)
{
    typename AssetMap::iterator miter = assets.find(name);
    if (miter == assets.end())
        return nullptr;
    return miter->second;
}


Asset * Add(ds::astring const& name, Asset * asset);

This function is used to added a asset/resource to the ResourceManager. The function does the following. First check if a resource already exist that goes by that name. If a resource
already exist with that name check if the asset pointers equal to the queried resource pointer. if they are both equal the asset is not added to the manager because maintain a unique list of resources in the manager is crucial. On the other hand if the pointers are not equal the asset should be added as a resource. The asset pointer is then return.
  • @param  (ds::astring const& name) is key of the map index and name to use for the Resource data structure that will contain the asset pointer. All names given to a resource in a manager should be unique.
  • @ param  Asset * asset: is the asset to added to the manager.
  • @return  Asset * asset: the same asset in the parameter is returned.
template< class Asset > Asset * ResourceManager< Asset >::Add(ds::astring const& name, Asset * r)
{
    /* make sure name is not empty */
    //if (name.empty()) return nullptr;
    
    /* check if there exist an assetlist at 'name'. this is the list the resource is to be added to */
    AssetList * assetlist = GetResourceList(name);
    
    /* make sure the same asset is not being added more than once */
    Resource< Asset > * asset = GetAssetFrmList(r, assetlist);
    if (asset) return asset->object;
    
    /* if no such asset exist create one, and set its resource */
    asset = new Resource< Asset >(name);
    asset->object = r;
    
    /* add asset to assetlist, and assetlist to asset map */
    Add(assetlist,asset);
    return asset->object;
}


Asset * Create(ds::astring const& name);
Creates and add a resource asset to the namer and return the asset pointer.
  • @param  (ds::astring const& name) is key of the map index and name to use for the Resource data structure that will contain the asset pointer. Keep in mind that all names given to a resource in a manager should be unique.
template< class Asset > Asset * ResourceManager< Asset >::Create(ds::astring const& name)
{
    // make sure name is not empty
    if (name.empty()) return nullptr;
    Resource< Asset > * asset;
    /* check if there exist an assetlist at 'name'. this is the list the resource is to be added to */
    AssetList * assetlist = GetResourceList(name);
    
    /* make sure the same asset is not being added more than once */
    asset = GetAssetFrmList(name, assetlist);
    if (asset) return asset->object;
    
    if (assetlist)
    {
        /* if no such asset exist create one, and set its resource */
        asset = CreateNewAsset(name);
        assetlist->push_back(asset);
    }
    else
    {
        asset = CreateNewListWithAsset(name);
    }
    asset->reference_counter++;
    return asset->object;
}


Asset * Remove(ds::astring const& name);
Remove an asset from the manager.

  • @param  (ds::astring const& name) is key of the map index and name to use for the Resource data structure that will contain the asset pointer. Keep in mind that all names given to a resource in a manager should be unique.
  • @return  Asset * asset: if no asset exist that correspond to the param name, null is returned instead.

template< class Asset > Asset * ResourceManager< Asset >::Remove(ds::astring const& name)
{
    if (name.empty()) return nullptr;
// find the list the resource is in that needs removal
    typename AssetMap::iterator asset_iter = assets.find( name );
    // if no such list exist return null
    if (asset_iter == assets.end())
        return nullptr;
// otherwise find the object in list and remove it from list
    typename AssetList::iterator resrc_iter;
    for (resrc_iter = asset_iter->second->begin(); resrc_iter!=nullptr; resrc_iter++)
    {
        if (resrc_iter->name == name)
        {
            Asset * asset = resrc_iter.element()->object;
            asset_iter->second->erase(resrc_iter);
            
            // if the list has 0 resource after removal, delete the list
if (asset_iter->second->count() == 0)
            {
                delete asset_iter->second;
                asset_iter->second = nullptr;
                assets.erase(asset_iter);
            }
            return asset;
        }
    }
    return nullptr;
}



Asset * Get(ds::astring const& name);
Get an asset from the manager.

  • @param  (ds::astring const& name) is key of the map index and name to use for the Resource data structure that will contain the asset pointer. Keep in mind that all names given to a resource in a manager should be unique for this purpose.
  • @return  Asset * asset: if no asset exist that correspond to the name, null is returned instead.

template< class Asset > Asset * ResourceManager< Asset >::Get(ds::astring const& name)
{
    if (name.empty()) return nullptr;
    
    typename AssetMap::iterator asset_iter = assets.find( name );
    
    if (asset_iter == assets.end())
        return nullptr;
    
    typename AssetList::iterator resrc_iter;
    for (resrc_iter = asset_iter->second->begin(); resrc_iter!=nullptr; resrc_iter++)
    {
        if (resrc_iter->name == name)
        {
            return resrc_iter.element()->object;
        }
    }
    return nullptr;
}


virtual Asset * Load(const ResInfo* resinfo, ds::astring const& filename);
Load an asset into the manager. If the asset was already loaded, the loading is not necessary and the already loaded asset is returned instead.

  • @param  (const ResInfo* resinfo) is information/setting one might want to pass to the ResourceLoader that is responsible for loading the asset from file.
  • @param  (ds::astring const& filename) is the filename of the file to load.
  • @return  once loaded the asset is returned. If file was not found null is returned instead.
template< class Asset > Asset * ResourceManager< Asset >::Load(const ResInfo* resinfo, ds::astring const& filename)
{
    /* make sure filename is not empty */
    if (filename.empty()) return nullptr;
    /* create the loader context to pass on important data to loader */
    typename ResourceLoader< Asset >::LoaderContext context;
    /* parse the name of file from filename */    context.name = filename.parse_fname();
    /* parse the file extension from filename */
context.ext = context.name.parse_fext(); /* check if the asset is alreay loaded. to do this we first * get the assetlist which the asset is contained */ context.reslist = GetResourceList(context.name);
    /* once we have the list get the asset from that list */
    if (context.reslist)
    {
        Resource< Asset > * asset = GetAssetFrmList(context.name, context.reslist);
        /* if an asset was return, this mean the object was already loaded */
        if (asset)
        {
            /* increase the reference count to indication that more than one objects using this resource. and return the asset */
asset->reference_counter++; return asset->object; } } // everything after this line means that the asset was not found. // thus we must create the asset and load it. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - res::ResourceLoader< Asset > * loader = GetLoader(context.ext); // if no loader exist to load the asset with file ext, return null since such a file can not be loaded. if ( !loader ) return nullptr; /* if a list to store the new asset doesnt exist. create the list * and add the list to this resourse manager.*/ if ( !context.reslist ) { context.reslist = new AssetList; InsertAssetList(context.name, context.reslist); } /* now we create and load the asset and return it */ return loader->Load(context, filename); }


void Delete(Resource const* resource);
Delete an resource from the manager.
  • @param  (Resource const* resource) is resource to delete from the manager.

template< class Asset > void  ResourceManager< Asset >::Delete(Resource< Asset > const* resource)
{
    Delete(resource->name);
}


void Delete(ds::astring const& name);
Delete an asset from the manager that correspond to the given name.


  • @param  (ds::astring const& name) is name of the asset to delete.
template< class Asset > void ResourceManager< Asset >::Delete(ds::astring const& name)
{
    typename AssetMap::iterator map_iter = assets.find( name );
    
    if (map_iter == assets.end())
        return;
    
    typename AssetList::iterator asset;
    for (asset = map_iter->second->begin(); asset!=nullptr; asset++)
    {
        if (asset->name == name)
        {
            asset->reference_counter--;
            
            // if asset reference_count >= 1 exist since it is still in use by another object.
            if (asset->reference_counter >= 1) break;
// at this point reference_count is either <= 0 thus must be deleted
            if(auto_delete)
            {
                delete asset->object;
                asset->object = nullptr;
            }
            map_iter->second->erase( asset );
            break;
        }
    }
    if (map_iter->second->empty())
    {
        delete map_iter->second;
        assets.erase(map_iter);
    }
}


void Delete(Asset const* asset);
Delete an asset from the manager.
  • @param  (Asset const* asset) is asset to delete from the manager. Tho this is an in-efficient way of deleting an asset because one has to search the entire list until the asset is found. Only use this method when you don't know the name of an asset. it could be made more efficient but that would mean using more memory to store a pointer and name.

template< class Asset > void ResourceManager< Asset >::Delete(Asset const* r)
{
    typename AssetList::iterator l;
    typename AssetMap::iterator m;
    for (m=assets.begin(); m!=assets.end(); m++)
    {
        for (l=m->second->begin(); l!=nullptr; l++)
        {
            if(l->object == r)
            {
                l->reference_counter--;
                
                // if asset referance count drop to zero or below it shoud be deleted
                if (l->reference_counter >= 1) break;
                
                // delete the asset and remove it from the list of assets
                delete l->object;
                l->object = nullptr;
                delete l.element();
                m->second->erase(l);
                
                if (m->second->empty())
                {
                    delete m->second;
                    assets.erase(m);
                }
                return;
            }
        }
    }
}


void SetAutoDelete(bool value);
Set auto_delete boolean member variable to true if one wants the manager to call delete on  the asset when a resource is being deleted.

  • @param  (bool value) the value to set auto_delete to.


bool GetAutoDelete()const;
return the state of auto_delete.
  • @return  true of false.



TemplateResourceList * ResourceManager< Asset >::GetResourceList(ds::astring const& name)
{
    typename AssetMap::iterator miter = assets.find(name);
    if (miter == assets.end())
        return nullptr;
    return miter->second;
}

AssetList * GetResourceList(ds::astring const& name);
find and return the resource list that correspond to the string name.
  • @return  AssetList or nullptr.




Resource< Asset > * CreateNewAsset(ds::astring const& name);
this function is used to create a new resource add it to a list and return a pointer to that resource.
  • @return  pointer to the newly created resource.
template< class Asset > Resource< Asset > * ResourceManager< Asset >::CreateNewAsset(ds::astring const& name)
{
    /* if no such asset exist create one, and set its resource */
    Resource< Asset > * asset = new Resource< Asset >(name);
    asset->object = new Asset;
    asset->name = name;
    return asset;
}

Resource< Asset > * GetAssetFrmList(ds::astring const& name, AssetList * list);
this function is used to make life easier. A name and a list is pasted to the function, then function then use the name to search the list for the resource that correspond to the name.
  • @param  (ds::astring const& name) the name of the resource.
  • @param  (AssetList * list) is the list to search with the given name.
  • @return  a resource or return nullptr if none was found in the asset list.
template< class Asset > Resource< Asset > * ResourceManager< Asset >::GetAssetFrmList(Asset * r, AssetList * assetlist)
{
    if (!assetlist) return nullptr;
    typename AssetList::iterator iterator;
    for (iterator = assetlist->begin(); iterator!=nullptr; iterator++)
    {
        if (iterator->object == r)
        {
            return iterator.element();
        }
    }
    return nullptr;
}


void InsertAssetList(ds::astring const& name, AssetList * list);
store an asset list at the index (name) in the map.
  • @param  (ds::astring const& name) is the key for the index in the map.
  • @param  (AssetList * list) is the list to store at that index.
template< class Asset > void ResourceManager< Asset >::InsertAssetList(ds::astring const& assetname, AssetList * assetlist)
{
    assets.insert( std::pair<ds::astring, AssetList*>(assetname,assetlist) );
}


void Add(AssetList * assetlist, Resource< Asset > * asset);
add the resource to the list if the list is not nullptr. if the list is nullptr create a new list, add the resource to the new list and insert that new list into the map.
  • @param  (AssetList * assetlist) is the asset list to use.
  • @param  (Resource< Asset > * asset) is the resource to add to the asset.
template< class Asset > void ResourceManager< Asset >::Add(AssetList * assetlist, Resource< Asset > * asset)
{
    /* add asset to assetlist */
    if (assetlist)
    {
        assetlist->push_back(asset);
    }
    else
    {
        assetlist = new AssetList;
        assetlist->push_back(asset);
        InsertAssetList(asset->name, assetlist);
    }
}


void Destroy();
delete all resources. delete all asset is auto_delete is enabled.

template< class Asset > void ResourceManager< Asset >::Destroy()
{
    typename AssetList::iterator l;
    typename AssetMap::const_iterator m;
    for (m=assets.begin(); m!=assets.end(); m++)
    {
        for (l=m->second->begin(); l!=m->second->end(); l++)
        {
            if (auto_delete)
            {
                delete (*l)->object;
                (*l)->object = nullptr;
            }
        }
        m->second->clear();
        delete m->second;
    }
    assets.clear();
    
    if (singleton == nullptr)
    {
        delete singleton;
        singleton = nullptr;
    }
}


Destructor

template< class Asset > ResourceManager< Asset >::~ ResourceManager()
{
    Destroy();
}


Constructors

template< class Asset > ResourceManager< Asset >::ResourceManager(res::ResourceLoader< Asset > * loader)
:   pool(nullptr)
,   DeleteAssetFunc(nullptr)
,   auto_delete(true)
{
    AddLoader(loader);
}


template< class Asset > ResourceManager< Asset >::ResourceManager(DeleteAssetFuncPtr DeleteFunc, res::ResourceLoader< Asset > * loader)
:   pool(nullptr)
,   DeleteAssetFunc(DeleteFunc)
,   auto_delete(true)
{
    AddLoader(loader);
}


continuation article -> Core Resource Manager - (Game Development Resource Management)

Comments