第 10 章

第七世代的熔炉

2005年11月21日深夜,华盛顿州雷德蒙德的气温降到了冰点以下。在微软总部园区外的临时帐篷里,数百人裹着外套跺脚取暖,等待午夜钟声敲响时Xbox 360的正式发售。队伍里混杂着大学生、收藏家和黄牛,但站在最前排的几个男人没有携带睡袋或折叠椅。他们手里提着一个黑色Pelican防护箱,里面装着一台安装了Unreal Engine 3性能分析工具的Xbox 360开发机。Epic Games的核心引擎程序员丹尼尔·赖特弯下腰,把防护箱平放在地面上,打开卡扣。

箱体内的泡沫衬垫里嵌着一台经过改装的零售版主机,外壳上贴着银灰色标签,标明这是Epic的财产,不得带出指定场所。这台机器在微软总部的安全实验室里已经运行过数千次基准测试,但那些测试使用的都是工程样机——芯片步进版本不同,内存时序不同,散热方案也不同。赖特和他的同事等待的不是玩《战争机器》。他们等待的是验证一个问题的答案——这个问题不是关于市场份额的预测,而是关于当制造三维世界的工具变得足够复杂时,谁还能掌握它。

在哥本哈根,Unity的创始人们刚刚在六月发布了他们的1.0版本,向世界宣告了另一种可能:让从未写过游戏代码的人也能制造三维世界。而现在,在雷德蒙德的寒夜里,第七世代硬件契约将用它的第一份性能日志,向所有引擎开发者提出同一个问题:你们准备好重新学习如何思考了吗?

他们等待的是看UE3在零售版Xbox 360的三核PowerPC处理器上能否达到E3演示时承诺的帧率。这个场景浓缩了第七世代主机战争的开场时刻。它不是在游戏发售日开始的,而是在引擎工程师蹲在冰冷的水泥地上等待验证数据的那个瞬间开始的。

因为这一代主机的发布不是一次性能升级——它是硬件契约的单方面改写。微软和索尼各自设计了一套架构,这两套架构的共同特征是:它们要求引擎在编译期就对并行计算、内存带宽和渲染管线做出深度优化决策。引擎不再是一份可以在不同硬件上跑起来的代码,它必须成为一套针对特定硬件拓扑进行预编译的构建系统。

Xbox 360的心脏是一颗代号Xenon的三核PowerPC处理器。每个核心支持双线程,总共六个硬件线程,运行频率为3.2GHz。这听起来像是PC多核处理器的自然延伸,但细节里藏着真正的断裂点:三个核心共享1MB的二级缓存,缓存延迟与命中率取决于线程调度策略;

内存子系统使用GDDR3,提供每秒22.4GB的带宽,但这512MB内存是CPU和GPU统一寻址的——没有独立的显存池。图形处理器Xenos采用统一着色器架构,48个着色器单元可以动态分配为顶点着色器或像素着色器,而不是像上一代那样固定分工。芯片上还嵌入了一块10MB的eDRAM,提供高达每秒256GB的内部带宽,用于实现几乎零成本的4倍多重采样抗锯齿。

这些数字对玩家没有意义。对引擎程序员而言,它们是一份新的语法规则。统一着色器意味着渲染管线不再有固定的顶点和像素边界,引擎可以在一个pass里完成以前需要多次切换的操作——但这要求着色器编译器在编译期就做出智能分配决策。统一内存架构意味着CPU和GPU共享同一块物理内存,消除了PCIe总线上的数据传输延迟,但也意味着两个处理器争夺同一个带宽池。

eDRAM的10MB容量刚好够容纳一个1280x720分辨率的颜色缓冲和深度缓冲,但如果分辨率提高或者需要额外的渲染目标,就必须将数据分块进出eDRAM,而分块策略必须在引擎管线设计阶段就确定下来。赖特和他的团队在E3 2005上展示的UE3演示运行在一台Alpha开发套件上,那台机器的内存配置比零售版更慷慨。

现在他们需要确认零售版硬件能否承受同样的负载。午夜过后,赖特拿到了第一台从收银台传递出来的Xbox 360。他没有拆开包装盒欣赏工业设计,而是直接走进微软为合作伙伴预留的测试室,接上电源,插入调试线缆,启动性能分析工具。

屏幕上跳出的第一组数据是帧时间分布图。UE3的渲染管线在Xenon的三个核心上被分解为六个主要线程:主线程负责游戏逻辑和可见性判断,渲染线程负责生成绘制命令,三个工作线程处理物理模拟、动画混合和粒子更新,还有一个线程专门处理音频和网络。

