OGRE源码分析---Resource 板块分析

引擎名称:OGRE 版本:1.11.5
模块名称:资源模块
2.
游戏资源管理主要做什么?
·资源组织
根据各种资源的特性和使用的方式等,设计资源数据的内存组织方式
·资源管理
统一的资源处理方法,如:内存分配、资源状态管理、加载和卸载操作、资源调度算法、以及多线程管理等
游戏资源管理目的:通过有效地组织和管理各类资源,能在合适的时候、快速地提供给游戏程序。

游戏资源管理的种类
文件管理:游戏的资源是以文件的形式存储的
·包括文件的加载和卸载,文件解析,格式转换,打包和传输资源文件等。
内存管理:使用资源时需要加载到内存
·内存分配
·访问保证
·内存释放

状态管理:资源当前的状态,如已定义、已加载、已移除等
资源调度算法:在有限的内存中,为使用资源的程序提供最好的方案
·优先队列
·LRU
多线程处理:资源管理的并行处理

类和类关系描述

3.1主要类的关系
OGRE源码分析---Resource 板块分析
简单来说,他们之间的关系

Resource、ResourceManager、ResourceGroupManager这三个类是OGRE引擎进行资源管理的三个核心类,Ogre把资源分为“Font”、“GpuProgram”、“Material”、“Mesh”、“Skeleton”和“Texture”等类型,它们分别用Font、GpuProgram、Material、Mesh、Skeleton、Texture等同名的类对象来描述,这些类都直接从Resource基类派生。Ogre的Resource对象都由ResourceManager来管理。不同类型资源的管理,分别由不同的资源管理器来实现,比如以上各种类型资源都对应着各自的资源管理器,FontManager、GpuProgramManager、MaterialManager、MeshManager、SkeletonManager、TextureManager等,它们都以ResourceManager作为自已的基类。各种类型资源类对象的创建、Load/Unload、销毁等操作,都由相应的ResourceManager来完成。但Ogre的对资源的管理还不仅限于此。为了更方便资源的使用,提高资源的使用的效率。Ogre中有一个被称为ResourceGroupManager的类,其中内嵌了一个ResourceGroup的结构定义,很明显定义ResourceGroup只是为了ResourceGroupManager内部使用。在需要进行3D场景展示的一般应用中,经常会遇到需要进行场景切换的时候,比如游戏中的关卡切换时,虚拟现实中角色由一个地点转换到另一个地点时等等。而在渲染每个场景时所需的资源往往涉及了所有的资源类型,一旦场景发生切换,当前所使用的大量资源都需要被逐一卸载,而新的场景所需的各类资源要逐一被加载。在游戏编程时,可以在自已编写的关卡管理器中处理类似工作,这明显会产生额外的工作量,更麻烦的是这部分代码逻辑可能需要在每个应用中被重复编写,而如果借助Ogre提供的ResourceGroup就可以直接方便地实现类似功能了。

Ogre处理一个资源的简单流程:1、Ogre启动时对外部资源并不了解,通过添加资源名称、组名、文件路径等信息,录入资源信息(通常Ogre通过外面脚本定义资源路径)。2、将一个资源归入一个资源组,并通过定义的信息,在内存中实例化相应的资源(具体的创建过程是由ResourceManager调用Resource完成的)

OGRE源码分析---Resource 板块分析
Undefined:这是程序开始时,所有资源的缺省状态。除非它们被声明,ogre对程序用到的资源一无所知,手工调用代码ResourceGroupManager::declareResource()或在脚本中被解析之后,资源的状态变为Declared

Declared:声明就是告诉ogre想要加载某些资源。ResourceGroupManager::declareResource()
总是有效的,包括在渲染系统初始之前,这与ResourceManager::create()不同,因为后者依赖于渲染系统。
Unloaded:通过调用
ResourceGroupManager::initialiseAllResourceGroup(),ResourceGroupManager::initialiseResourceGroup(),或Root::initialise()(它会初始化在此调用之前所有声明的资源),资源状态进入到Unloaded,资源会占用一点内存来保存它的定义的实例,但是资源本身还没有加载到内存。从另一角度看(从Loaded到Unloaded),引起状态变化的调用有:
ResourceManager::unload(),ResourceManager::unloadAll(), ResourceManager::unloadAllUnreferencedResources(),ResourceGroupManager::unloadResourceGroup(), or Resource::unload().所有这些调用仍会保存资源实例,但真正的资源数据会从内存中卸载。
Loaded: 这种状态下,所有数据都变得有效。与此状态有关的调用有Resource::load(), Resource::reload(), ResourceManager::load(), ResourceManager::reload(), ResourceManager::reloadAll(), ResourceManager::reloadAllUnreferencedResources(), and ResourceGroupManager:: loadResourceGroup().

