3d engine 中的贴图资源管理
今天有同事问了我一个涉及贴图管理的问题,看起来他们是想改进他们项目现在用的 3d engine (前些年由网易方舟工作室开发的)。我们随便聊了一下,最后的结果是他们取消了一开始的一个需求。
是的,知道自己最终需要什么是特别重要的,不要把过程当成目的。
下面要写的和今天的讨论无关,只是想记录一下:我们的 3d engine 中的贴图资源管理方案。
资源管理是一个很复杂的模块,当然我们应该尽量简化它。在此之前,我曾经记录过我们的资源管理模块的设计变迁。上篇文章到现在也有几个月了,我又做了些设计上的简化,不过变化不大,暂时不提。
而贴图资源作为一种特殊资源,又有所不同。仔细斟酌之后,我把它放在高一层次专门管理。
如前辈所言:确保特殊的情况是真的特殊。在《The Elements of Programming Style》和 《Unix 编程艺术》中都有提及。
那么,第一个问题是:为什么其是特殊的?
在 3d engine 中,贴图往往占用的是显存,而不是内存。系统显存的上限和内存上限是独立的。显存的使用策略,和相关 api 限制(比如多线程因素)稍微复杂一点。
另外,贴图并不总是可以直接从外存加载进来。在支持换装系统的 3d engine 中,某些贴图是由多张小贴图,或多张贴图的一部分组合起来的。比如把人物的上衣、皮带、裤子等等分放在不同的贴图上,使用的时候再组合成一整张。这是因为,对于显卡来说,零碎的贴图会极大的降低性能。
组合贴图这点破事,看起来很好实现。实际上优雅的管理它们,又兼顾一些性能,还是不太容易的。据我所见,至少网易已经运营的 3d 游戏所用的 engine 中(天下所用之 bigworld 和大唐系列所用之大唐)并无很好的管理方案。其结果是,人物换装不那么细腻。而在 wow 中,你可以单独更换身体的许多细节部分,而在外观上表现出来(哪怕只有一小点变化)。
ps. 当然,换装系统不仅仅是更换贴图。
我所用的方法是定义一个贴图组(Texture bundle)数据文件。里面描述了不只一张贴图。对于 engine ,直接使用贴图组的默认贴图。但是可以动态组织它。
如果需要整张贴图更换,直接设置贴图组的状态,切换到某一张具体贴图数据上。
这样做的原因是,模型由美术制作好后,并非可以任意更换贴图数据。往往只是某几张特制的贴图可以互换。如果由使用者为模型任意指定贴图显然是不合适的,而切换贴图组的状态则更为易用、健壮。
如果需要拼接贴图,则在贴图组中描述出矩形的区域划分。每张具体贴图都按统一的划分方案制作局部图案。
单张贴图可以只有局部图案,而不需要全部区域填满。
使用的时候,使用者提供一个 pattern 来组合出最终需要的贴图。即:每个区域使用第几张贴图。
在实现的时候,我采用方便解析又适合做 hash key 的单个字符串来描述 pattern 。
关于贴图的管理:
采用一个两级 hash map 和一个 list 的复合数据结构。
第一级 hash map 用来从一个资源对象检索出贴图数据块(对应一个贴图组)。第二级 hash map 用来 cache 从一个特定的贴图数据中,以一个特定 pattern 组合出的需要的贴图。
list 是一个先入先出的按使用频率排序的最终贴图对象列表。
从第一级 hash map 得到的贴图数据块,以一个永久有效的 handle 索引。(因为它一定会对应唯一数据文件,数量有限,故而我们不需删除 handle )
engine 使用贴图前,都需要从上述 handle 中拣选出所需要 pattern 。使用者不可认为拣选出来的真正贴图对象一直有效。事实上,engine 只保证其有效期维持到当前帧渲染完毕。
这个设计可以让 engine 的贴图管理模块,以类似操作系统管理物理内存那样,根据实际资源情况释放掉暂时不在使用的显存。并 cache 住那些常用的贴图 pattern 。
由于贴图数据的特殊性,有时候,我们需要其以 id 的形式纳入显卡驱动层的管理下;而另一些时候,我们可能需要访问其数据。所以贴图对象有两种存在于 engine 内的形式。
我们在设计的时候,参考了 freetype 2 对字模的管理方式。让这些数据以 slot 的形式存在并让用户去访问。访问之前,调用对应的 api 加载即可。而不用理会其生命期。(engine 保证数据到当前帧渲染完毕前都有效)
btw, 对于 DXT 类的压缩贴图。了解了它的原理和数据格式 后,也是可以直接切分和组合的。只是要按 4x4 像素为一最小单元去操作。
Comments
Posted by: mmosquito | (20) September 24, 2009 01:41 PM
Posted by: cloud | (19) August 1, 2009 04:04 PM
Posted by: fancy | (18) August 1, 2009 02:04 PM
Posted by: ix | (17) August 1, 2009 01:31 PM
Posted by: cloud | (16) August 1, 2009 11:23 AM
Posted by: analyst | (15) August 1, 2009 10:50 AM
Posted by: ix | (14) August 1, 2009 07:31 AM
Posted by: cloud | (13) August 1, 2009 12:29 AM
Posted by: ix | (12) July 31, 2009 08:01 PM
Posted by: cloud | (11) July 31, 2009 06:14 PM
Posted by: ix | (10) July 31, 2009 04:46 PM
Posted by: cloud | (9) July 31, 2009 03:38 PM
Posted by: Anonymous | (8) July 31, 2009 01:32 PM
Posted by: ix | (7) July 31, 2009 01:01 PM
Posted by: Cloud | (6) July 31, 2009 12:23 PM
Posted by: ix | (5) July 31, 2009 12:16 PM
Posted by: Anonymous | (4) July 30, 2009 10:36 PM
Posted by: analyst | (3) July 30, 2009 10:59 AM
Posted by: analyst | (2) July 30, 2009 10:53 AM
Posted by: xiaowen | (1) July 30, 2009 08:33 AM