这种分解不是运行时自动分配的——它是在编译期通过Epic自己开发的并行任务调度器硬编码到引擎里的。每个线程被锁定到特定核心,缓存亲和性被手动优化,数据结构被重新排列以最小化跨核心的缓存失效。赖特后来在2006年GDC的一次闭门会议上描述了这一过程:让UE3在Xbox 360上达到稳定30帧的初期目标,花掉了引擎团队整整三个月的时间——这三个月不是在修复bug,而是在重新学习如何思考并行。

这就是第七世代硬件契约的核心条款。在第六世代,一台PlayStation 2的架构虽然怪异——情感引擎的向量单元和图形合成器的eDRAM同样需要特殊处理——但PC和Xbox使用的x86与DirectX组合仍然维持着一定程度的抽象一致性。引擎可以在运行时查询硬件能力,动态调整渲染路径和资源管理策略。

第七世代终结了这种灵活性。Xbox 360的统一着色器和统一内存要求引擎在编译期就确定数据流向。

而PlayStation 3更进一步——它把硬件的异质性推到了极端。2006年11月11日,PlayStation 3在日本首发。它的核心是一块Cell宽带引擎处理器,由索尼、东芝和IBM联合设计。Cell包含一个主处理单元和八个协处理单元,每个协处理单元拥有256KB的本地存储——不是缓存,是直接由程序员管理的便签式存储器。这些协处理单元可以以惊人的速度处理单指令多数据运算,理论上每个单元可以达到25.6Gflops的浮点性能,八个单元加起来超过200Gflops。

但这份力量附带着一个苛刻的条件:数据必须在协处理单元的256KB本地存储和主内存之间显式搬运,通过直接内存访问完成。没有硬件缓存来隐藏延迟,没有自动预取机制来猜测程序员意图。每一个字节的移动都必须由代码精确编排。PS3的图形处理器RSX基于NVIDIA的G70架构,与Cell通过FlexIO总线连接。

这条总线提供每秒20GB的读取带宽和每秒15GB的写入带宽——听起来足够快,但比Xbox 360的统一内存带宽低得多。更关键的是,RSX的256MB GDDR3显存与Cell的256MB XDR主内存是物理分离的。数据在这两个池之间移动需要经过FlexIO总线,而这条总线的延迟远高于芯片内部通信。

这两套架构共同构成了一座熔炉。它们不是在既有道路上拓宽车道,而是要求每一支引擎团队重新学习驾驶方式。选择在编译期深度优化某一套架构,就意味着放弃在另一套架构上的移植便利性。选择维持抽象层的通用性,就意味着无法榨取任何一台主机的全部性能。

这是引擎史上最严酷的一次站队考验。id Software的约翰·卡马克在2006年初的一次内部技术评审中遭遇了这个考验的锋利边缘。id Tech 5的开发已经进行了两年,其核心技术支柱是MegaTexture——一种虚拟纹理技术,允许在有限内存中显示理论上无限大的纹理细节。

原理是将一张巨大的纹理预先分割为小块,根据摄像机位置动态流式传输当前需要的小块到内存中。在PC上,这个方案依赖于大容量系统内存和高速硬盘来缓存纹理页表。在Xbox 360上,统一内存架构和DVD驱动器提供了足够的回旋余地。

但在PS3上,卡马克发现了一个无法绕过的障碍。MegaTexture的纹理查找表需要存储每一块纹理在内存中的位置、分辨率级别和加载状态。对于一个典型的开放世界场景,这个查找表的大小在数十兆字节级别。在Xbox 360上,这可以放在共享的512MB系统内存中。在PS3上,Cell的协处理单元是处理纹理流式传输的理想选择——它们的SIMD架构可以高效执行压缩和解压缩操作,DMA引擎可以异步搬运数据。但每个协处理单元只有256KB的本地存储。

查找表无法完整装入任何一个单元。它必须被切分,通过DMA分批搬运进出本地存储。

而每次DMA操作都有延迟开销,当玩家快速移动时,协处理单元必须在256KB的窗口中不断换入换出查找表片段,导致纹理数据无法及时送达RSX。卡马克在评审会上画出了数据流图。协处理单元从XDR主内存中读取压缩纹理块,解压后通过FlexIO总线传输到RSX的显存中。这个管道的理论带宽受三个瓶颈制约:协处理单元的DMA吞吐量、FlexIO总线的可用带宽、以及RSX显存的写入速率。在最理想的情况下——所有数据对齐完美,DMA链无缝衔接——管道可以接近FlexIO的峰值带宽。但加上查找表的分页开销后,实际吞吐量骤降到理论值的一半以下。

