Ogre源代码浅析——资源管理逻辑结构(二)

       创建好的资源对象指针会被保存在两个地方,第一处是在相应的ResourceManager的mResources容器中或mResourcesWithGroup容器中;第二处是在ResourceGroupManager的ResourceGroup中的loadResourceOrderMap中。

       在之前的ResourceGroupManager::createDeclaredResources()代码中可以看到资源对象由ResourceManager对象调用ResourceManager::create()来创建,此函数又会调用自身的addImpl() 成员函数来保存生成的资源对象的指针,以下是它的代码:

 1 void ResourceManager::addImpl( ResourcePtr& res )
 2 {
 3     OGRE_LOCK_AUTO_MUTEX
 4 
 5     std::pair<ResourceMap::iterator, bool> result;
 6         
 7     if(ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(res->getGroup()))
 8     {
 9         result = mResources.insert( ResourceMap::value_type( res->getName(), res ) );
10     }
11     else
12     {
13         ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(res->getGroup());
14 
15         // we will create the group if it doesn't exists in our list
16         if( itGroup == mResourcesWithGroup.end())
17         {
18             ResourceMap dummy;
19             mResourcesWithGroup.insert( ResourceWithGroupMap::value_type( res->getGroup(), dummy ) );
20             itGroup = mResourcesWithGroup.find(res->getGroup());
21         }
22         result = itGroup->second.insert( ResourceMap::value_type( res->getName(), res ) );
23 
24     }
25 
26     if (!result.second)
27     {
28         // Attempt to resolve the collision
29         if(ResourceGroupManager::getSingleton().getLoadingListener())
30         {
31             if(ResourceGroupManager::getSingleton().getLoadingListener()->resourceCollision(res.get(), this))
32             {
33                 // Try to do the addition again, no seconds attempts to resolve collisions are allowed
34                 std::pair<ResourceMap::iterator, bool> insertResult;
35                 if(ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(res->getGroup()))
36                 {
37                     insertResult = mResources.insert( ResourceMap::value_type( res->getName(), res ) );
38                 }
39                 else
40                 {
41                     ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(res->getGroup());
42                     insertResult = itGroup->second.insert( ResourceMap::value_type( res->getName(), res ) );
43                 }
44                 if (!insertResult.second)
45                 {
46                         OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the name " + res->getName() + 
47                             " already exists.", "ResourceManager::add");
48                 }
49 
50                 std::pair<ResourceHandleMap::iterator, bool> resultHandle = 
51                         mResourcesByHandle.insert( ResourceHandleMap::value_type( res->getHandle(), res ) );
52                 if (!resultHandle.second)
53                 {
54                         OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the handle " + 
55                             StringConverter::toString((long) (res->getHandle())) + 
56                             " already exists.", "ResourceManager::add");
57                 }
58             }
59         }
60     }
61     else
62     {
63         // Insert the handle
64         std::pair<ResourceHandleMap::iterator, bool> resultHandle = 
65             mResourcesByHandle.insert( ResourceHandleMap::value_type( res->getHandle(), res ) );
66         if (!resultHandle.second)
67         {
68                 OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Resource with the handle " + 
69                     StringConverter::toString((long) (res->getHandle())) + 
70                     " already exists.", "ResourceManager::add");
71         }
72     }
73 }

       mResource被称为“global pool”;mResourceWithGroup则被称为“group pool”。从前面的讨论可知,Ogre的资源对象一般都从属于一个ResourceGroup,每个ResourceGroup类对象有一个成员变量——bool inGlobalPool,在默认情况下生成的ResourceGroup对象的这个值会被设为true。一个资源对象在被创建后,会根据它所属ResourceGroup的inGloablaPool的真值来确定是被保存在ResourceManager的“global pool”中还是“group pool”中。从上面7-10行代码可知,在默认情况下(inGlobal==true),资源对象指针是被保存在“gloabl pool”中的。如果资源要保存到“group pool”中,则稍有些麻烦,这是由mResourceWithGroup造成的,以下是它的定义:

typedef HashMap< String, ResourcePtr > ResourceMap;
typedef HashMap< String, ResourceMap > ResourceWithGroupMap;
...
ResourceWithGroupMap mResourcesWithGroup;

      可以看到mResourcesWithGroup用了一个二级散列,这是为了减小collision的概率,但无论如何对mResourcesWithGroup来说,collision始终是可能发生的。如果一个资源对象指针要被保存到“group pool”中,Ogre就会先试着直接存入(13-22行),一旦在存入过程中发生collision(26行),Ogre会先试着向ResourceGroupManager的当前ResourceLoadingListener对象发送“resourceCollision”消息(29-31行),如果发送成功,Ogre会再次重新偿试存放此资源对象指针,如果仍然要被保存到“group pool”中且仍然发生collision,那么Ogre将会抛出弃常并打log(34-48行)。

      资源对象指针在被成功地存入到“group pool”或“global pool”后,Ogre还会以对象句柄为key,将其在mResourcesByHandle容器中再做一个备份(50-51行及64-65行)。这样做的目的显然是为了提高资源访问的效率。下面是相关的定义:

