PowerVR TBDR架构全解析:移动端GPU如何靠“省带宽”逆袭 如果只用一个词给PowerVR图形架构定位我会选“异类”。当桌面GPU还在比拼三角形吞吐和显存带宽时PowerVR选择了另一条路先把场景里的三角形按屏幕小块分好再在着色前提前干掉看不见的像素最后才逐块上色。这条路在PC战场没能赢却意外契合了移动端的生存法则于是有了Dreamcast上的惊艳、iPhone系列里的辉煌、以及今天汽车座舱里的新故事。这篇文章我想把PowerVR的TBDR架构从头到尾拆一遍告诉你它为什么省带宽、怕什么、适合怎么优化。如果你是做移动端渲染的开发者或者刚从桌面转到移动平台花点时间理解这套架构比盲目照搬优化八股有用得多。1. 为什么今天还要啃PowerVR这块“老骨头”聊PowerVR很容易变成考古现场但我还是想先把历史坐标摆出来。因为架构设计从来不是凭空长出来的PowerVR今天的形状是过去二十多年的选择堆出来的。1.1 三起三落的历史坐标PowerVR的起点其实是PC显卡。上个世纪九十年代PowerVR PCX系列和之后的Kyro系列在桌面市场跟3dfx、NVIDIA打得有来有回。当时它最大的卖点就是省带宽官方宣传的“无限像素”指的就是硬件隐藏面消除让像素着色器不白干活。但PC游戏的API生态非常看重绘制顺序和即时反馈TBDR这种“先装箱、后上色”的思路在兼容性上吃了不少亏。后来桌面市场没打赢这段经历经常被忽略——但正是这次失败逼着PowerVR把技术路线沉淀成了自己的DNA。真正让PowerVR站上行业顶点的转折点是世嘉Dreamcast。这台主机里搭载的PowerVR2把次世代画面的门槛拉低了一大截也让整个行业第一次意识到低带宽开销的GPU在复杂场景里能跑出远超同代硬件的效率。再往后就是移动端的大爆发。苹果从iPhone 4那一代开始大量使用PowerVR SGX系列从SGX535一路做到A7内置的G6430PowerVR几乎定义了早期移动GPU的性能标杆。可以说早期iPhone很多让人惊艳的图形效果底色都来自PowerVR的TBDR架构。之后的故事大家就熟了苹果转向自研GPUImagination几经易主PowerVR在手机端的声量不再像当年那么刺耳。但这不等于它消失了。这些年PowerVR的B系列、C系列、D系列持续往汽车仪表盘、舱驾一体、数据中心和光线追踪方向走反而在一个更看重“带宽、功耗、确定性”的赛道里重新找到了位置。所以今天聊PowerVR不是怀旧而是在看一套仍在进化的图形体系。1.2 移动渲染的真正短板带宽而非浮点为什么PowerVR非要走分块渲染这条路核心原因是移动端的第一瓶颈从来不是算力而是内存带宽和功耗。桌面GPU可以堆GDDR显存、堆散热移动设备不行——LPDDR颗粒的带宽被物理尺寸和功耗死死摁住。别说GPU核心光是一次DRAM读写消耗的能量就比几十次ALU运算还高这个差距在移动端是数量级的。所以移动GPU设计的第一性原则永远是“少读写DRAM少做无效计算”而不是“无脑堆浮点”。PowerVR把所有中间结果都压在片上SRAM里让颜色、深度、混合这些高频操作尽量不出芯片正是顺着这个原则长出来的。理解这一点后面所有优化手段都能串起来不是一招一式的性能小技巧而是整套架构在“向带宽要性能”。2. TBDR的省钱逻辑从光栅化到DRAM的精算图上说得再天花乱坠不如算一笔账来得直观。这一节重点回答一个问题TBDR到底把钱省在哪儿怎么省的。2.1 IMR一天到晚在浪费什么传统桌面GPU大多走Immediate Mode Rendering也就是IMR。它的流程很简单粗暴几何处理完一个三角形马上光栅化马上执行片元着色器马上写回帧缓冲。下一个三角形继续完全不管上一个三角形是否被当前这一个挡住了。这个模式最大的问题是什么overdraw。一个屏幕上可能最终只显示100万个像素但场景深度叠了三四层于是你的GPU每秒要做的像素着色量就是400万次起步。只要物体间有遮挡大量像素着色器算完全部光照和纹理采样最后深度测试一查发现自己被挡了白算。算力白费也就罢了更致命的是每一层中间结果都要写回DRAM再读出来继续混合。一个720p的帧光帧缓冲相关的DRAM往返就能轻松累积到几十MB放到手机上就是发热和掉帧的源头。很多人以为IMR加上early-z就解决问题了。其实early-z只是在片元着色之前先问一次深度缓冲“我是不是被挡住了”机制上仍然是一次像素一个三角形挨个处理没法从全局看到某个像素最终到底归属于哪个图元。遇到半透明混合、shader里discard、或者深度写入顺序混乱时早测还得退化成晚测overdraw该来还是来。2.2 PowerVR的流水账tiler装箱逐块清扫PowerVR的TBDR把整个流程拆成两段。第一段叫tiler。场景里所有图元会先做顶点变换、裁剪、背面剔除然后按屏幕坐标被“装箱”到一个一个小块里。这个块尺寸不同型号略有差异大体在16×16到32×32像素之间。一个三角形跨了几个tile就会被登记到几份区块列表中。这里要注意tiler阶段只做几何和装箱不急着给像素上色。第二段才是真正的渲染。GPU会逐个tile处理光栅化这个区块里的所有三角形做深度和隐藏面消除然后执行片元着色器把结果直接写到片上缓存的颜色缓冲和深度缓冲里。因为整个区块的颜色数据都待在同一小块SRAM里读-改-写循环根本不需要出芯片。一个tile处理完了最终颜色才被一次性写回DRAM。这套流水账最值钱的地方在于传统GPU在三角形间反复切换时帧缓冲像一杯水被倒来倒去PowerVR则是把一杯水一次性端起来走到水池边再放下。外部DRAM的读写被压缩到极致片上缓存又特别快带宽账一下子就好看很多。2.3 HSR它和early-z不是一回事PowerVR有一个常被误解的卖点叫Hidden Surface Removal隐藏面消除。很多人一听就说“那不就跟early-z差不多吗”真不是。early-z的逻辑是“运行片元着色器之前先测深度不通过就不着色”它依然是逐三角形、逐像素片段的局部判断。而HSR的出发点更狠在tile内部先把每个像素位置到底被哪个最前面的图元覆盖这件事算清楚然后让所有被遮挡的像素永远走不到片元着色器那一步。也就是说着色器只对最终可见的像素干活。对完全不透明的几何来说overdraw基本被消灭了。代价也有。因为每个三角形都必须先经过tiler装箱哪怕它完全被建筑物挡住也依然要付出一次tiler侧的几何处理。这就导致PowerVR对“顶点数量”和“draw call数量”非常敏感反而对overdraw没那么敏感。桌面转过来的团队最容易栽在这一点上明明overdraw高得吓人PowerVR设备却跑得很稳测试机卡顿瓶颈反而在几何和tiler吞吐上。下面我用一张表把三种处理方式的差异摆出来特性传统IMR带early-z的IMRPowerVR TBDR深度处理时机片元着色后/中片元着色前逐片段判断tile内HSR剔除后再着色overdraw敏感度高中低对不透明几何几何额外开销低低高tiler装箱帧缓冲DRAM流量高中高低拿一个720p场景算细账屏幕像素约92万个假设平均overdraw是2.5倍传统IMR大约要执行230万次像素着色PowerVR的HSR如果有效工作基本只算最终可见的那92万次像素着色量直接省掉六成以上。加上framebuffer读写的带宽节约整体DRAM流量差一个数量级都不奇怪。当然这是数量级估算不代表所有场景都吃满收益但方向是确定的。3. 面向PowerVR的渲染优化实践反常识清单了解架构之后就是动手环节。PowerVR有一个很反直觉的点很多桌面侧修炼多年的优化手段到了这里会变成负优化。下面这份清单是我在实际项目里反复验证过的。3.1 第一刀砍掉Z-prepass无数桌面GPU的教科书都在教人用“先画一遍深度再画一遍颜色”的Z-prepass来减少overdraw。这在PowerVR上基本是个坑。原因很简单HSR已经替你把不可见像素在片元着色前剔掉了你再做一次深度预渲染等于让所有几何在tiler里白跑一遍。几何负载翻倍颜色pass还得再来一遍装箱帧时间不升才怪。我见过不止一个项目因为“别人说移动端要做Z-prepass”就无脑在PowerVR设备上开两遍渲染结果帧率掉了一截还找不到原因。正确做法是如果做的是普通不透明场景把Z-prepass去掉靠HSR处理遮挡如果是因为半透明排序或某些深度依赖效果需要深度信息优先考虑在原有pass里输出深度而不是额外多开一整个pass。提示判断自己是否踩了这个坑最简单的办法是跑同一场景对比开/关Z-prepass的帧时间。PowerVR上绝大多数情况下关了反而更快。3.2 顶点数才是命门PowerVR和桌面GPU的性能曲线形状完全不同。桌面GPU金字塔型的瓶颈可能是像素填充率或者带宽而PowerVR对几何处理的敏感度会高很多。因为每个三角形都需要进一遍tiler即使它完全不可见也会消耗装箱资源。于是模型网格的复杂度会直接反映成性能差异这是很多从桌面转过来的团队最容易低估的。优化方向很明确第一LOD体系必须做扎实远距离物体切低模别舍不得。第二警惕“细碎三角形”——一片密密麻麻的草叶、大量接近像素大小的小三角形会让tiler和后续光栅化效率大幅下降。第三正确使用索引缓冲和顶点缓存顺序优化减少顶点变换的重复劳动。第四还是那句老话CPU侧和GPU侧剔除要做别以为PowerVR不怕overdraw就可以不做剔除。overdraw省下来了但三角形装箱成本一分也没少。3.3 半透明的排序战争HSR对完全不透明的几何非常友好可一旦碰到alpha blend事情就变了。半透明需要“读-改-写”颜色缓冲必须在tile内按正确的顺序融合。PowerVR虽然能先把某个tile的所有图元都搜刮齐但同一个tile里多个半透明三角形之间最终还是得按提交顺序逐层混合。这意味着“从远到近排序”这一条老规矩在PowerVR上依然绕不开。所以在我自己的项目里半透明物体和粒子系统都会专门维护一层视距排序。树冠、水面、烟尘这类东西能合并批次就合并尽量减少半透明draw call数量能用不透明渲染解决的绝不扔进blend阶段。另一个选择是少用alpha test/discard做树叶裁剪。它会把像素从HSR的正常流程里拽出来不仅排序困难还可能让隐藏面消除的优势打折扣。能换成alpha blend加预乘或者直接改成不透明几何性能通常更好。3.4 纹理压缩与mipmap的带宽账PowerVR的老家底是PVRTCPVRTC2在第二代做了不少改进。老SGX设备上你没有太多选择PVRTC基本是唯一高效率选项。但PVRTC有个特点它是一种全局相关度很高的压缩方案对渐变、UI边缘、细小文字这类高频内容特别不友好。2bpp压缩的纹理经常在边缘出现脏色和“振铃感”尤其白色文字配深色背景时最明显。新设备上我更推荐ASTC带宽、质量、兼容性平衡得最好ETC2则是老设备兜底优先考虑。无论选哪种都别忘了mipmap。没有mipmap的纹理在缩小时会疯狂读取相邻纹素cache命中率掉得厉害带宽损耗很容易翻倍。具体怎么选我习惯做一个小表对照格式典型码率质量特点适用场景PVRTC 2bpp最低边缘易脏、全局感强老SGX设备、大色块贴图PVRTC 4bpp中低比2bpp干净不少老设备上的漫反射贴图ETC2中兼容性好、边缘稳定通用移动设备兜底ASTC灵活质量和带宽可调新设备主力选择3.5 把后处理塞进tileSubpass的正确用法PowerVR这类TBDR架构还有个隐藏福利它支持在片元着色器里读取当前像素的framebuffer内容甚至读邻域像素也就是framebuffer fetch对应到Vulkan里就是render pass的subpass机制。这意味着很多后处理效果根本不用把整张画面先写到DRAM再读回来。举例做Bloom的第一步降采样时传统做法是先渲染一张高亮图保存到纹理再在下一次pass里采样。PowerVR上你可以在同一个pass内直接在片元着色器里把上一阶段的颜色捞出来处理数据一直待在tile的SRAM里。这就把后处理的带宽成本砍掉一大截。给项目设计render pass结构的时候我会把“同一个tile内能完成的多阶段效果”优先合成到一个subpass里而不是一上来就拆成无数fullscreen pass。需要注意的是Subpass的片上读取是有边界范围的。如果后处理的采样半径超出了tile范围比如一个大半径的高斯模糊跨到了相邻tile片上就拿不到对应数据该回DRAM还是得回。所以通常的做法是先在小范围内做局部处理再交给有一定数据交换的pass做跨tile效果分工明确。4. PowerVR、Mali、Adreno的“三角关系”移动端实际能遇到的GPU主要就这三家。它们都往“分块渲染”的大方向走但走法不一样。搞清楚差异才不会拿着一套PowerVR经验去硬套所有设备。4.1 同是分块Mali为何不能照搬PowerVR经验ARM的Mali系列同样是tile-based路线每一帧也会把场景拆成小块用片上缓冲暂存颜色和深度。但Mali的核心思路和PowerVR不是一回事。PowerVR有专门的HSR硬件能在片元着色前把不透明像素的遮挡关系处理得七七八八Mali更多是借助early-z、深度压缩、帧缓冲压缩比如AFBC这一类技术来降低带宽。这带来什么差异在Mali上overdraw并不像PowerVR那么“免费”复杂的alpha blend和较深的遮挡层数照样会侵蚀性能。Mali的优化重点经常在纹理读取和内存压缩上一些PowerVR项目里“无脑砍Z-prepass”的做法到了Mali上未必永远正确。所以跨平台优化不能只记结论要理解结论背后的架构前提。4.2 Adreno的弹性策略高通的Adreno和PowerVR、Mali很不一样。它也是tile-based渲染路线但高通的驱动和硬件设计更强调灵活性。较新的Adreno系列会根据场景特征自动决定用片上GMEM分块处理还是直接在系统内存里走更接近即时渲染的路径。这个机制在不同驱动版本、不同GPU代际上的表现还会漂移。结果就是Adreno设备上的性能表现经常“看缘分”——同一个场景换个机型或换个驱动瓶颈可能就从带宽变成了shader算力。这让Adreno成为优化工作里最难总结规律的一家。但反向看也成立正因为PowerVR的行为更确定、文档更清晰你用它当教学模型来理解“分块渲染如何省带宽”反而能学得最扎实。之后再适配Mali和Adreno手里有底座不会慌。4.3 换平台时最容易转错的三个念头这些年在跨平台项目里我总结出三个最容易带偏人的旧观念“Z-prepass是移动端标配”——对PowerVR常常是负优化在Mali上收益依场景而定在Adreno上可能时好时坏。不能一刀切。“overdraw是移动端头号大敌”——对IMR确实是对PowerVR则优先级没那么靠前头号敌人反而是几何复杂度和带宽。“TBDR都一样优化手段可以互相搬运”——三条技术路线各有各的软肋同代码在不同GPU上性能反转太常见。适配时先确认项目目标设备的主力GPU是什么再决定优化主攻方向永远比抄一份通用优化清单靠谱。5. 实测案例PowerVR设备上的三个“病症”与修复理论说完了上点实战记录。下面三个病例都是我实际撞过的问题症状不同根子都在PowerVR架构特性上。5.1 病历一上了Z-prepass反而没提速第一次带iOS项目时场景是山崖、树林加一层层遮挡物目标设备是某台老款iPad用的就是PowerVR方案。团队沿用一个从桌面项目带过来的习惯所有几何先刷一遍深度再刷颜色。结果帧时间高居不下GPU负载拉满但带宽数值并不高。后来我用PVRPerfKit里的抓帧工具逐pass看真相才浮出水面场景几何量本来就大两遍渲染让所有三角形在tiler里过了两遍几何压力直接翻倍而第二遍颜色pass里HSR本来就能把被山崖挡住的树木处理掉深度预pass纯属多此一举。修法也很痛快砍掉Z-prepass只留颜色pass帧时间立刻掉下去一截。这个案例的价值在于提醒所有人优化前先定位瓶颈别让“桌面经验”替你决定怎么做。5.2 病历二透明粒子造成的发热风暴另一个项目里烟尘粒子系统是性能黑洞。粒子shader里为了裁出边缘用了alpha test/discard再加上大量半透明粒子按层混合整个PowerVR设备迅速升温掉帧。问题有两层一是discard让HSR没法正常预判该像素的可见性等于把一部分像素从隐藏面消除体系里拽了出来二是大量半透明层需要顺序混合驱动没法帮你优化。修法分三步去掉discard改成预乘alpha的blend重新生成粒子纹理让边缘本身就是软过渡而不是硬裁把所有粒子批次合并减少draw call。改完以后画面几乎没变发热和掉帧却肉眼可见地缓解了。这个案例说明在PowerVR上想省性能尽量别跟架构过不去。5.3 病历三PVRTC纹理的绿色花边还有一次是美术素材出问题UI图标用PVRTC 2bpp压缩后文字边缘出现一层脏色和“花边”尤其白字深底时特别明显。这不是美术偷懒而是PVRTC的压缩特性决定的码率太低高频边缘信息被整体压糊了边缘色块往外溢。修法取决于设备范围。老SGX设备上把敏感UI纹理改成4bpp或者不用压缩格式新设备上直接切ASTC并重新生成mip链问题基本绝迹。经验是检查纹理压缩质量不要只看包体大小要在目标设备上实际放大边缘看尤其是UI、法线贴图、细碎高光这类对高频信息敏感的贴图。5.4 用PVRPerfKit把性能瓶颈钉在报表上最后说说工具链。Imagination的老牌工具是PVRPerfKit里面有PVRMonitor负责实时监测GPU频率、带宽、帧时间有PVRFrame负责逐draw call抓帧回放。我调试PowerVR性能时的固定流程是先开PVRMonitor跑一段目标场景看是哪条曲线先顶到上限带宽顶到就用纹理压缩、合并pass、降MSAAGPU算力顶到就压shader复杂度、控像素量几何相关指标高就减顶点、做剔除、降LOD。这套流程看起来朴素但很多性能问题恰恰卡在“不知道瓶颈在哪”这一步。PVRMonitor的几条曲线一对比问题通常立刻暴露比闷头改shader高效太多。6. 把TBDR思维变成你的默认工作方式聊了这么多PowerVR的架构细节和优化实践最后分享点我自己的体会。6.1 三条心法第一先把“不必要的工作”消灭在tiler之前。PowerVR提醒我们能不让GPU看到的三角形就别让GPU看到——剔除、LOD、减顶点的价值永远排在最前面。第二别用桌面GPU的眼光去评判overdraw。同一个像素被画几次在IMR和TBDR上的代价完全不同动手优化前先搞清目标设备属于哪种流派。第三所有优化最后几乎都能折算成带宽账。帧率上不去时先问自己哪一步在读DRAM哪一步在读不必要的纹素哪一步把中间结果倒来倒去6.2 进阶方向如果想继续往深处走我的建议是把手头的渲染代码主动往“分块友好”的方向改造比如尝试把延迟渲染的GBuffer写在tile内、用subpass合并后处理、研究半透明物体的Ordered Opaque优化思路。PowerVR只是TBDR的代表Mali和Adreno同样在向这个方向靠拢这套思维会越来越通用。我自己从PowerVR起步后来同时做Mali和Adreno适配最大的感受是架构差异不只有参数高低它还会改写你优化时的第一反应。PowerVR教会我的一件事永远不会过时——在动手调shader之前先问自己一句“这个像素真的必须画吗”。如果答案不确定那就回到帧缓冲带宽这个总账上重新算一遍。