这不是性能优化能解决的问题。这是架构假设的根本冲突。MegaTexture的设计假设了平坦的大容量内存空间来存放查找表,而Cell的架构强制使用分块的、显式管理的便签式存储。卡马克在那次会议上没有宣布放弃PS3版本,但他的措辞让在场的工程师们明白了严重性。

他告诉团队,这不是一个能在六个月内修复的问题,而是需要重新思考整个资源流式传输策略的问题。《Rage》项目的发售日期从2007年推迟到2008年,再推迟到2009年,最终在2010年10月才上市。PS3版本在发售时仍然存在纹理加载延迟——当玩家快速转身时,地面纹理会短暂显示为模糊的低分辨率版本,然后才被高分辨率替换。这不是bug,这是架构冲突在用户体验层面的可见伤痕。

而Epic Games走了一条完全不同的路。UE3从一开始就与Xbox 360的架构高度耦合。Epic的工程师们深入参与了微软的开发套件设计过程,他们的反馈影响了Xbox 360的内存子系统配置和图形API设计。UE3的着色器编译器针对Xenos的统一着色器架构进行了专门优化,能够自动分析着色器代码的算术强度和纹理采样模式,将负载均衡分配到48个着色器单元上。

引擎的渲染器充分利用了eDRAM的10MB空间,将颜色缓冲、深度缓冲和模板缓冲全部放入eDRAM,实现了零成本的4倍MSAA抗锯齿——这在PC上需要消耗大量显存带宽。UE3的内存分配器针对统一内存架构设计了自定义的池化策略,避免了CPU和GPU之间的数据复制。

这种深度耦合的结果是《战争机器》。2006年11月发售时,这款游戏定义了整个第七世代的视觉标准。泥灰色调的废墟城市、角色盔甲上的划痕反射、电锯枪刃口的光芒——这些效果不是来自任何单一的技术突破,而是来自UE3对Xbox 360架构的系统性利用。Epic在GDC 2007上公开了部分性能数据:《战争机器》在720p分辨率下运行在30帧,每个像素经过至少三次渲染pass——基础颜色pass、光照pass和后处理pass——全部在eDRAM内部完成,不消耗外部内存带宽。这只有在编译期就针对eDRAM容量精确规划渲染目标尺寸和格式的前提下才能实现。代价是移植。

当Epic将UE3移植到PS3时,引擎团队发现Xbox 360版本中大量针对统一内存和eDRAM的优化策略完全失效。PS3的分离式内存要求数据在XDR主内存和GDDR3显存之间显式搬运,而FlexIO总线的带宽远低于Xbox 360的内部带宽。UE3在PS3上的初始性能比Xbox 360版本低了近百分之四十。Epic不得不组建一支专门的PS3优化团队,重写了渲染器的资源管理模块,将原本在eDRAM中一次性完成的多次渲染pass拆分为分块处理,通过协处理单元来协调数据搬运。这个过程花了将近一年时间。《虚幻竞技场3》的PS3版本直到2007年12月才发售,比Xbox 360版本晚了五个月。

id和Epic代表了第七世代熔炉中的两种典型命运:一种因为架构冲突而陷入开发泥潭,一种因为深度耦合而获得先发优势但付出了沉重的移植成本。还有第三种命运——缺席。

Unity的创始人们在2005年6月发布了Unity 1.0,目标平台是Mac OS X。当Xbox 360和PS3在2005年和2006年相继登场时,Unity完全没有参与这场主机战争。2005年6月发布的Unity 1.0.1只支持Mac平台的Web播放器和独立应用导出。直到2009年3月,Unity 2.5才加入了对Windows的支持。对Xbox 360和PS3的支持来得更晚——Unity直到2013年11月才宣布与Xbox One合作,而那时第七世代已经接近尾声。

这种缺席不是战略失误,而是战略选择的必然结果。Unity的轻量级运行时和抽象层设计意味着它不需要在编译期对特定硬件拓扑做极致优化,因为它瞄准的本来就不是硬件性能的边界。Unity的目标是让一个从未写过游戏代码的人也能制造三维世界——这个目标要求引擎隐藏硬件的复杂性,而不是暴露它。

这正是第9章结尾那个追问的答案:当制造三维世界的工具变得足够简单时,谁来制造世界?Unity的回答是:所有人。但这个回答在第七世代的熔炉里意味着放弃在主机战场上竞争AAA级别的视觉标准。

