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中的重复保存吗?
浙公网安备 33010602011771号