创建好的资源对象指针会被保存在两个地方,第一处是在相应的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中的重复保存吗?