隐藏复杂性意味着放弃性能,而放弃性能意味着无法在主机平台上竞争AAA级别的视觉标准。但缺席也意味着幸存。Unity没有被拖入Xbox 360和PS3之间的编译期优化战争。它没有像id Tech 5那样在Cell架构上遭遇架构冲突,也没有像UE3那样为深度耦合付出跨平台移植的代价。

当Epic和id的工程师们在2006年到2009年间夜以继日地与硬件搏斗时,Unity的团队在哥本哈根专注于另一件事:让引擎在Mac和Windows上都能简单运行,让iPhone成为新的目标平台,让独立开发者能够用几百美元而不是几十万美元的预算制作游戏。2009年10月,Unity 2.6独立版开始免费。

这个时间节点与第七世代主机战争的高峰期重合——《战争机器2》刚刚发售,《杀戮地带2》展示了PS3的极限性能,《使命召唤:现代战争2》即将引爆销量纪录。在这些头条新闻的阴影下,Unity的免费策略几乎没有引起主流游戏媒体的注意。

但它打开了一扇门。那些被主机开发的高昂门槛挡在外面的创作者——学生、独立开发者、小型工作室——开始涌入Unity的生态系统。他们制作的游戏不会出现在GameStop的货架上,不会在E3的展台上演示,不会得到Metacritic的评分。但它们存在于App Store上,存在于浏览器的Flash播放器中,存在于Steam的新兴独立游戏分类里。

这第三种命运揭示了一个反直觉的事实:在第七世代的熔炉中,生存下来的策略不是适应高温,而是暂时离开熔炉。Unity的缺席让它避开了硬件契约重构带来的编译期优化军备竞赛,保存了技术资源和管理精力,在主流战场之外培育了一个新的开发者群体。当第八世代主机在2013年到来时——Xbox One使用x86架构,PlayStation 4也使用x86架构,两者都采用统一内存设计——Unity带着已经积累数百万注册开发者的社区重新进入主机市场。

那时,id Tech 5已经因为《Rage》的漫长开发周期耗尽了id Software的精力,UE3虽然统治了第七世代但在跨代移植时背负着沉重的遗留代码包袱。这场熔炉考验的不仅仅是技术能力。它考验的是引擎开发者对硬件契约本质的理解深度。

微软和索尼在2005年做出的架构选择,不是简单的工程权衡——它们是两种不同的哲学立场。微软选择了统一:统一的内存、统一的着色器、统一的开发工具链。这种选择降低了引擎开发者的初始学习成本,使得从PC向Xbox 360移植相对顺畅。但它也意味着引擎必须接受微软设定的抽象层次,放弃对硬件的极致控制。

索尼选择了分离:分离的内存池、分离的处理器架构、分离的编程模型。这种选择给了开发者前所未有的控制力——如果愿意投入足够的工程资源,PS3理论上可以做到Xbox 360做不到的事情。但它也意味着学习曲线陡峭到几乎垂直。两种哲学立场在引擎产业中制造了一道裂谷。

在id Software内部,卡马克的评审结论引发了一场静默的地震。不是因为某个不可逾越的技术障碍——id的老兵们经历过从软渲染到硬件加速的每一次阵痛,他们习惯了卡马克在最后一刻找到解决方案。

这次的不同在于时间线的性质。MegaTexture在PS3上的问题不是算法优化的问题,而是物理约束的问题。256KB的本地存储不是一个可以通过更聪明的代码绕过的限制,它是硅片上蚀刻的边界。

卡马克的笔记本上画满了DMA调度时序图,每一张图都指向同一个结论:在玩家快速移动时,协处理单元无法在256KB的窗口内同时容纳查找表片段和解压后的纹理数据。他尝试过将查找表压缩,但压缩后的查找表仍然超过200KB,留给纹理数据的空间不足56KB——这勉强够存放一两块高分辨率纹理块,但远不足以覆盖屏幕视野。他尝试过将查找表卸载到RSX的显存中,但FlexIO总线的延迟使得每次查表操作都成为管线中的阻塞点。他尝试过让主处理单元承担部分查表工作,但主处理单元与协处理单元之间的同步开销吞噬了所有性能收益。每一次尝试都像是一只手试图堵住堤坝上的裂缝,而裂缝的数量在不断增加。

