梦幻西游在去年出了个新版本,在这个版本中,采用了 3d 技术渲染人物。我参加过部分的技术讨论。总的来说,对于公司的关键产品,是以稳定性为第一。所以不希望对程序做大改动。最好以独立扩充的模块为主。所以最终采用的技术是用 3d 技术渲染成图片,再依靠旧的程序框架跑起来。
采用 3d 渲染,主要是为了解决人物换装时的图片资源组合爆炸问题。当然还有更绚的特效等。
最近,梦幻西游的项目经理提出,新的版本性能依旧有些问题。当老的版本可以同时打开 5 个客户端时,新的版本只能开两个。允许用户同时开多个客户端,对梦幻西游这款产品非常重要。我最近几天就开始着力解决这个优化问题。
目前的方案是用一个独立进程去渲染图片,然后通过共享内存的方式,把渲染结果提供给游戏客户端。这较好的解决了多个客户端的性能下降问题。 我认为,这也是继续延用 2d engine 做核心的优势。(当然更重要原因是,这个游戏的客户端程序不适合重新制作,否则重制版一旦出现严重问题,对公司影响太大)
我研究了一下现有方案。考虑了几天,把一些细节重新设计了一下。感觉可以压榨出一些性能改善的空间。
3d 渲染本身并不是大的瓶颈,不过渲染结果从显存拷贝出来会是个问题。我个人倾向于使用软件渲染引擎。Pixomatic 是个不错的选择。不过这是个独立模块,倒是无所谓什么时候做。目前已经在用的这一块工作良好,暂时是不用动的。
另一块是从 32bit 平坦位图数据(渲染结果)压缩为 8bit 的 RLE 格式数据,和以前的 2d engine 中的数据格式兼容。这部分目前的代码运行时间大约相当于渲染环节的 1/4 。虽然还有一定的优化空间,不过暂时也可以不动。注:这里的优化,主要是集中在调色盘的计算上,目前用的算法是可以改进的。
在旧的 Engine 中,RLE 压缩后的图片是以行为单位储存。而每行数据,是记录的指针。这点可以修改一下,改记为相对数据块首的偏移量。这样,压缩图片数据就是地址无关的。更适合做进程间共享。这一点,大约花几个小时就能修改过来,而且不需要更改压缩图片数据在磁盘中的格式。
重点可以优化的地方在:我们可以把所有图片加载的环节全部移到单一的进程中。而不仅仅只是用一个独立进程负责实时渲染。这可以降低在多个游戏进程同时工作时的 IO 压力。
其实,现代操作系统,已经会把所有闲置内存用于磁盘 cache 。所以,重复加载文件,只是一些内存拷贝工作。不过这个工作也正是消耗 CPU 时间的工作。减少重复加载的数据量,也可以减轻 CPU 负担。
让多个游戏进程共享资源,也可以减少总的内存开销。这就能极大的改善总体性能。
最终的方案,是用一个独立 128bit ID 来定义每个资源的请求。这个 128bit ID 是由整个请求的参数计算出来的。
游戏进程计算出 ID 后,每次需要资源前,都向资源提供进程提交 ID , 但并不等待回应。而资源进程,按优先次序,依次获取图片资源,并逐个广播地址给每个游戏进程。为了加快响应,可以先返回缺省替代图的地址,然后等资源获取完整后,再刷新一次完整的地址。
游戏进程则维护一张缓存表,每当资源进程通知它新的图片地址后,就更新表中的对应关系。
资源共享使用分页共享内存的方式。按经验数值,一页 8M 。每个游戏进程映射的共享区虚拟地址不一。但由于图片数据是地址无关的,所以不太所谓。整个共享空间可以达到 512M 足够游戏使用。
其实,这个资源管理程序,不限于从磁盘加载数据,或是利用 GPU 渲染图片。更可以从网络下载。只需要把共享推送机制最好就行了。需要解决的只是响应速度问题。