IxEngine开发笔记

导航

第十回 资源管理

 所有的关于Engine的介绍都会说到资源管理,我也来说说我们的,其实也没什么太新鲜的东西,因为大家写得都差不多. 目前engine中大约有十几种资源类型,比较重要的有vb/ib,texture,shader,mesh,material,animation等,除了vb/ib外,大多数资源的数据都是由各种外部工具生成的,保存在硬盘上,每一个资源保存为一个文件,根据后缀名表示它是什么类型的资源,比如mesh资源的后缀是"msh",material资源的后缀是"mtl". 资源数据载入内存后成为资源对象,以下是资源对象的基本接口: class IResource { public: virtual int AddRef()=0;//增加引用计数 virtual int Release()=0;//释放引用计数 virtual const char *GetPath()=0;//得到资源的路径名 virtual AResult Touch()=0;//检查资源是否可以使用 virtual BOOL ForceTouch()=0;//确保资源可以使用 virtual DWORD GetVer()=0;//返回版本号,当这个资源被重新载入的时候,这个版本号会被增加 }; engine中每一个资源类型都会有一个资源管理器,它的内部维护了一个资源对象的列表.大多数资源管理器只暴露一个接口函数, class IResouceMgr { public: IResource *ObtainRes(const char *path)=0; }; ObtainRes(..)首先将传入的路径名作为一个hashkey,到已经载入的资源对象列表中去寻找,如果找到了,就为这个对象增加一个引用计数,并返回,如果没有找到,则根据这个路径名到文件系统中去找到对应的资源文件,并把它载入内存成为一个资源对象,加到资源对象列表中去,增加一个引用计数然后返回.这样保证了一个资源文件在内存中只会有一个资源对象和它对应. 资源对象是通过引用计数来管理生存周期的,但我们的实现略有不同的是,当一个资源对象的引用计数减到0的时候,并不会立即销毁这个对象,资源管理器会定期的检查,销毁那些太长时间没有被引用到的资源对象.某些情况下,这样可以防止过于频繁的卸载/载入资源. 请注意我们使用路径名来标识一个资源数据,这样做比较简单,但有一个比较不好的地方就是:当资源文件移动位置的时候,原来路径名会失效.我考虑了一些方法来减轻这个问题: 1.首先还是要做好规划工作,避免出现经常移动文件位置的情况 2.把引用的资源尽量放到同一目录下,比如某个材质引用了若干张贴图,我们要尽量把这些贴图放到和这个材质同一个目录下,这样材质中只要为贴图记录相对于自己的路径就行了,而在文件

    移动时,同一个目录下的文件一般是一起移动的,这样就不会出现路径名失效的问题.这种情况在开发中还是比较常见的 3.提高程序的健壮性,在引用的资源读取失败后,绝对不能当机,并要能输出错误信息 4.在编辑器中提供方便的修补功能,如下图:     

       首先我们对于missing的资源名用醒目的颜色标识出来,然后我们提供修补按钮,自动在整个目录里搜索,找出最匹配的路径名供用户选择. 上面提到了资源数据的载入,由于采用了多线程载入,这个过程变得复杂了,资源对象分为多个状态: enum ResourceState { Empty=0, Loading, Loaded, Failed, Ready, }; 资源对象的载入过程包括以下几步: 1.资源管理器创建一个资源对象,此时它的状态为Empty 2.资源管理器将这个资源对象连同它的路径名加入到一个文件载入线程中,并标记资源的状态为Loading. 3.资源管理器将资源对象加到资源对象列表中,并将资源对象的指针返回 4.文件载入线程根据文件名将资源文件的原始数据全部读到内存里来,把这块内存保存到资源对象内部,并把资源对象的状态标记为Loaded,如果文件载入失败,就标记为Failed 5.任何资源在每次使用前都要调用Touch(),Touch()的返回值有三中:Ok,Fail和Waiting 6.Touch()函数内部会检查对象的当前状态, 如果为Failed,则返回Fail, 如果为Loading则返回Waiting, 如果为Loaded,则会根据从文件里读出的原始数据创建真正的资源对象数据.如果成功,则把资源状态标记为Ready,并返回Ok,否则标记为Failed,并返回Fail. 如果已经是Ready了,则返回Ok, 7.资源的使用者根据Touch()的返回值来决定要不要使用这个资源,如果返回值为Waiting,它可以选择暂时不使用这个资源(比如界面上显示的某张图片尚未载入时,我们可以暂时不画它),

    也可以等待资源被载入: while(pRes->Touch()==Waiting); //这也是ForceTouch()所做的事情. 目前引擎中的资源管理还是比较简陋的,只是完成了一些基本的功能.需要改进的地方有: 1. 对于贴图资源,也许要考虑能够先快速的载入一张低精度的版本用于显示,然后在后台继续载入高精度版本 2. 对于角色动画资源等数据量比较大的资源,也许要考虑只读取资源某一部分的情况. 我能想到的就这些,如果谁有好的建议,也欢迎提出来,一起讨论. 不过我觉得要保证游戏在载入时没有明显的延迟,归根结底还是上层要能够提供一套好的载入预测机制,这不是资源管理器可以解决的. 最后说一下资源热加载的功能,所谓资源热加载,就是在不需要重启游戏或者编辑器的情况下,对资源文件的修改可以立即反映到游戏中去.比如游戏里的一个角色使用某张贴图,我们可以边在PhotoShop里修改这张贴图,边在游戏里看到实际修改的效果,而不用退出游戏.我们在实现了shader的热加载以后,shader的调试时间被大大缩短了,某种程度上比调试vc程序更方便.游戏编程精粹第六册里详细介绍了这种技术,我们也是重点参考了它的源代码.我觉得这是个性价比很高的功能,一旦实现了它,你一定会后悔没有更早实现它的. 资源管理就说到这,下回说说shader.


posted on 2010-07-26 22:17  ixnehc  阅读(958)  评论(1编辑  收藏  举报