zoukankan      html  css  js  c++  java
  • Ogre源代码浅析——资源管理逻辑结构(三)

          在完成了资源定位和资源初始化之后,Ogre就随时可以对资源进行加载了。现以mesh对象的加载过程为例,分析一下资源的加载过程。以下是Call Stack中,实现mesh对象数据加载的各被调函数的调用顺序:

    1. MeshManager::load( const String& filename, const String& groupName, 
            HardwareBuffer::Usage vertexBufferUsage, 
            HardwareBuffer::Usage indexBufferUsage, 
            bool vertexBufferShadowed, bool indexBufferShadowed)
    
                                ||
                                \/
    
    2. MeshManager::createOrRetrieve(
            const String& name, const String& group,
            bool isManual, ManualResourceLoader* loader,
            const NameValuePairList* params,
            HardwareBuffer::Usage vertexBufferUsage, 
            HardwareBuffer::Usage indexBufferUsage, 
            bool vertexBufferShadowed, bool indexBufferShadowed)
    
                                ||
                                \/
    
    3. ResourceManager::createOrRetrieve(
        const String& name, const String& group, 
        bool isManual, ManualResourceLoader* loader, 
        const NameValuePairList* params)

         在结点“3”中,createOrRetrieve函数会先通过getByName(name, group)搜索name所指的资源对象,代码如下:

     1 ResourcePtr ResourceManager::getByName(const String& name, const String& groupName /* = ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME */)
     2 {        
     3     ResourcePtr res;
     4 
     5     // if not in the global pool - get it from the grouped pool 
     6     if(!ResourceGroupManager::getSingleton().isResourceGroupInGlobalPool(groupName))
     7     {
     8         OGRE_LOCK_AUTO_MUTEX
     9         ResourceWithGroupMap::iterator itGroup = mResourcesWithGroup.find(groupName);
    10 
    11         if( itGroup != mResourcesWithGroup.end())
    12         {
    13             ResourceMap::iterator it = itGroup->second.find(name);
    14 
    15             if( it != itGroup->second.end())
    16             {
    17                 res = it->second;
    18             }
    19         }
    20     }
    21 
    22     // if didn't find it the grouped pool - get it from the global pool 
    23     if (res.isNull())
    24     {
    25         OGRE_LOCK_AUTO_MUTEX
    26 
    27         ResourceMap::iterator it = mResources.find(name);
    28 
    29         if( it != mResources.end())
    30         {
    31             res = it->second;
    32         }
    33         else
    34         {
    35             // this is the case when we need to search also in the grouped hash
    36             if (groupName == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
    37             {
    38                 ResourceWithGroupMap::iterator iter = mResourcesWithGroup.begin();
    39                 ResourceWithGroupMap::iterator iterE = mResourcesWithGroup.end();
    40                 for ( ; iter != iterE ; iter++ )
    41                 {
    42                     ResourceMap::iterator resMapIt = iter->second.find(name);
    43 
    44                     if( resMapIt != iter->second.end())
    45                     {
    46                         res = resMapIt->second;
    47                         break;
    48                     }
    49                 }
    50             }
    51         }
    52     }
    53     
    54     return res;
    55 }

          可以看到,Ogre会在MeshManager的"group pool"中搜索(6-20行、33-51行);也会在"global pool"中搜索(29-32行),并返回搜索结果。如果没找到(返回的资源指针为空),createOrRetrieve函数会调用ResourceManager::create()来创建一个mesh对象(同前)。以下为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;
    
    }

           create()函数会先创建一个mesh对象(并未加载数据),并将mesh对象指针根据实际情况保存到ResourceManager的相关容器中;并通知ResourceGroupManager修改相应ResrouceGroup中的数据。

           通过以上过程,在返回到调用堆栈结点"1"的函数MeshManager::load()后,MeshManager和ResourceGroupManager中一定会存在相应的mesh对象。接下来load函数将调用mesh对象的load函数,开始真正的mesh数据加载操作。mesh数据的加载是通过调用Resource::load(bool background)函数来实现的。代码如下:

      1 void Resource::load(bool background)
      2 {
      3     // Early-out without lock (mitigate perf cost of ensuring loaded)
      4     // Don't load if:
      5     // 1. We're already loaded
      6     // 2. Another thread is loading right now
      7     // 3. We're marked for background loading and this is not the background
      8     //    loading thread we're being called by
      9 
     10     if (mIsBackgroundLo aded && !background) return;
     11 
     12     // This next section is to deal with cases where 2 threads are fighting over
     13     // who gets to prepare / load - this will only usually happen if loading is escalated
     14     bool keepChecking = true;
     15     LoadingState old = LOADSTATE_UNLOADED;
     16     while (keepChecking)
     17     {
     18         // quick check that avoids any synchronisation
     19         old = mLoadingState.get();
     20 
     21         if ( old == LOADSTATE_PREPARING )
     22         {
     23             while( mLoadingState.get() == LOADSTATE_PREPARING )
     24             {
     25                 OGRE_LOCK_AUTO_MUTEX
     26             }
     27             old = mLoadingState.get();
     28         }
     29 
     30         if (old!=LOADSTATE_UNLOADED && old!=LOADSTATE_PREPARED && old!=LOADSTATE_LOADING) return;
     31 
     32         // atomically do slower check to make absolutely sure,
     33         // and set the load state to LOADING
     34         if (old==LOADSTATE_LOADING || !mLoadingState.cas(old,LOADSTATE_LOADING))
     35         {
     36             while( mLoadingState.get() == LOADSTATE_LOADING )
     37             {
     38                 OGRE_LOCK_AUTO_MUTEX
     39             }
     40 
     41             LoadingState state = mLoadingState.get();
     42             if( state == LOADSTATE_PREPARED || state == LOADSTATE_PREPARING )
     43             {
     44                 // another thread is preparing, loop around
     45                 continue;
     46             }
     47             else if( state != LOADSTATE_LOADED )
     48             {
     49                     OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Another thread failed in resource operation",
     50                         "Resource::load");
     51             }
     52             return;
     53         }
     54         keepChecking = false;
     55     }
     56 
     57     // Scope lock for actual loading
     58     try
     59     {
     60 
     61         OGRE_LOCK_AUTO_MUTEX
     62
     63 
     64         if (mIsManual)
     65         {
     66             preLoadImpl();
     67             // Load from manual loader
     68             if (mLoader)
     69             {
     70                 mLoader->loadResource(this);
     71             }
     72             else
     73             {
     74             // Warn that this resource is not reloadable
     75                     LogManager::getSingleton().stream(LML_TRIVIAL) 
     76                         << "WARNING: " << mCreator->getResourceType()  
     77                         << " instance '" << mName << "' was defined as manually "
     78                         << "loaded, but no manual loader was provided. This Resource "
     79                         << "will be lost if it has to be reloaded.";
     80             }
     81               postLoadImpl();
     82         }
     83         else
     84         {
     85 
     86                 if (old==LOADSTATE_UNLOADED)
     87                     prepareImpl();
     88 
     89                 preLoadImpl();
     90 
     91             if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME)
     92             {
     93                 // Derive resource group
     94                 changeGroupOwnership(
     95                         ResourceGroupManager::getSingleton()
     96                         .findGroupContainingResource(mName));
     97             }
     98 
     99             loadImpl();
    100 
    101                 postLoadImpl();
    102         }
    103 
    104         // Calculate resource size
    105         mSize = calculateSize();
    106 
    107     }
    108         catch (...)
    109         {
    110             // Reset loading in-progress flag, in case failed for some reason.
    111             // We reset it to UNLOADED because the only other case is when
    112             // old == PREPARED in which case the loadImpl should wipe out
    113             // any prepared data since it might be invalid.
    114             mLoadingState.set(LOADSTATE_UNLOADED);
    115             // Re-throw
    116             throw;
    117         }
    118 
    119         mLoadingState.set(LOADSTATE_LOADED);
    120         _dirtyState();
    121 
    122     // Notify manager
    123     if(mCreator)
    124         mCreator->_notifyResourceLoaded(this);
    125 
    126     // Fire events, if not background
    127     if (!background)
    128         _fireLoadingComplete(false);
    129 
    130 
    131 }

           实际执行加载任务的是loadImpl()(99行),但之前的预加载处理prepareImpl()(87行)值得关注。Ogre是采用数据流的方式对外部数据进行读写的,为此专门引入DataStream类和FileStreamDataStream类来进行相关操作;而在用DataStream对象进行具体的数据操作之前,要先将DataStream对象与指定的外部数据文件关联起来,Ogre用ResourceGroupManager::openResource()函数来实现这个关联操作。ResourceGroupManager::openResource()是在prepareImpl()函数中被调用的。以下是ResourceGroupManager::openResource()函数展开:

     1 DataStreamPtr ResourceGroupManager::openResource(
     2         const String& resourceName, const String& groupName, 
     3         bool searchGroupsIfNotFound, Resource* resourceBeingLoaded)
     4 {
     5     OGRE_LOCK_AUTO_MUTEX
     6 
     7     if(mLoadingListener)
     8     {
     9         DataStreamPtr stream = mLoadingListener->resourceLoading(resourceName, groupName, resourceBeingLoaded);
    10         if(!stream.isNull())
    11             return stream;
    12     }
    13 
    14     // Try to find in resource index first
    15     ResourceGroup* grp = getResourceGroup(groupName);
    16     if (!grp)
    17     {
    18             OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
    19                 "Cannot locate a resource group called '" + groupName + 
    20                 "' for resource '" + resourceName + "'" , 
    21                 "ResourceGroupManager::openResource");
    22     }
    23 
    24     OGRE_LOCK_MUTEX(grp->OGRE_AUTO_MUTEX_NAME) // lock group mutex
    25 
    26     Archive* pArch = 0;
    27     ResourceLocationIndex::iterator rit = grp->resourceIndexCaseSensitive.find(resourceName);
    28     if (rit != grp->resourceIndexCaseSensitive.end())
    29     {
    30         // Found in the index
    31         pArch = rit->second;
    32         DataStreamPtr stream = pArch->open(resourceName);
    33         if (mLoadingListener)
    34             mLoadingListener->resourceStreamOpened(resourceName, groupName, resourceBeingLoaded, stream);
    35         return stream;
    36     }
    37     else 
    38     {
    39             // try case insensitive
    40             String lcResourceName = resourceName;
    41          StringUtil::toLowerCase(lcResourceName);
    42         rit = grp->resourceIndexCaseInsensitive.find(lcResourceName);
    43         if (rit != grp->resourceIndexCaseInsensitive.end())
    44         {
    45                 // Found in the index
    46              pArch = rit->second;
    47              DataStreamPtr stream = pArch->open(resourceName);
    48              if (mLoadingListener)
    49                 mLoadingListener->resourceStreamOpened(resourceName, groupName, resourceBeingLoaded, stream);
    50              return stream;
    51         }
    52         else
    53         {
    54                  // Search the hard way
    55             LocationList::iterator li, liend;
    56             liend = grp->locationList.end();
    57             for (li = grp->locationList.begin(); li != liend; ++li)
    58             {
    59                 Archive* arch = (*li)->archive;
    60                 if (arch->exists(resourceName))
    61                 {
    62                    DataStreamPtr ptr = arch->open(resourceName);
    63                    if (mLoadingListener)
    64                       mLoadingListener->resourceStreamOpened(resourceName, groupName, resourceBeingLoaded, ptr);
    65                    return ptr;
    66                 }
    67             }
    68         }
    69      }
    70 
    71         
    72     // Not found
    73     if (searchGroupsIfNotFound)
    74     {
    75         ResourceGroup* foundGrp = findGroupContainingResourceImpl(resourceName); 
    76         if (foundGrp)
    77         {
    78             if (resourceBeingLoaded)
    79             {
    80                 resourceBeingLoaded->changeGroupOwnership(foundGrp->name);
    81             }
    82             return openResource(resourceName, foundGrp->name, false);
    83         }
    84         else
    85         {
    86                 OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND, 
    87                     "Cannot locate resource " + resourceName + 
    88                     " in resource group " + groupName + " or any other group.", 
    89                     "ResourceGroupManager::openResource");
    90         }
    91     }
    92     OGRE_EXCEPT(Exception::ERR_FILE_NOT_FOUND, "Cannot locate resource " + 
    93             resourceName + " in resource group " + groupName + ".", 
    94             "ResourceGroupManager::openResource");
    95 
    96 }

          从上面代码可以看到,Ogre首先是在相应ResrouceGroup的resourceIndexCaseSensitive和resourceIndexCaseInsensitive中搜索与resourceName相匹配的Archive对象,如果找到了就在Archive指定的路径下打开resourceName所指定的文件。(27-51行)如果在以上两个容器中都未找到对应的Archive,那么Ogre会在locationList中再进行一轮搜索(55-67行)。如果还没找到,Ogre就会通过函数findGroupContainingResourceImpl()将搜索范围扩大到ResourceGroupManager中的所有ResouceGroup,搜索的方式与上面是相同的。当所有搜索结束而未得到相应的Archive时,Ogre将抛弃常。

          从之前(一)、(二)的讨论可知,resourceIndexCaseSensitive和resourceIndexInsensitive中所有的key值是资源文件名,也就是说经过资源定位操作(见之前的讨论)之后,一般情况下只要是“定位”过的目录,其中的文件名都会存在于某个ResourceGroup的以上两个容器中,所以通常resouceName所指定的资源在前两轮对这两个容器的搜索中就可以成功得到相应的Archive;但还有一种特殊情况——Archive所指定的不是“普通目录”而是“压缩包”。如果Archive指向“压缩包”文件,那么在之前的资源定位操作中,Ogre并不会把压缩包中所包含的资源文件名提取出来,保存到resourceIndexCaseSensitive或resourceIndexInsensitive中去,因而如果被加载的资源文件处在某个压缩包中时,加载过程中对这两个容器的搜索就无法得到想要的结果。这样看来Ogre提供的对locationList就显得很有必要了。Ogre在遍历locationList的过程中会对其中的每个Archive对象所示的目录或压缩包中的所有文件进行逐一探查(通过bool exists(const String& filename)函数),看看其中是否有一个文件名与resourceName相同的文件存在。以下是FileSystemArchive与ZipArchive的exists()函数:

        bool FileSystemArchive::exists(const String& filename)
        {
            String full_path = concatenate_path(mName, filename);
    
            struct stat tagStat;
            bool ret = (stat(full_path.c_str(), &tagStat) == 0);
    
            // stat will return true if the filename is absolute, but we need to check
            // the file is actually in this archive
            if (ret && is_absolute_path(filename.c_str()))
            {
                // only valid if full path starts with our base
    #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
                // case insensitive on windows
                String lowerCaseName = mName;
                StringUtil::toLowerCase(lowerCaseName);
                ret = Ogre::StringUtil::startsWith(full_path, lowerCaseName, true);
    #else
                // case sensitive
                ret = Ogre::StringUtil::startsWith(full_path, mName, false);
    #endif
            }
    
            return ret;
        }
    
        bool ZipArchive::exists(const String& filename)
        {
            // zziplib is not threadsafe
            OGRE_LOCK_AUTO_MUTEX
            ZZIP_STAT zstat;
            int res = zzip_dir_stat(mZzipDir, filename.c_str(), &zstat, ZZIP_CASEINSENSITIVE);
    
            return (res == ZZIP_NO_ERROR);
    
        }

          从上面的讨论可以看到,Ogre进行资源加载前的“资源定位”操作是何等重要。用户即便是准备好了资源,组织好了外部存储方式,如果之前未对其进行正确的“资源定位”,那么在后续的操作中此资源文件将始终无法被搜索到,从而无法被加载。这里要强调的是“资源定位”(locate)与“资源声明”(declare)是两个不同的概念。

    作者:yzwalkman
    转载请注明出处。
  • 相关阅读:
    hdu2222 AC自动机入门
    bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习
    【NOI2014】起床困难综合症 贪心
    bzoj1822: [JSOI2010]Frozen Nova 冷冻波网络流
    洛谷3767 膜法 带权并查集+分治
    NOI2015品酒大会 后缀数组
    NOI2015程序自动分析 并查集
    NOI2015软件包管理器 树剖线段树
    51nod1244 欧拉函数之和 杜教筛
    51nod1244 莫比乌斯函数之和 杜教筛
  • 原文地址:https://www.cnblogs.com/yzwalkman/p/2832354.html
Copyright © 2011-2022 走看看