差不多3个月前,我去听过一场由Apple主办的公开讲座,请了鹰角的技术中心负责人Will,来分享关于《明日方舟:终末地》是如何在多平台上实现了目前行业顶尖的性能优化效果。

  当时我刚从旧金山的GDC游戏开发者大会回来不久,而那场讲座给我的印象,就是硬核程度和GDC上的不少技术分享不相上下——关卡设计师青青展示了早期白盒,Will介绍了所用的Bindless渲染方案,也直接提及了“ECS框架”这类数据处理方式……听完之后感觉学到了不少关于游戏开发技术的新知识。

  但Will在后台告诉我,因为这场演讲主要面向普通玩家,所以内容准备得比较浅显;如果将来有机会面向行业,他也愿意分享更具体的真实开发经验。

  事情的后续来得比预料中更快。

  上周在韩国首尔举办的Unite Seoul,是如今Unity开发者社区最重要的官方技术会议之一——Unity 7就是由Unity总裁兼CEO在这次会议上公开的。

  Will则受邀去做了一场题为“重构《明日方舟:终末地》的运行时渲染框架”的全英文演讲。它被标注为“进阶”(Advanced),也是议程中唯一一场来自中国游戏公司的项目分享。

  Will这场演讲是“会场限定”,没有安排线上直播和录像。会议现场座无虚席,演讲结束后也有来听讲的开发者排队来和Will交流。

  这场演讲的内容,也让我见识到了Will所说的“技术干货”究竟是什么样子——说实话其中大部分我都听得云里雾里,大部分技术名词是我仅作为多年玩家以及游戏媒体从业者从没见过的。

  于是在Will回国之后,我们又见了一次面,结合这次的演讲内容深入聊了一次。

  经由他的补充和解释,我发现这次演讲所涉及的内容,有不少就是我们上次聊到的一些玩家也能听懂的优化技巧背后,真正的底层技术,很能做一些呼应;另一方面,也因为真正深入了代码层的探讨,让我对于鹰角的技术体系搭建与理念也有了新的认识。

  接下来,我会尝试在上次报道的基础上,再往下剥一层,让大家看到图像背后更基础的代码和架构。Will也协助我重新筛选、解释了演讲内容,希望能把它们尽量说得深入浅出。

  不过我想这些内容依然属于非常“干”或者说“难懂”的范畴。一方面,我不认为自己能仅凭几千字,就把一场建立在大量专业知识之上的演讲,拆成既严谨又人人易懂的科普文;另一方面,比起借助大量比喻给大家营造一种似懂非懂的暧昧印象,我更希望尽可能保留原意与细节,让确实想要了解鹰角技术幕后的读者能够获得相对精准的认知——也算是对技术的一种尊重。

  1

  在终末地上线之前,大家就普遍知道鹰角对游戏所使用的Unity引擎进行了深度的定制化改造。高精度角色、大量动态对象、无缝箱庭以及跨平台……这些目标叠加在一起,光靠Unity的通用路径根本没法满足项目的性能预算。

  复杂的玩法设计和一开始就定的比较高的优化目标,从一开始就决定了终末地必须突破Unity的一些通用路径

  Will的这次演讲,则首次系统地公开了他们到底改了哪些部分、甚至具体怎么改的。

  演讲中主要提到了四个部分:

  其一,自研了ECS数据内核,用于支撑每帧数万个可渲染对象的高吞吐处理;

  其二,重构剔除、排序与批处理链路,让多个视图共享同一组可见性数据,再把渲染状态编码进同一个sort key,让“按状态聚簇”成为排序的天然产物,而非额外分组;

  其三,将关键的运行时渲染管理改为自写的原生C++管线,配合在CPU上手写Render Graph,管理资源生命周期和依赖关系;

  其四,底部再垫一层沿用Vulkan语义的设备抽象,屏蔽平台图形API差异,把移动端和 PC 端拉到同一条数据导向路径上。而且这层封装刻意做在最底层,描述符、屏障这些底层能力原样保留,上层的排序、绑定复用、屏障合并才能一路贯通到每个平台的后端;在原生支持 Vulkan 的设备上,这一层几乎零开销。

  在上次的文章里,我们提到过终末地在移动端上的一条优化底线:即便是在最低端的可运行设备上,也要尽量保住至少一公里的视距,而不是用一层浓雾把远景遮掉。

  能看到远方的景物依旧轮廓清晰

  景深吃掉的性能总得从别处省出来——技术团队采用的办法是在工厂区周围布置山体或大型建筑,让它们自然挡住场景里的部分内容。玩家看到的画面会更干净,被挡住的对象也不必继续绘制、消耗性能。

  当时我以为这种“遮一遮”的技巧是团队在开发中途灵机一动想到的点子,属于“灵感>技术”。但Will这次纠正了我的理解:实际顺序是技术团队先做出这套遮挡能力,再向美术和策划推荐利用遮挡关系来设计场景——可以说是终末地这个项目的基建。

  这套CPU软件遮挡具体实现起来,大致分为这么几个步骤:团队先从场景中挑出几百个主要遮挡物,不使用它们原本的完整模型,而是制作成非常简单的遮挡片——通常只是几个quad;

  运行时,CPU会把这些遮挡片变换、裁剪成屏幕空间里的三角形,再按屏幕区域分组,由不同线程分别画入一张低分辨率深度图;

  系统再用这张深度图判断哪些对象躲在遮挡物后面,把被完全挡住的对象从这一帧的绘制中剔除。被剔掉的对象并没有从场景中删除,只是这一帧不画,下一帧会重新参与判断。顺序上,遮挡测试其实是最后一关:在它之前,系统已经先做完视锥体测试(在不在视野里)和 LOD 选择(用哪一档精度),而且这两步是和深度图的构建同时进行的,谁也不用等谁,这正是线程之间不互相等待,“不留空闲气泡”的设计。另外还有一个独立的设计:十几二十个视角不再各自维护一份对象列表,而是共享同一份可见性数据,每个对象用一个 32 位数字记录自己在哪些视角里可见。

  完成剔除后,剩余对象才会进入排序和批处理。团队把屏幕顺序、pipeline、mesh、材质和距离等信息编码进一个128-bit(16字节)的排序键,通过一次标准排序让相同状态的对象集中在一起,相邻的 draw 用的是同一套参数,重复的绑定就能整段跳过。但有一类数据躲不开:每个物体自己的位置、参数,每条 draw 都不一样。这里他们用了另一个设计:不给每个物体单独建绑定表,而是全场景共用一份,指向同一个大缓冲区,每次 draw 只移动一个偏移量,指到自己那一小段数据,所以哪怕画几万个物体,这一档也是零分配。

  最后才交由GPU正式渲染。在Unity的编辑器、资源管线和工作流之下,团队自写了原生C++运行时渲染管线,以减少C#托管层与C++原生层之间的跨界开销。

  这里的每个环节,都涉及了对Unity引擎的一些底层架构做改动。

  你可能也注意到了:在GPU的图形处理能力越来越强的今天,终末地的关键优化技巧之一,是在一些步骤手写代码。

  Will解释说,他们也不是为了手写而手写——CPU软件遮挡其实不算全新概念,团队就参考过英特尔公开的论文,行业里也有其他游戏使用类似思路。

  但就像前面提到的,终末地在性能优化上面对的是复合高压。在游戏的PC与主机版本中,单个可操控角色精度最高的渲染层级是8万到10万面,在移动端也有4万到5万面;而战斗中涉及最多同时操控4名角色;此外还有大量动态对象持续参与渲染,再叠加较大范围的场景、远中近景的阴影,以及跨平台的观感一致性要求……林林总总,很难直接套用一个通用方案。

  严格来说,这套CPU光栅化在数学上并不要求修改Unity底层:单独写一个程序,也可以把几百个遮挡片画成深度图,再据此进行遮挡判断和剔除。

  但用Will的话来讲,这种做法的代价太“贵”了——为了让终末地能在主流PC、主机和移动端旗舰机上以60帧甚至更高帧率的高画质运行,那么一帧画面的完整预算就是16.66毫秒乃至更少,其中能留给CPU端遮挡和剔除的时间只有零点几毫秒。普通写法无法压进这个预算。

  Will告诉我,早期他们也实验性地尝试过如果尽量套用原生方案,Render Path只做简单排序等优化,那么游戏甚至难以稳定达到30帧。如今通过自研ECS、连续内存、多线程剔除、一次排序和原生C++管线,接成的这套完整架构,才达到了项目所追求的性能效用。

  2

  说到自研ECS,在上次的采访中,Will其实已经解释过终末地是如何使用ECS框架,来优化“大量物件同屏”情况下的画面效果。

  最新的「向渊行」版本也仍在增加更多的集成工业玩法设施种类

  ECS,即Entity Component System,一般译作“实体组件系统”。粗略来说,它让场景中的每个对象不再各自背着一整套复杂逻辑行动,而是把同类数据连续摆放,再统一成批处理。

  Unity为开发者提供了可选的Entities/DOTS ECS框架。ECS这类架构尤其适合处理成千上万个对象同屏的情况,但终末地还在工厂玩法上叠加了多名角色的同屏战斗、无缝箱庭和极远景深——通用框架就很难压进项目的性能预算。为此Will他们做了一系列相当激进的取舍:

  · 大约70种component ID全部在编译期写死;

  · 用固定的128-bit mask描述component组合;

  · 不做运行时类型注册、反射和字典查找;

  · 变长数据尽量内联到定长结构;

  · entity只是8字节handle;

  · job直接使用原生C++和显式fence;

  · 磁盘文件与运行时chunk尽量采用相同布局;

  · 结构变更集中批处理,不在帧中途随意发生。

  做到这地步,得到的其实就是一套专为终末地定制的ECS框架了,并由此实现了一些趋向极致的性能压榨。

  比如开发团队会预先对场景进行整理和打包,尽量让磁盘上的ECS数据与运行时的chunk——即存放同一种组件组合的一批实体数据的连续内存——采用相同布局。这样在加载这部分数据时,基本可以通过一次memcpy(内存复制)直接搬入内存,省去了逐对象解析、分配和重组。为了避免误解,需要强调一下这里说的只是预制ECS数据进入运行时的过程,并不等于完整场景的加载只需要一次内存复制。

  与此同时,就像大家所知道的,终末地里除了预制场景,还包含大量由玩家自由搭建的环境设施和工厂,开发组无法预先整理和打包。它们落地时会生成一批实体创建请求,再由系统通过批量结构变更写入相应chunk。入口虽然不同,但进入运行时后,两类对象会使用相同的连续内存布局、剔除、排序和渲染路径。

  在保持人物、玩家自定义场景和远景都以高质量画面呈现的基础上,实现高性能优化

  可以说自研ECS就是终末地整套优化的地基。数据只有先以可预测的方式排列好,后面的多线程、剔除和排序才可能快起来,也让CPU光栅化有条件被压进足够低的预算。

  在PC、主机和移动端等不同设备上,终末地的核心数据组织和运行时渲染架构保持一致,平台差异则留给画质、对象预算和底层后端处理。

  Will展示的移动端和PC案例中,系统先通过多种剔除方式把几十万个候选对象压到数万个,再把屏幕顺序、pipeline、mesh、材质和距离等信息编码进另一组128-bit排序键。经过一次排序,相同状态的对象会集中在一起,系统不必在每次draw之间反复绑定材质、贴图和pipeline。这套架构把两端Render Path本身的CPU开销压到了约1毫秒——同样为避免歧义强调一下,这只是其中一个模块,排序和分批另有开销。

  Will说他们在终末地这个项目上追求的是对性能优化的极致“压榨”,具体来说,就是不断检查每一笔性能开销是否真的带来了玩家能够感知的效果;如果收益不够,就继续删除、替换或重构。

  虽然Will不是有意对比,但在聊到性能压榨和技术取舍的时候,也提到AI已经是业内技术团队普遍在关注和实践的方向,鹰角的技术团队同样做过不少探索。

  实际上他们做过一项尝试:用AI来压缩游戏里的光照数据所占用的内存,以及缩小游戏的安装包体。

  这项方案做了比较长的时间,得到的压缩效果和开发工作流其实都比较理想。但最终他们发现,虽然包体和内存都变小了,运行时处理这些光照信息却会持续占用GPU,在部分中低端设备上的成本超过2毫秒,所以最终没有实装。

  结合前面提到的手写代码,其实就挺能体现鹰角技术部门内部所倡导的“实用主义”——手搓代码也好、AI也罢,最终都要服务于玩家能感知的体验,才会被应用到项目中。

  3

  不论是从这次的演讲内容还是采访交流中,都能感受到Will很在意自己讲的“干货”是不是足够“干”。他说自己作为技术人员将心比心,也总会希望自己特意去听的讲座里多些真东西。

  此前Unity也曾多次邀请鹰角做技术分享,但终末地当时还未上线,团队内部认为先让大家看到产品形态和实际效果,再讲技术如何实现,才是一个更踏实的顺序,所以都婉拒了;现在,玩家和同行都有了实机内容可以对照,对于其背后的深度定制后有了真正的好奇和猜想,这些技术分享才变得更有意义。

  Will回忆说,在演讲结束后,还有个仍是相关专业学生的韩国听讲者找到他,说自己毕业后想从事“二次元相关渲染”,中国公司在这个领域做得很厉害,但在韩国甚至很难找到对应的项目或工作机会。

  Will在会场内与听众交流

  在我看来,这次演讲的一个特别之处,就在于国内的游戏产品其实还是很少有机会像这样,在某一个技术领域内被业内公认为“标杆”,去国际上做一些足够具体、具有创新性的技术分享,构建一些外界对于国内游戏行业的新印象。

  过去几年里,海外行业所关注和认同的中国游戏厂商所擅长的创新,大多集中在产品形态上:把开放世界做成免费游戏,把主机级的内容规模塞进移动端,或者建立一套长期运营的内容生产方式,或者是在一些外围技术比如反外挂、AI语音识别等方面有领先,或是在营销领域能够用本土文化调动起一个庞大市场的消费力。

  这一切都有自身的含金量,但依然容易被归入“内容和商业模式创新”。

  包括在玩家层面,大家对中国厂商“技术强”的理解,通常也是“做得快、做得多、能把东西装进手机”。

  中国游戏行业并不缺优秀的工程师。移动端性能、网络架构、长期运营和大规模内容生产等领域,国内团队很早就处理过许多前沿问题。但在相当长的时间里,这些能力未必附着在一款能由团队自己定义和表达的产品上——有些被掩盖在外包和协作项目背后,有些又因为产品本身缺少辨识度,很难作为代表走到台前。

  今年的GDC上有不少来自中国厂商的演讲,但就像前面提到的,如今被加上“游戏节”后缀的GDC意图吸引更基础的开发者甚至普通玩家来参与,所以现场讲座大部分内容比较浅显,整体比起学术论坛更像是游戏展会了

  但终末地这次带出去讲的,都属于大型项目最底层的工程能力:内存里的数据布局、任务依赖与无锁并行、一次排序怎样减少状态切换、不同图形API如何被收进同一层抽象。这些是中国开发者在过去很少以自己的项目、自己的口径,在海外技术会议上得到关注的内容。

  回头来看,鹰角建立至今也不到十年,在技术层获得如此快速的发展还是非常令人感叹

  这种变化和“中国制造”在其他工业领域走过的路有些相似。最先被注意到的总是卷得飞起的产能和成本,后来才是工艺、标准、研发,以及属于自己的产品。只有当内容、品牌和技术同时长到一定程度,制造它的方法才会成为别人愿意研究的对象。

  结语

  很大程度上来讲,这也是我去和Will交流、以及写下这篇文章的理由。

  国产游戏行业近两年发展很快、变化也很大,作为玩家、作为媒体,我们也终于可以更大方更自豪地谈论“我们的游戏产业”,以及用更加平衡、而非过去那种充满羡慕甚至接近于朝圣的心态,去和其他国家的开发者们做交流。

  也正因如此,我更加感受到作为游戏媒体,理应去学习和了解更多的专业知识与行业见闻,才能匹配得上这种进步。

  终末地在「向渊行」版本更新后,也推出了由项目团队进行幕后创作分享的视频。由对应岗位的成员,从相对专业的角度分享美术、关卡与地图、玩法、剧情方面的创作故事,其中也会融入一些技术向科普,能看到玩家们对于这些内容其实也是感兴趣的。

  我相信,接下来也会有更多国内厂商像鹰角一样,愿意围绕着自己产品的技术强项来做对外分享——尽管比起美术、音乐、叙事、玩法策划等内容的创作故事,技术分享对于普通玩家的吸引力和宣传价值在现阶段明显要弱一些,要讲明白也更费劲。但这样的付出对于行业的长远健康发展而言,无疑有着深远的意义。