到2006年夏天,id Tech 5的PS3版本进度已经明显落后于Xbox 360版本。这不仅仅是技术问题,它开始影响工作室的组织结构。id的工程师团队被分裂成两个小组:一组继续在Xbox 360上推进MegaTexture的完整实现,另一组在PS3上尝试各种折中方案。两组人使用同一套代码库,但代码中的条件编译分支越来越多,越来越深。Xbox 360路径上添加的每一个新特性都必须在PS3路径上进行评估——它是否依赖统一内存的低延迟?它是否假设了超过256KB的连续数据空间?它是否使用了eDRAM特有的渲染目标格式?每一次评估都可能得出一个否定的答案,而每一个否定的答案都意味着需要为PS3编写一套完全不同的实现。这不是跨平台开发,这是用两套引擎开发同一款游戏。

卡马克在2007年QuakeCon的闭门会议上向社区坦白了这个困境,他的措辞比内部评审时更加直接:“PS3是一台强大的硬件,但它的力量需要一种不同的编程思维。我们低估了这种思维的转换成本。”

与此同时,Epic的PS3移植团队也在承受着另一种形式的痛苦。UE3在Xbox 360上的成功建立在一套精密的编译期优化体系之上——着色器编译器了解Xenos的每一个执行单元,内存分配器了解eDRAM的每一字节容量,任务调度器了解Xenon每一个核心的缓存行大小。当这套体系被搬运到PS3上时,它像是一台被拆散后装错位置的精密仪器。

Epic的工程师们发现,Xbox 360版本中流畅运行的六个主要线程在PS3上必须被重新分解——不是因为PS3的核心数不够,而是因为Cell的协处理单元与PowerPC主核心之间的通信模型与Xenon的三核对称设计完全不同。在Xenon上,线程之间通过共享二级缓存交换数据,同步开销相对较低。在Cell上,协处理单元之间的数据交换必须通过主内存中转,每一次同步都意味着DMA操作的延迟累积。Epic的解决方案是将部分渲染任务从协处理单元移回主处理单元,牺牲并行度来换取更简单的同步模型。

但这意味着PS3版本无法利用Cell的全部计算能力——那些被宣传为“超级计算机级”的浮点运算单元,有一部分只能闲置。这是深度耦合的另一面代价:当你为一套架构做了极致优化之后,反向适配另一套架构的成本不是线性的,而是指数级的。

裂谷的一侧是Epic代表的深度耦合路线:选择一方,全力优化,接受移植成本。裂谷的另一侧是id代表的通用架构路线:设计一套理论上可以跨平台的系统,然后在每一台具体主机上与硬件现实搏斗。

裂谷之外还有Unity代表的缺席路线:不参与这场战争,在另一个战场上培育完全不同的能力。2005年11月22日凌晨三点,丹尼尔·赖特在微软总部的测试室里合上了笔记本电脑。UE3在零售版Xbox 360上的帧率达到了预期——每秒30帧,偶尔跌到28帧,但在可接受的范围内。他将性能日志保存到U盘里,收拾好设备,走出大楼。停车场里还有没散去的小群玩家,他们抱着新买的白色主机,讨论着《使命召唤2》和《完美黑暗零》的画面有多惊人。没有人注意到一个穿着Epic夹克的男人拎着黑色防护箱走过他们身边。那个防护箱里装着的不是一款游戏,而是一份答案。这份答案确认了Epic在第七世代熔炉中选择了哪一条路——深度耦合、编译期优化、与单一硬件平台结盟。

这条路会在接下来的五年里让UE3成为最广泛授权的第三方引擎,出现在从《质量效应》到《蝙蝠侠:阿卡姆疯人院》的数百款游戏中。但这条路也意味着当第八世代到来时,Epic必须重新解构自己的引擎架构,剥离那些与Xbox 360硬件特性深度绑定的优化层。

而在哥本哈根,三位Unity创始人还在处理客户支持邮件。他们的引擎还无法在任何一台主机上运行。他们还不知道,缺席本身正在成为另一种形式的准备。

当熔炉的温度升到最高时,不是所有进入者都能完整地走出来。有些被烧熔变形,有些被打造成型,有些——那些根本没有进入的——保存了在下一个时代重新定义游戏规则的可能性。

引擎产业的版图在这一刻已经被重新划分。不是因为任何一场正面的技术对决,而是因为三支团队在面对同一份硬件契约时做出了三种根本不同的选择。这些选择的后果不会立即显现,但它们已经嵌入了每一行代码、每一个架构决策、每一份授权合同中。第七世代的熔炉在2005年11月的这个寒夜里点燃了第一把火,而它的余焰将持续燃烧到下一个十年。