typedef unsigned long long int ResourceHandle;
...
typedef map<ResourceHandle, ResourcePtr>::type ResourceHandleMap;
...
ResourceHandleMap mResourcesByHandle;

       以上就是资源对象指针在ResourceManager中的存储过程。

      在前面列出的ResourceGroupManager::createDeclaredResources()代码中可以看到,ResourceManager在创建了相应的资源对象并对其加以保存后,紧接着会把此资源对象的指针保存到其所属的ResourceGroup的loadResourceOrderMap中。 之前的ResourceGroup结构声明中已看到了loadResourceOrderMap定义,这里再专门列出其定义相关的所有代码:

typedef list<ResourcePtr>::type LoadUnloadResourceList;
...
typedef map<Real, LoadUnloadResourceList*>::type LoadResourceOrderMap;
LoadResourceOrderMap loadResourceOrderMap;

      loadResourceOrderMap是一个以实数为key的map容器,这个key是用来区分资源类型的。通过前面的讨论可知,一个group包含了各种类型的资源对象,当以group为单元对这些资源对象进行操作时,如何对资源进行分门别类地管理就成了必须的了。Oger在ResourceManager中定义了一个变量,Real mLoadOrder,从ResourceManager中派生的资源管理器在创建时,会初始化这个mLoadOrder变量。各资源管理器的这个值是固定的,而且各不相同,具体值可以在相应的资源管理器的构造函数中找到。下面列出其中的一部分:

TextureManager      mLoadOrder=25.0
GpuProgramManager   mLoadOrder=50.0
MaterialManager     mLoadOrder=100.0
CompositionManager  mLoadOrder=110.0
FontManager         mLoadOrder=200.0
SkeletonManager     mLoadOrder=300

     资源对象指针在被保存到所属的ResourceGroup中时,会先根据mLoadOrder值在mLoadOrder中找到相应的list(如果没找到,就直接创建一个),然后将资源对象指针保存到此list中去。这样一来,ResourceGroupManager对象在进行资源访问时,就可以先通过mLoadOrder找到相应类型的资源list,然后再遍历此list找到需要的资源对象了。

-----------------------------------------------------------------------------------------------------------------------------------

     存疑:在ResourceGroupManager::createDeclaredResources()中有以下调用:

ResourceManager* mgr = _getResourceManager(dcl.resourceType);
// Create the resource
ResourcePtr res = mgr->create(dcl.resourceName, grp->name,
                dcl.loader != 0, dcl.loader, &dcl.parameters);

     其中ResourceManager的create方法为:

ResourcePtr ResourceManager::create(const String& name, const String& group, 
        bool isManual, ManualResourceLoader* loader, const NameValuePairList* params)
{
    // Call creation implementation
    ResourcePtr ret = ResourcePtr(
            createImpl(name, getNextHandle(), group, isManual, loader, params));
       if (params)
           ret->setParameterList(*params);

    addImpl(ret);
    // Tell resource group manager
    ResourceGroupManager::getSingleton()._notifyResourceCreated(ret);
    return ret;

}

      而被调用的ResourceGroupManager::_notifyResourceCreated(ret)又会调用ResourceGroupManager::addCreatedResource(),其代码为:

void ResourceGroupManager::addCreatedResource(ResourcePtr& res, ResourceGroup& grp)
{
        OGRE_LOCK_MUTEX(grp.OGRE_AUTO_MUTEX_NAME)
    Real order = res->getCreator()->getLoadingOrder();

    ResourceGroup::LoadResourceOrderMap::iterator i = grp.loadResourceOrderMap.find(order);
    LoadUnloadResourceList* loadList;
    if (i == grp.loadResourceOrderMap.end())
    {
        loadList = OGRE_NEW_T(LoadUnloadResourceList, MEMCATEGORY_RESOURCE)();
        grp.loadResourceOrderMap[order] = loadList;
    }
    else
    {
        loadList = i->second;
    }
    loadList->push_back(res);
}

     也就是说,在调用ResourceManager::create()时,会将所创建的资源对象指针保存到loadResourceOrderMap中,而ResourceGroupManager::createDeclaredResources()函数会在创建完资源对象后,也将其指针保存到相应的ResourceGroup的loadResourceOrderMap中。这会造成资源对象在ResourceGroup中的重复保存吗?

作者:yzwalkman
转载请注明出处。
原文地址:https://www.cnblogs.com/yzwalkman/p/2830862.html