Resource
该类定义了所有Ogre中可导入资源的统一接口,保存了资源的信息。该类为抽象类
类的定义如下
/// Creator
ResourceManager* mCreator;
/// Unique name of the resource
String mName;
/// The name of the resource group
String mGroup;
/// Numeric handle for more efficient look up than name
ResourceHandle mHandle;
/// The size of the resource in bytes
size_t mSize;
/// Optional manual loader; if provided, data is loaded from here instead of a file
ManualResourceLoader* mLoader;
mCreator:每个资源类都是由ResourceManager创建,该变量保留创建者的指针。
mName/mGroup/mHandle/mSize:资源类的名称,所属的组名,句柄以及占用内存大小。
mLoader:一般资源可以有Ogre自动导入,当需要手动进行导入时,需要设置该变量来设定手动导入操作的类,实现不同的导入操作。
mLoadingState:资源当前状态。AtomicScalar是一个封装原子操作的模板类,这个为多线程资源管理提供了可能。

资源状态LoadingState枚举如下
enum LoadingState
{
/// Not loaded
LOADSTATE_UNLOADED,
/// Loading is in progress
LOADSTATE_LOADING,
/// Fully loaded
LOADSTATE_LOADED,
/// Currently unloading
LOADSTATE_UNLOADING,
/// Fully prepared
LOADSTATE_PREPARED,
/// Preparing is in progress
LOADSTATE_PREPARING
};
从上述枚举中可以看出,一个资源有两个非常重要的状态:load和prepare。Resource类中的函数基本也就是围绕着这两个状态改变而进行的。

virtual void preLoadImpl(void) {}
virtual void postLoadImpl(void) {}
virtual void preUnloadImpl(void) {}
virtual void postUnloadImpl(void) {}
virtual void prepareImpl(void) {}
virtual void unprepareImpl(void) {}
virtual void loadImpl(void) = 0;
virtual void unloadImpl(void) = 0;
可以看出这些函数的作用就是定义load和prepare前、中。后需要进行的操作。 这里用到了设计模式的Template模板,定义接口,将所有实现都延迟到了子类中。 同时Resource也提供了更细颗粒度的方法来控制load和prepare过程——监听。Resource定义了公共内嵌类Listener,可以通过继承该监听类回调函数,灵活地设置资源状态改变时的操作。

引用Resource时,使用的是智能指针。
typedef SharedPtr ResourcePtr;
在ResourceManager中都是以这样的形式出现的。

ResourceManager
Resource类是负责具体资源的load和prepare细节,而ResourceManager类是负责创建、删除和调用Resource的,并管理ResourcePool方便同一资源的多次使用。
该类的主要数据结构定义如下:
typedef HashMap< String, ResourcePtr > ResourceMap;
typedef HashMap< String, ResourceMap > ResourceWithGroupMap;
typedef map<ResourceHandle, ResourcePtr>::type ResourceHandleMap;
ResourceMap将资源名称与资源一一对应起来。
ResourceWithGroupMap则是将资源进行一定分组管理。
ResourceHandleMap则是将资源句柄与资源对应。

ResourceGroupManager
如果说ResourceManger是对Resource的管理,那ResourceGroupManager就是对ResourceManger的集中管理。刚才提到过ResourceManager中将资源进行了分组管理,ResourceGroupManager就针对这每个分组进行了管理。
分组的信息记录在结构体ResourceGroup中
/// Group name
String name;
/// Group status
Status groupStatus;
/// List of possible locations to search
LocationList locationList;
/// Created resources which are ready to be loaded / unloaded
// Group by loading order of the type (defined by ResourceManager)
typedef map<Real, LoadUnloadResourceList*>::type LoadResourceOrderMap;
LoadResourceOrderMap loadResourceOrderMap;
上述是ResourceGroup中的部分代码。
该结构体中定义了一个Group的名字、状态、文件所在的位置,以及导入资源的顺序。关于导入顺序,Ogre代码中给出了一个例子,在导入mesh信息前需要导入骨骼和材质,所以后者的优先级就高于前者。 拥有了这样的Group描述信息,ResourceGroupManager就可以进行管理了。

/// Map from resource group names to groups
typedef map<String, ResourceGroup*>::type ResourceGroupMap;
ResourceGroupMap mResourceGroupMap;
默认在ResourceGroupManager构造函数中,创建三个分组General、Internal、Autodetect。

还有另一个重要的结构体ResourceDeclaration。
String resourceName;
String resourceType;
ManualResourceLoader* loader;
NameValuePairList parameters;
loader是手动导入资源的类指针;parameters用于StringInterface类设置类成员变量。
该结构定义了一个资源的信息,但注意,Ogre中定义了一个资源并没有真正地将它导入进来,也就是并没有生成资源句柄,只是录入了一些资源信息而已。
这些数据结构定义就是用来保存ResourceDeclaration信息的,ResourceDeclaration同样也保存在ResourceGroup中。
/// List of resource declarations
typedef list::type ResourceDeclarationList;
typedef map<String, ResourceManager*>::type ResourceManagerMap;
Ogre处理一个资源的简单流程了。
1、Ogre启动时对外部资源并不知晓,通过添加资源名称、组名、文件路径等信息,录入资源信息(通常Ogre通过外面脚本定义资源路径);
2、将一个资源归入一个资源组,并通过定义的信息,在内存中实例化相应的资源(具体的创建过程是由ResourceManager调用Resource完成的);

5.资源管理过程的时序图
OGRE源码分析---Resource 板块分析