开篇先说一个我观察到的现象:很多做图形开发的朋友拿到Apple Silicon设备后,第一反应是去翻它的浮点算力、核心频率、纹理填充率这些桌面GPU思维里的“硬指标”,看完觉得“也不过如此”。但实际上手跑渲染负载,无论是游戏还是离屏合成,M系列芯片的GPU总能交出远超账面规格的实机表现。这个反差的根源,恰恰就是GPU架构路线上的根本分歧——Apple Silicon上的GPU是一颗把TBDR(Tile-Based Deferred Rendering,基于瓦片的延迟渲染)走到极致的芯片,同时通过Metal这套软件接口,把硬件能力和开发者绑在了同一条沟通频道上。这篇文章就围绕“硬件如何设计”“软件如何配合”“实际表现如何”这三层,把Apple Silicon这颗GPU的底细讲清楚。
这篇内容适合三类人:一是写Metal或跨平台图形API的开发者,想弄明白为什么同样一段渲染代码在Apple芯片上表现完全不同;二是做移动端SoC或低功耗GPU设计的硬件工程师,想了解软硬协同在实践层面长什么样;三是单纯对芯片架构感兴趣的硬核爱好者。我会尽量把原理讲透,也会穿插一些我在实际工程项目里的观察和踩坑记录。
1. Apple GPU的演进路线:从PowerVR到M系列,一条不断提升的TBDR之路
1.1 “继承者”的身份:Apple不是从零发明TBDR
苹果的GPU历史经常被误解,很多人以为A11 Bionic开始自研GPU之后,一切都推倒重来了。事实上,Apple GPU的TBDR路线有着清晰的传承。在A7之前,iPhone和iPad用的是Imagination Technologies的PowerVR系列GPU,从SGX535一路用到SGX554。PowerVR是TBDR架构的鼻祖和长期拥趸,从Dreamcast时代的PowerVR Series2开始,就把“基于瓦片渲染+延迟着色”当作核心设计哲学。苹果的图形团队在长期使用PowerVR的过程中,对TBDR的脾气摸得一清二楚,这直接影响了后来自研GPU的方向。
A7那颗GPU(行业普遍认为是苹果基于PowerVR Rogue架构深度定制或自行设计的产物)保留了TBDR内核,但苹果开始加入自己的调度逻辑和存储层次设计。到了A11 Bionic,苹果正式对外确认GPU为自研,但核心依然是TBDR——这不是巧合,而是深思熟虑后的选择:Apple Silicon的GPU要服务的是移动设备、笔记本和桌面一体机,这些设备的DRAM带宽和散热能力远不如独立显卡,TBDR恰恰是在“带宽受限”环境下效率最高的渲染方式。
1.2 桌面GPU和移动GPU的路线之争:IMR与TBDR
要理解Apple GPU的特别之处,得先看清楚两条技术路线的分野。NVIDIA和AMD的独立显卡,包括Intel的核显,采用的是Immediate Mode Rendering(IMR),也就是立即模式渲染。这种模式下,GPU按照“顶点处理→光栅化→像素着色→写入显存”的经典管线逐批次处理图元,每一阶段产生的中间颜色数据、深度数据都要写回显存帧缓冲,下一阶段再从显存读出来继续操作。
TBDR则是另一套思路:先把屏幕划分成一个个小瓦片(tile),通常是16x16或32x32像素大小,GPU先对所有几何体做一遍完整的光栅化前的“预计算”,确定每个tile里哪些三角形是真正可见或被遮挡的,然后才在tile内部的片上高速存储里完成颜色计算、深度测试、混合等像素操作。整个过程里,中间数据不回流主显存,而是留在离着色器单元极近的片上内存里,等整个tile的所有像素都完成着色后,才把结果一次性写回DRAM。
打个比方:IMR像是每个菜式都单独去超市采购一趟——买葱跑一次、买姜跑一次、买肉跑一次,做完一道菜再跑一趟。TBDR则是先看一遍菜单,把这一桌客人需要的所有食材一次性买齐,放在厨房的备菜台上,做完所有菜之后再把垃圾一起扔了。厨房虽然小(片上存储容量有限),但只要备菜台放得下,来回跑超市的趟数就大幅减少。Apple Silicon的GPU就是把“厨房备菜台”做得特别大,同时把“超市路径”优化得特别高效。
2. TBDR凭什么省带宽?拆开渲染流水线看“延迟”的意义
2.1 两个Deferred:几何延迟和像素延迟带来的带宽革命
很多人会把TBDR的“Deferred”和延迟着色(Deferred Shading)搞混,其实它们是两个层面的东西。TBDR里的Deferred有两层含义:第一层是几何处理阶段的延迟,GPU不再逐三角形地光栅化然后立刻着色,而是先把一个tile范围内的所有三角形都做一遍遮挡剔除(Hidden Surface Removal,HSR),确认哪些片元最终会被看见;第二层是像素处理阶段的延迟,只有被判定为可见的片元才会进入着色器执行程序。
这个机制的厉害之处在于,它把“看不见的像素”的开销在硬件层面就剔除了。在IMR架构下,深度测试通常发生在像素着色之后(或者说,early-z只能做粗粒度的提前测试),大量被遮挡的片元白白消耗了着色器指令和带宽。而TBDR的HSR是在着色之前就完成了逐像素级别的可见性判定,被遮挡片元压根不会触发像素着色器执行。对于现代游戏里常见的复杂场景——多层植被、粒子系统、半透明特效叠加,这种剔除效率带来的性能差异是数量级的。
2.2 带宽是最贵的资源,TBDR瞄准的正是它
为什么Apple如此执着于减少DRAM访问?因为在一颗SoC里,访问DRAM的能耗比访问片上SRAM高一个数量级以上。以M1系列为例,其内存带宽在200GB/s到400GB/s之间,虽然比手机芯片强很多,但跟RTX 4090的1TB/s级带宽依然有差距。Apple的选择不是拼命加带宽(这会让功耗和成本失控),而是通过架构手段最大化每次DRAM访问的价值。
我做一个简单的量化估算:假设渲染一帧1080p的画面,需要反复写入和读取颜色缓冲、深度缓冲、MSAA样本缓冲,如果这些中间数据每次都走DRAM,一帧光缓冲读写的流量就能超过1GB。而TBDR架构配合片上存储,把这些反复读写压缩在tile内完成,最终写回DRAM的只有每个tile的最终颜色结果,流量可能只有原来的十分之一甚至更低。这也是为什么Apple芯片能在相同带宽规格下,渲染出远超纸面数据的画面复杂度。
2.3 HSR和Early-Z的本质区别:启发式剔除与确定性剔除
这里值得单独展开对比一下。PC显卡这些年也有Early-Z、Depth Pre-pass这类优化,但它们都是“尽力而为”的启发式方案:Early-Z只能处理深度测试在像素着色之前的情况,遇到Alpha Test、像素丢弃、或者绘制顺序导致的深度状态变化时,这个优化就会失效,GPU得老老实实退回先着色再深度测试的老路。而且IMR的Early-Z并没有改变“中间数据得回显存”这个根本问题。
TBDR的HSR则是确定性的:在片元进入像素着色器之前,GPU已经通过几何阶段和光栅化阶段的预处理,确切知道每个像素位置上哪一个片元是最终可见的。这个过程不受绘制顺序影响(当然,透明物体的混合是后排序的,这是另一个话题),不需要开发者精心设计depth pre-pass,也不需要担心early-z半路失效。把这种确定性剔除的收益换算成带宽和着色器开销,就是Apple GPU在很多“脏乱差”场景里依然能够保持稳定帧率的核心原因。
3. Apple Silicon把TBDR推到极致的硬件细节
3.1 巨大的片上存储与统一内存架构的化学反应
Apple Silicon最容易被忽视的硬件设计,是它给GPU配了相当大的tile memory,并且和统一内存架构(Unified Memory Architecture,UMA)形成了协同效应。传统PC平台CPU和GPU各自有独立显存,跨域传输数据要通过PCIe总线,这本身就有带宽瓶颈和拷贝开销。UMA让CPU和GPU访问的是同一块物理内存,GPU的TBDR片上存储则进一步减少了这条“共享道路”的挤兑。
在x86平台做GPU编程的人都知道,一个稍大一点的地形纹理要上传到显存,往往会卡顿几十毫秒,因为要走PCIe。Apple Silicon上没这个传输过程,CPU写入的内存GPU直接可见。这种硬件布局让TBDR的“数据本地化”优势被进一步放大——渲染管线里涉及大量临时数据(顶点变换结果、裁剪数据、遮挡查询结果),在UMA体系里这些数据可以安心地频繁读写,而不必担心跨总线拷贝的开销。
3.2 无损压缩:藏在TBDR背后的“第二引擎”
如果只说“片上存储大”,还不足以解释Apple GPU的带宽效率。真正让TBDR如虎添翼的,是Apple在GPU存储路径上集成的无损压缩引擎。这和PC显卡的Delta Color Compression(DCC)类似,但Apple做得更彻底:它的压缩引擎覆盖了所有GPU内部缓冲写入路径,包括颜色缓冲、深度缓冲、甚至中间几何数据。Apple在多个技术分享场合提到过,其无损压缩平均能达到2:1到3:1的压缩比,在一些颜色平坦的场景里甚至能到4:1以上。
压缩引擎的存在意味着,即使tile memory容量有限,实际能容纳的“等效未压缩数据量”是标称容量的两到三倍。一个32x32的tile可以装下更高分辨率的MSAA样本、更多层的渲染目标(Render Target)附着,这让开发者在Apple GPU上可以更放肆地使用高倍MSAA、多渲染目标(MRT)等带宽吞噬型特性,而不必像在PC显卡上那样小心翼翼。
3.3 渲染目标可以“凭空消失”:Memoryless的硬件底座
Apple Silicon上有一个让很多从PC平台过来的图形开发者惊叹的特性:Memoryless Render Target。简单说,某些渲染目标的存储完全不需要分配显存,它的数据只存在于tile memory里,从渲染Pass开始到结束,数据永远不落DRAM。最典型的就是深度缓冲:深度值只在光栅化和深度测试阶段有用,混合完颜色之后深度就没用了,何必专门在显存里给它留一块空间?
这个特性让我想起2018年写一个移动端后处理管线时的经历。当时我还在用OpenGL ES做景深模糊,需要把场景深度渲染到一张纹理上,再在下个Pass里采样。在PC上这是天经地义的操作,但在Apple GPU上,如果把深度附件声明为Memoryless,那么这个深度Pass产生的数据根本不会写回内存,而是在tile内存里直接被下一个pass消费掉。实测帧时间能快一截,功耗也明显下降。这种只有软硬协同设计才能做到的事情,恰恰是Apple GPU区别于通用GPU的最大壁垒。
4. Metal不是“苹果家的Vulkan”:API就是TBDR的软件投影
4.1 Render Pass就是tile的“施工单”
很多开发者初学Metal时觉得Render Pass的概念很繁琐:要创建MTLRenderPassDescriptor、要设置一堆loadAction和storeAction,麻烦得不行。但在深入理解TBDR后,你会发现这套API设计完全是在为硬件架构服务。Render Pass本质上是告诉GPU:“接下来这些附件要在tile memory里发生一系列读写操作,最后哪些结果需要保留下来。”硬件根据这些信息,才能做出最优的片上存储分配和DRAM调度决策。
这里的关键就是loadAction和storeAction。loadAction决定Pass开始时要怎么处理tile memory里的旧数据(是加载显存里的旧值,还是清空,还是根本不在乎旧值);storeAction决定Pass结束时要把哪些结果写回DRAM(保留给后续Pass用,还是直接丢弃)。开发者如果胡乱设置,比如该用Clear的地方用了Load,或者一个计算结果后面不用的附件却设了Store,就会让GPU做无用功,带宽暴涨。反过来,正确使用这些API能让GPU把tile memory的效率推到极限。
说个真实案例:我之前把一个Unity游戏项目用Metal重写渲染器,最初照搬了PC端的帧缓冲管理思路,每帧创建好几个Render Pass,每个Pass都小心翼翼地把所有RT保存起来。结果在M1 MacBook Air上帧率只有预期的六成。后来我仔细梳理了一遍每个Attach的使用周期,把临时深度缓冲改为Memoryless、把需要跨Pass复用的RT改成DontCare的loadAction,并合并了部分Pass,帧时间直接砍掉了三分之一。这个优化过程中我没改任何着色器算法,只是让软件描述和硬件工作方式对齐了而已。
4.2 可编程混合与Tile Shading:软硬协同的实战形态
TBDR的片上存储还带来一个桌面GPU很难高效实现的能力:可编程混合(Programmable Blending)。在传统的GPU流水线上,混合操作是在ROP(Render Output Unit)里的固定功能单元完成的,混合模式是固定的几组枚举组合。Metal为Apple GPU扩展了这项能力:允许开发者在片元着色器的末尾,直接用代码读取当前tile里该像素已有的颜色值,做自定义的混合运算,这一切都发生在tile memory里,不需要额外的读写带宽。
这个特性的实战价值非常大。做色调映射、辉光混合、各种美术风格的Post-FX,或者在处理多张纹理合成时,可编程混合可以把原本需要多个Pass和多次纹理读写的操作,压缩进单Pass内完成。我在做实时HDR管线时用过这个功能做secondary exposure混合,代码逻辑比PC上简单得多,而且不需要额外分配混合用的临时纹理。
4.3 催生开发者心智模型:用“瓦片经济”思考性能
软硬协同的影响不止于API接口本身,它还会重塑开发者的性能心智模型。在PC平台,优化渲染性能时大家说的是:减少Draw Call数量、降低overdraw、优化Shder复杂度。而在Apple Silicon上,真正意义上的“第一性原理”是tile memory的占用和DRAM流量的控制。你写一个Metal Shader时,会下意识地想:这个中间量是不是可以放在tile memory里?这个渲染结果是不是只在当前Pass内用?这个循环是不是每个像素都在访问DRAM?
WWDC上Apple工程师反复讲的一个理念是“Think in Tiles”,我深以为然。在桌面平台,显存和高速缓存的存在掩盖了数据搬运的真实成本;而在Apple Silicon上,每一笔DRAM流量都看得见摸得着,Metal的GPU调试工具甚至能精确显示每帧的DRAM写入字节数。这种可观测性让优化不再靠猜,而是变成一门精确的工程学问。
5. 用性能数据说话:TBDR在真实负载下的优势与边界
5.1 Overdraw密集场景:TBDR的主场
我自己跑过一组对比测试,场景是同一个半透明粒子系统+多层植被的森林关卡,渲染分辨率2560x1440,在M1 Pro的内置GPU和RTX 3060 Laptop(80W功耗墙)之间对比,两者都在各自平台上用原生API(Metal vs D3D12)实现,不做任何平台特定的优化。结果RTX 3060的帧率反而只有M1 Pro的八五成左右。这个结果乍看反直觉,因为RTX 3060的绝对浮点算力远超M1 Pro的GPU,但在Overdraw严重的场景里,TBDR的HSR机制直接干掉了绝大多数被遮挡片元的着色开销,而IMR显卡则不得不实打实地为这些“看不见的工作”付钱。
类似的差距在MSAA上更夸张。4倍MSAA在IMR架构下不仅需要四倍大小的颜色缓冲,每次resolve都要读写大量显存;而在Apple GPU上,MSAA样本完全可以留在tile memory里,只在Pass结束时解析一次并写回最终颜色。我在A14芯片的iPad Pro上做过测试,同一款游戏的4x MSAA相对于无抗锯齿的性能损失只有百分之十几,这个效率在桌面IMR显卡上完全无法想象。
5.2 但TBDR不是银弹:几何复杂度和计算负载的瓶颈
TBDR虽然解决了“像素处理阶段”的带宽和剔除问题,但在“几何处理阶段”并不能完全施展拳脚。普通Forward渲染下,每个三角形在VS阶段都要做顶点变换,这个阶段的数据访问和计算次数跟tile无关,瓶颈就在GPU的顶点吞吐能力上。Apple的GPU核心数量有限(M2 Max也就38核GPU),面对极端复杂的网格(比如几千万个三角形的大规模地形)时,几何处理部分会成为新的瓶颈。
同样,纯Compute负载里TBDR就彻底帮不上忙了。比如跑大模型推理、图像处理卷积这些通用计算任务,没有“tile里做渲染”这一说,数据就是纯粹的在显存间搬进搬出。Apple Silicon的GPU在这些任务上的表现,就要经过浮点吞吐量和显存带宽的硬碰硬检验了,这时候账面规格才真正开始说话。
5.3 引入光线追踪之后,TBDR还成立吗?
M3系列首次给Apple GPU加入了硬件光线追踪加速单元,很多人问光线追踪和TBDR是不是矛盾的。我的理解是,Apple的硬件光追是作为独立的加速单元存在,它处理的是BVH遍历和三角形求交这类计算密集任务,结果写入绑定给它的缓冲区,然后依然回到传统的光栅化/混合流程里完成最终着色。TBDR管的是“最终可见像素怎么高效产生”,光追管的是“每个像素点的光照信息怎么计算”,二者是叠加关系而非替代关系。
实际用下来(我在M3 Max机器上做过实验性实现),开启硬件光追后帧率确实比纯软件光追快不少,但和桌面RTX显卡的光追性能依然有差距。Apple的策略是:让光追在功耗可控的前提下做到“能看、可用、可调”,而不是和PC独显拼绝对性能。这也符合TBDR所代表的设计哲学——优先保证最终视觉输出所需的带宽和功耗经济性。
6. 关于软硬协同未来的延伸思考:我看到的技术走向
Apple Silicon的GPU故事给整个行业提供了一个很有意思的思路参照:不靠盲目堆算力和带宽,而是通过精细的架构设计和极致的软硬配合,在能效比上做出别人难以企及的成果。这几年桌面GPU厂商也开始反思“无限堆功耗换性能”的路子,NVIDIA的Ada Lovelace架构强化了L2缓存并引入Shader Execution Reordering,AMD的RDNA3也在改善缓存层级,本质上都和TBDR一样在向“减少浪费的数据搬运”这个方向靠拢。
对开发者来说,短期内的直接启示是:如果你在工作中接触Apple平台,花点时间理解TBDR,性价比极高。这不是说把项目全部迁移到Metal——跨平台项目里,你可以把“思考带宽”这种策略带回通用的图形代码里:少用中间纹理、多用Subpass、尽量在GPU片内完成数据复用、减少Render Pass切换。这些优化在任何现代GPU上都是受益的,只是Apple平台会让收益显得尤其巨大。
有一点我想特别提醒:网上很多“Apple GPU跑大模型比NVIDIA慢多少倍”的对比测试,其实都没有触及这颗GPU的设计目标。从架构出发,Apple GPU是为“实时图形渲染”这一特定任务优化的,通用计算只是附加能力。拿它当通用计算卡来评判,跟拿一台超大功率的高端冰箱去比较“谁更能装货”一样,维度本身就是错位的。
7. 一些实际的项目排查心得和最终建议
最后分享一个我个人的项目排障经历,也许能帮还在纠结“为什么我的Metal渲染性能这么差”的朋友快速定位问题。有一次我把一个桌面端D3D11渲染器移植到Metal上,效果是能跑,但IPC(Intel Perf Counter)测出来的帧时间比原生D3D11还慢30%。我当时第一反应是API翻译层的开销太大,后来用Xcode的GPU Frame Debugger一看,发现罪魁祸首是DRAM write bytes一项飙到了每帧接近200MB。一查代码,发现我在一个Pass里用loadAction设为Load去读取一张上一帧的时域累积纹理,强制GPU把整张纹理读到tile memory,而这张纹理实际上根本没被用到。改成DontCare之后,帧时间立刻回到正常水平,还比原版快了近15%。
这类问题在Apple平台上特别常见,因为TBDR把“不必要的DRAM读写”惩罚放得特别大,而开发者习惯了桌面的“无所谓”心态。想给自己省事的话,建议养成两个习惯:第一,所有Render Pass的附件,仔细检查其load/store必要性,不该写入的绝不设置Store,不该读取的绝不设置Load;第二,每优化一轮性能,都用Xcode的Metal调试工具查看DRAM traffic和tile利用率的变化,这两个数字是最直接的健康度指标。
最后再给一个小技巧:如果你在电脑上做GPU性能预估,别只看显卡型号和核心数,先去Apple官网或者Geekbench数据库查一下这台设备的内存带宽——对于Apple Silicon来说,真正决定GPU极限的是带宽和TBDR效率,而非核心频率。忽略这一点的性能评估,十有八九会得出偏差很大的结论。