“这份讲义只用 16 页讲一件事:PowerVR 全系建在 TBDR 上,而 TBDR 的核心设计原则是把系统内存带宽压到最低:进出内存的数据流正是图形硬件功耗的最大来源。它先把三种管线摆在一起比,再落到自己的两段式实现,最后用一款手游把账结掉。这一篇沿同一条链走,并把每个数字的口径与边界补齐。”
- 先立坐标:GPU 与 CPU 从晶体管分配就分道——一边堆缓存与流控制,一边堆寄存器与 ALU,这才有了 SIMD 与「几百万线程」的前提。
- 算两笔效率账:向量宽度 4 撞上 3 分量代码只剩 75%、单标量只剩 25%;专用着色模块的时间线上则留下一段 Stall 与浪费,这是 PowerVR 全面转向统一架构(且 Series 6 起转向标量)的理由。
- 认清 overdraw:它指「为对最终画面没有贡献的像素着色」,而 Early-Z 的补救受制于提交顺序、排序粒度与状态切换三方冲突。
- 对比三条管线:IMR 缓冲住系统内存、TBR 把缓冲搬上片但明确不碰 overdraw、TBDR 用 HSR 与延迟着色把不透明部分的 overdraw 完全摘掉。
- 把账结掉并划边界:Shadowgun 实测每屏幕像素从 4.7 个片段降到 1.2 个(约 75%),但 1.2 到不了 1,因为场景并非完全不透明;另有六条适用范围写在最后一章。
01开场:一份讲「省带宽」的移动 GPU 架构讲义
先把这份文档的来路说清。它是 Imagination Technologies 面向开发者的《PowerVR Hardware — Architecture Overview for Developers》,Revision 号写的是 PowerVR SDK REL_18.2@5224491,一共 16 页:引言、现代 3D 图形架构概览、图形架构概览、PowerVR 是什么、更多信息、联系方式,外加一个 20 条的术语表(Appendix A)。它不算论文,也不是白皮书,更像一份「假设你已经会写 OpenGL 或 DirectX 管线、现在来告诉你硬件那一侧为什么长这样」的入门讲义。
这份讲义要论证的事只有一件:PowerVR 全系都建在 Tile Based Deferred Rendering(TBDR,分块延迟渲染)上,而 TBDR 的核心设计原则是「把图形硬件对系统内存的带宽需求压到最低」。引言里那句话把因果讲得很直白:TBDR 的着眼点是尽早在管线里剔掉冗余操作,从而同时少用带宽、少耗电、还把吞吐做上去。到了第 4 节,理由被补全:系统内存的进出数据是图形硬件功耗的最大来源,所以这一块的任何削减都会直接换来更低的运行功耗;而带宽需求的下降、连同它带来的硬件优化(比如用片上缓冲),又让应用能跑出比其他架构更高的性能。低功耗与高性能这对通常互相拉扯的目标,在这里被同一个手段同时满足,这正是这份讲义值得精读的地方。
把这条逻辑摊开,就是一份完整的架构论证链:先讲 GPU 为什么要 SIMD、要跑几百万线程(第 2 节);再讲向量与标量两种着色架构各自的效率账(第 2.2 节);接着把三种主流管线 IMR、TBR、TBDR 摆在一起比(第 3 节);然后落到 PowerVR 自己的两段式实现,即 Tiler 与 Renderer(第 4.1 节);最后用一款真实手游的 overdraw 实测把账结掉(第 4.2 节)。这一篇就按这条链,一章一章把它拆开。
还有一个背景值得先记住。第 5 节列了 PowerVR 至今的商业化代数:Series5、Series5XT、Series6、Series6XT、Series 7、Series 8XE,并且明确说这些现代代数全部基于第 3.3.3 节讲的那套 TBDR。这句话等于给整份文档定了位:它讲的对象超出一款芯片,是一个跨越多代、前后一贯的架构主张。读到后面那些管线图时,可以把它们当成这条主张在不同代数上的同一张脸。
这份讲义还有一层身份值得记下来。它的页眉标着「Public」,说明它是可以对外散布的开发者材料;而它的内容又确实落在「定义产品」这一侧:TBDR 是 Imagination 的专利架构,这份文档就是厂商自己对它的正式解释。于是它同时有两重用途:对外,它是开发者第一次接触 PowerVR 时的入门读物;对本篇而言,它是这一套术语与模块划分的一手出处。后面凡是提到 TA、ISP、TSP、PB、tile 这些名词,都可以回到这份文档去找它的原始定义,而不是靠二手资料转述。
最后交代一句阅读门槛,以免读错方向。原文在前言里加了一条注:这份文档假设你熟悉 OpenGL、DirectX 或类似 3D 图形 API 的渲染管线。它的写法因此是「对照式」的:每讲一处硬件设计,都会顺手和你熟悉的 API 行为挂上钩,比如「提交顺序会影响 Early-Z 的效果」「缓冲区的读写发生在哪里」。这一篇会保留这种对照的读法,把每一项硬件决定都落回「这一笔开销原来花在哪儿」。
02SIMD 与 SoC:CPU 和图形核心从分配晶体管就分道了
起点是那颗 SoC。原文的第 2 节开门见山:现代 System on Chip 往往把 CPU 和一个图形处理器集成在一起,而这两者在设计取向上是相反的。CPU 被优化来处理顺序的、高度分支的数据集,这类任务要求低内存延迟,于是晶体管被大量投给流控制与数据缓存;图形核心反过来,被优化来处理重复的、无分支的大批量数据,也就是 3D 渲染这种活,于是晶体管被投给寄存器与算术逻辑单元,而不是数据缓存和流控制。图 1 把这两种资源分配画在同一张图上,这是理解后面所有取舍的前提。
为什么图形核心敢把缓存做小?因为它的负载的形态和 CPU 完全不同。原文接着讲 Single Instruction, Multiple Data(SIMD):典型 CPU 被优化成一次在少量数据上执行庞大而重分支的任务,一个线程往往独一无二、基本独立于其他线程运行,任意一个处理单元只处理一个线程;一个程序在 CPU 上的线程数通常是一到八个,最多几十个。图形处理器的工作前提正好相反:同一段代码会在多个线程里被执行,线程数量常常达到数百万级,这些线程只在输入上不同,执行步骤基本一致。
把这句话翻译成硬件动作,就是 SIMD 本身:每个图形处理器在多个线程上并发执行同一条指令。因为线程之间「问的是同一个问题、只是拿的数据不同」,硬件的控制开销被摊薄到几乎可以忽略,整套逻辑可以做得又窄又密。原文在这里顺带分了两种实现:SIMD 处理器要么是标量(一次处理一个元素),要么是向量(一次处理多个元素)。这条分叉就是第 3 章要算的那笔效率账。
原文还给了一句带条件的结论:在应用结构正确的前提下,SIMD 架构的主要优势是能并行跑起数量可观的线程,而且效率极高;SIMD 架构能同时运行的线程数通常比典型 CPU 高出好几个数量级。但这句话有两处限定值得抄下来:一是「结构正确」,二是「面向大规模相干数据集」。原文明确说,SIMD 是为在大而相干的数据集上运行而设计的,在这类任务上表现异常出色;独立地在大规模相干数据集上做运算的算法,也就是图形与图像处理这一类,才最契合这种处理器。换句话说,SIMD 不是「更快的通用处理器」,它是「在特定形状的负载上快到不像话的处理器」;负载一旦散乱,优势就会被抵消掉。
03向量与标量:4 宽向量撞上 3 分量代码,效率只剩 75%
SIMD 的两种实现,在这份文档里被算了一笔很清楚的效率账。原文说,现代图形核心架构的处理单元要么基于向量、要么基于标量,两种都被不同代的 PowerVR 支持:Series 5 用向量,Series 6、Series 7 与 Series 8 用标量。定义很简单:标量处理单元一次处理一个值,向量处理单元一次处理多个值。
向量处理在某些场合极其高效,因为它一次能算多个值而不是一个。对颜色与顶点运算来说尤其合适,因为这类计算天然就在 3 个或 4 个分量上同时进行,传统渲染操作因此与向量架构非常契合。但原文紧接着给了一个必须记住的代价:如果代码里用的是标量值,或者用了比处理器预期更窄的向量,那么处理器的额外宽度就被浪费掉了。
原文把它落成了一个可以复算的比例。最常见的向量宽度是 4,也就是一次能容纳 x、y、z、w 四个分量;如果一份着色器主要操作 3 分量向量,那么这些指令的执行效率就是 75%;如果一份着色器一次只处理一个标量,效率可能低到 25%。把这个关系写成式子:
这条判据也不是纸面推演,它对应着 PowerVR 自己的换代选择。Series 5 的着色架构是向量式的,而 Series 6、7、8 换成了标量式;一份官方讲义把这件事写进「向量与标量」这一节,说明它认可「负载形态变了、架构就该跟着变」这条逻辑。从向量转向标量,代价是那些本来天然填满 4 个分量的颜色与顶点运算不再享有「一次四个」的便利;换来的是当着色器开始写分支、循环和标量控制流时,不再有 25% 到 75% 的宽度被白白空转。把这条放在手机这个功耗受限的场景里看,取舍的方向就更清楚了:能效优先于峰值填充率。
标量路线走的不是同一个方向。原文说,标量处理器在每个硬件周期内能执行的操作类型通常更灵活,因为不需要拿数据去填满额外的处理宽度;虽然向量架构在同样的硅面积上潜在能处理更多值,但对未向量化的代码来说,标量架构每个时钟周期产出的有效结果通常更多。原文由此给了两条结论:标量架构更适合通用处理,也更适合更高级的渲染技术。
把这段读透,能看出 PowerVR 换代时的一个判断:Series 5 用向量,是因为当时渲染操作高度规整、天然按 3 到 4 分量成组;而到了 Series 6 及以后转向标量,理由写在原文那句「更高级的渲染技术」里:着色器越来越像通用程序,分支、循环、标量控制流越来越多,向量宽度的填充率就成了实打实的损耗。换句话说,这条判据要回答的是「被测的负载长什么样」,而不是「哪种架构更快」:负载规整时向量占优,负载散乱时标量占优。这条判据在第 3 章的三条管线对比里会再出现一次,只不过换成了带宽与 overdraw 两个维度。
04统一与非统一架构:专用模块的时间线上有块空白
讲完着色架构的宽度,原文转到着色架构的组织方式。它给出一对概念:统一着色架构(unified)把片段着色器与顶点着色器这类着色程序放在同一批处理模块上执行;非统一架构(non-unified)则为顶点处理和片段处理各配一套专用模块。原文明确说,所有 PowerVR 硬件平台都采用统一着色架构。
为什么统一更好,原文没有停在「更省」这种结论上,而是用图 2 把原因画成了两条时间线。图里的两个面板分别是「Dedicated Shader Processing Modules」(专用着色处理模块)与「Unified Shader Processing Modules」(统一着色处理模块),横轴都是 Time,纵轴上分别标着 Vertex(顶点)与 Fragment(片段)。
原文对这张图的注解是:它展示了统一架构相比非统一架构,如何既省电又提升性能。省电的部分好理解:专用模块在等待时同样在耗电,闲置即白耗;性能的部分则来自同一批模块可以被两类任务共享,于是负载偏斜不再是灾难。原文还补了一条更实用的说法:统一架构在「某个应用到底是片段着色受限还是顶点着色受限」这件事上也更容易适应,因为统一处理器会按需被分配到相应的一侧。
统一架构的另一层含义不在硬件,而在软件栈。既然同一批模块既能跑顶点着色也能跑片段着色,硬件上就不存在「顶点模块」与「片段模块」的固定配比,编译器与驱动也不必按固定的资源划分去安排工作,模块是共享的资源池,任务按数据就绪与否被投进去。原文说统一架构「更容易适应某个应用到底是片段着色受限还是顶点着色受限」,说的正是这件事:负载偏斜时,资源池自己向吃紧的一侧倾斜,而不需要应用或驱动预先猜。原文最后补了一句适用范围:所有 PowerVR 硬件平台都采用统一着色架构。
把图 2 与第 3 章的向量/标量放在一起看,会发现 PowerVR 讲的其实是同一件事的两个层面。上一章讲的是「一个处理单元内部的宽度怎么用满」,这一章讲的是「多个处理单元之间的任务怎么排匀」。前者决定单个 ALU 的填充率,后者决定整块芯片的利用率;两者都属于「别让硬件闲着」这一个母题。而这个母题会在下一章结出一个更贵的果实:图形硬件里最贵的一种「闲着」,出在内存带宽被白白消耗上,而非算力空转。
05Overdraw:什么叫「白算」,以及它为什么取决于提交顺序
第 3 节先给了整篇文档最要紧的一个定义。原文的原话是:overdraw 指的是浪费地为那些对最终图像颜色没有贡献的像素着色。术语表里又把它复述了一遍,并补了触发条件:当为某个已绘制对象着色的像素被另一个对象覆盖时,overdraw 就发生了。
原文强调它不可避免的原因很朴素:绝大多数应用提交的是三角形网格,这些网格之间可能互相遮挡、甚至彼此穿插,而画家算法的顺序(谁先谁后)并不由「谁离相机近」决定。于是先画的那个物体,它被后来者盖掉的那部分像素,已经算过的颜色就白算了。原文由此说,图形核心会内置一些削减 overdraw 的技术,典型代表是 Early-Z 测试。
但原文没有把 Early-Z 说成万灵药,而是紧接着给了一个限定:这类技术的效率非常依赖应用的绘制调用提交顺序。这句话在第 3.3.1 与 3.3.2 节被展开成一段相当具体的批评,值得在这里先记个纲要,后面两章会分别对应它。原文说的是:应用要把几何按从前到后的顺序提交给硬件,这一优化才能生效,而这就要求每一帧都为移动的相机或几何做排序。原文还指出这带来两笔额外负担:一是排序本身的开销,因为只要相机或场景里任何物体动了,这一帧的排序就得重来;二是排序的粒度很粗,按「每个绘制调用」排序解决不了物体互相穿插造成的 overdraw。更麻烦的是第三笔代价:为了拿到 Early-Z 的好处而重排绘制调用,等于放弃了另一类排序目标,即把图形 API 的状态切换压到最少。
这就构成了一个典型的工程两难:排序的收益是减少 overdraw,排序的代价是重排开销加状态切换变多,而排序的效果上限又受限于粒度。原文用这个两难做铺垫,引出 TBDR 的对照项:它宣称的正是「不管绘制调用按什么顺序提交,HSR 都能把 overdraw 完全消除」。这句承诺的份量要放到第 4.2 节的实测里才称得出来,这里先把定义与两难记住。
把这三笔代价换算到移动端,量级会更清楚。overdraw 的每一次「白算」都不只是一次着色运算:它通常还带上一次纹理数据读取,而在 IMR 里,被覆盖的结果还要真的写进系统内存里的颜色缓冲、再被后来的像素覆盖一次。也就是说,一次白算的像素要付三笔账:算力、纹理带宽、缓冲写入。而原文开篇就说清了,进出系统内存的数据流正是图形硬件功耗的最大来源。于是 overdraw 在手机上的分量,与它在桌面独显上的分量完全不是一个量级:桌面上它主要是性能问题,在手机上它同时是续航问题。
最后把这个概念和上一章的母题接上。算力空转浪费的是时间,overdraw 浪费的除了时间还有带宽:每一个被覆盖的像素,不仅白走了一遍着色,还可能白取了一遍纹理数据;而在 IMR 里,颜色与深度缓冲又都在系统内存里,这些白算的结果还要真的写进去再被覆盖。于是「消除 overdraw」这件事在移动端的分量,就远远超过了一个画质优化的范畴:它直接决定了整块芯片的功耗。这也解释了为什么下一章要从三条管线的对比讲起。
06IMR:每个物体走完整条管线,代价是系统内存反复读写
三种管线里最好懂的是第一种。原文对 Immediate Mode Rendering(IMR,立即模式渲染)的描述只有一句,但这句话界定了它全部的性格:每个被提交的物体立刻走完整个管线:被变换、被光栅化、被着色,然后才轮到下一个物体。这种「来一个画一个」的直白做法省掉了所有调度复杂度,代价藏在两处:原文明说,简单的 IMR 架构有一系列低效之处,会导致算力与内存带宽的浪费,而图 3 就是这条管线的标准形态。
顺着这条图往下读,IMR 的第一个低效点就落在「多出了一条晚测」上。管线里既有 Early Visibility Test,也有 Late Visibility Test;前者正是原文说的 Early-Z 类技术:把深度测试提前到着色之前做,从而减少 overdraw。但它有前提:应用必须把几何按从前到后的顺序提交给硬件,这一优化才能被吃到;而原文提醒,这意味着只要相机或几何在动,每一帧都得重新排序。
图 3 里还有一处细节值得单独记一笔:与深度相关的测试在这条管线上出现了两次:Raster 之后的 Early Visibility Test,以及 Texture & Shade 与 Alpha Test 之后的 Late Visibility Test。原文正文只把它们列在流程里,没有展开各自的分工;这份讲义在这一层把解释权留给了读者,也就是它在前言里预设的那份「熟悉 OpenGL/DirectX 管线」的知识。对本篇来说,这条细节的价值在于它提示了一件事:即便在 IMR 里,深度测试也不是一个可以随便前移的开关:前移能省 overdraw,但前移之后会遇到那些还没算完就不该被丢弃的像素。
第二个低效点在带宽,而且它比 overdraw 更结构性。原文的分析是:既然颜色、深度、模板缓冲都住在系统内存里,那么每次深度测试、每次颜色写入都要真的访问那片内存;而这些访问要重复发生:读出现值、改掉一部分、再写回去,也就是「读-改-写」。像素级、每帧级、重复发生的 RMW,累加起来就是原文说的「很大的系统内存带宽开销」。原文也交代了 IMR 阵营的应对:现代 IMR 会配一块很大的图形缓存,目标就是减少系统内存的访问。这句话反过来也是一条线索:需要靠大缓存去兜,说明访存模式本身没有被改变。
把 IMR 的这两点合起来看,它就是一份「拿带宽换简单」的设计:结构直观、调度省心,但 overdraw 要靠应用排序去躲,缓冲读写要靠大缓存去缓。原文用它做反面参照,并不意外:接下来的 TBR 正是冲着带宽那一点去的,而 TBDR 则把两点一起收掉。
07TBR:先分块再渲染,把深度与颜色搬进片上
第二种是 Tile Based Rendering(TBR,分块渲染),它动了 IMR 的存储位置。原文先定义 tiling:把经过变换的几何数据分箱(binning)到一个个小的矩形区域里,这些区域就叫 tile。之后的光栅化与片段处理都按 tile 为单位进行。原文把 TBR 拆成两个阶段:顶点处理,与逐 tile 的光栅化。
这一步改动带来的收益是纯带宽意义上的。原文的说法是:图形硬件因此可以在片上缓冲里做颜色、深度与模板的 RMW,从而避免代价高昂的系统内存传输,改用高速片上内存。这正是 IMR 那一章留的坑被填上了:缓冲不再住系统内存,RMW 不再跨出芯片。原文把它概括为「TBR 相对传统 IMR 设计的改进」。
图 4 还有一处细节能反过来解释「为什么只搬缓冲、不搬纹理」:Textures Data 与 Frame Buffer 这两个框仍然被画在 System Memory 那一条带上。原因不难推:纹理总量远大于一个 tile 的缓冲,片上放不下;而颜色与深度缓冲的活跃工作集正好被 tile 尺寸限定住,才有搬上片的可能。这条对比也说明 TBR 的收益是有明确范围的:它优化的是逐像素反复读写的那一小块,不是全部的显存流量。原文对它的定位因此也很克制,只说它「改进了传统 IMR 设计」,没有把它说成一套全新的架构主张。
但原文紧跟着划出了一条硬边界:TBR 并不试图减少 overdraw。原因是它在渲染每个 tile 的时候,几何仍按提交顺序处理;被遮挡的片段照旧会被处理,于是冗余的颜色计算与纹理数据读取照样发生。原文给出的补救仍然是 Early-Z,并且把 IMR 那一章的限定原样重复了一遍:和 IMR 一样,应用必须把几何从前到后排序并提交,才能把 Early-Z 减 overdraw 的效果放到最大。这句话值得单独标出来,因为它意味着:把缓冲搬上片,解决的是带宽;overdraw 是另一笔账,TBR 没碰它。
到这里,三种管线的分工轮廓就出来了:IMR 两个问题都在;TBR 解决了缓冲位置与随之而来的 RMW 带宽,但 overdraw 仍要靠应用排序去躲;而原文之所以把 TBDR 单列一节讲,就是因为它是对第二个问题的正面回答。下一章会看到,TBDR 的办法把重点从「让应用排得更好」挪到了「在硬件里把不该着色的像素先摘掉」。
08TBDR:把着色推迟到可见性判完之后
第三种就是全文的主角。原文说,TBDR 把「逐 tile 的渲染过程」拆成两个阶段:Hidden Surface Removal(HSR,隐藏面消除)与延迟的像素着色。这两个词各管一件事。
HSR 解决的是「谁被挡住了」。原文先给了一个前置观察:当一个由三维物体构成的场景被创建出来时,某些物体与表面会遮住另一些物体的全部或一部分。HSR 就是把这些被遮挡的部分从渲染中剔除的那个过程。注意这里用的是「从渲染中剔除」,而不是「算出来再丢掉」,这个差别贯穿下一章的全部数字。
「延迟」解决的是「什么时候算颜色」。原文对 deferred rendering 的定义是:架构会把所有的纹理与着色操作推迟,直到所有可以延迟的对象,主要是那些不透明的几何,都被做完可见性测试。这句定义里有个限定词很关键:「可以延迟的」。它说明了延迟的适用范围,也预告了它的边界,第 14 章会专门回来算这笔账。
图 5 相比图 4 还多出一个框:Tag Buffer(标签缓冲),它与 On-Chip Depth Buffer、On-Chip Colour Buffer 并列在片上那一组里。原文正文没有展开它的职责,只在图上给了名字;本篇如实标注这一点,不替它编功能。能确定的是位置信息:它属于片上缓冲这一族,且出现在 TBDR 的管线里而非 TBR 的管线里,也就是与 HSR 这一段的引入同时出现。把这条和前面那句「ISP 只抓取该 tile 内几何的屏幕空间位置数据」放在一起读,可以确定 HSR 需要一份与逐像素判定相关的簿记,而这份簿记同样被安排在片上。
原文在图 5 之后给了一句结论式的说明:HSR 的效率高到对完全不透明的渲染,overdraw 可以被完全消除,而这一点会显著降低系统内存带宽需求。把这句话和前一章对照,TBDR 与 TBR 的差别就非常清楚了:TBR 让被遮挡的片段「先算完、再被覆盖」,TBDR 让它们「根本不算」。前者省的是搬运,后者省的是计算本身,以及计算所牵动的那一串纹理读取。
把这三种管线放在同一张表里,各自的取舍就一目了然:
| 维度 | IMR 立即模式 | TBR 分块渲染 | TBDR 分块延迟渲染 |
|---|---|---|---|
| 颜色/深度/模板缓冲位置 | 系统内存 | 片上(按 tile 裁剪) | 片上(按 tile 裁剪) |
| RMW 是否跨出芯片 | 是,累积成大幅带宽开销 | 否,走高速片上内存 | 否,走高速片上内存 |
| 是否消除 overdraw | 否,靠 Early-Z 缓解 | 否,明确不试图减少 | 是,不透明渲染可完全消除 |
| 是否依赖提交顺序 | 依赖,须逐帧从前到后排序 | 依赖,同 IMR | 不依赖,与提交顺序无关 |
| 着色时机 | 物体走完管线即时着色 | 逐 tile、按提交顺序着色 | 可见性判完之后才着色 |
| 代价 | 带宽 + overdraw 双付 | 仍需应用排序,overdraw 未解 | 需 tile 级中间存储(PB) |
这张表最右边一列是 PowerVR 的选择,最下面一行是它的账单:为了把 overdraw 消掉、把缓冲留在片上,它需要一套 tile 级的中间存储与两段式的执行结构。下一章就去看这套结构具体长什么样,以及那笔「中间存储」的账到底有多大。
09PowerVR 全貌:Tiler 与 Renderer 的两段式
原文第 4 节先给了 PowerVR 的身份:它是 Imagination Technologies 的图形硬件 IP 家族,所有代际都基于其专利的 TBDR 架构,核心设计原则是「把图形硬件的系统内存带宽需求压到最低」。理由在下一页被补全:进出系统内存的数据是图形硬件功耗的最大来源,所以这一块的任何削减都会让硬件能以更低的功耗运行;而带宽下降连同配套的硬件优化(例如使用片上缓冲),又让应用能跑出比其他图形架构更高的性能。原文还交代了这套取向的市场结果:正因为低功耗与高性能的这个平衡,PowerVR 图形核心在移动与嵌入式设备市场占据主导。
这套架构在实现上被切成两段,原文分别用两节讲。第一段是顶点处理,也就是 Tiler。每帧硬件会处理被提交的几何:执行应用定义的变换(例如顶点着色器),再把结果数据转换到屏幕空间。随后 Tile Accelerator(TA,分块加速器)判定每个变换后的图元落在哪些 tile 里;一旦知道这件事,逐 tile 的列表就被更新,以跟踪落在各 tile 边界内的图元。原文在这里点出了一个关键存储:变换后的几何与 tile 列表都被存进一个中间存储:Parameter Buffer(PB,参数缓冲),它位于系统内存,装着渲染这些 tile 所需的全部信息。图 6 演示的就是「帧缓冲被划分成 tile 区域」这件事。
第二段是逐 tile 的光栅化,也就是 Renderer。原文的流程描述得很细:当一个 tile 的操作开始时,对应的 tile 列表会被从 PB 取出,用来确定需要抓取的屏幕空间图元数据;接着 Image Synthesis Processor(ISP,图像合成处理器)抓取图元数据并执行 HSR,连同深度与模板测试一起做。原文在这里加了一句很值得留意的限定:ISP 只抓取该 tile 内几何的屏幕空间位置数据,也就是说,它判可见性时并不需要把完整顶点信息都取进来。之后才轮到 Texture and Shading Processor(TSP)上场,对可见像素应用着色操作(例如片段着色器)。一个 tile 渲染完成后,颜色数据被写回系统内存里的帧缓冲;这个过程重复下去,直到所有 tile 处理完毕、帧缓冲拼完。
把两段合起来写成伪码,这套结构的顺序感会更清楚:
Tiler 段(每个几何图元一次) 1 执行顶点着色器 → 结果转到屏幕空间 2 TA 判定该图元覆盖哪些 tile 3 更新这些 tile 的列表(把图元挂上去) 4 变换后的几何 + tile 列表 → 写入 PB(系统内存) Renderer 段(每个 tile 一次) 5 从 PB 取出该 tile 的列表 6 ISP 只取该 tile 内几何的屏幕空间位置 → 执行 HSR + 深度/模板测试 7 对通过可见性判定的像素,TSP 执行片段着色与纹理 8 该 tile 的颜色 → 写回帧缓冲(系统内存) 9 重复 5–8,直到全部 tile 完成
这段伪码里有一处容易被扫过去、但决定了「片上缓冲」可行性的数字。术语表说 tile 通常是 32×32 像素,那么一个 tile 就是 1024 个像素;按颜色缓冲每像素 4 字节(RGBA8)、深度与模板每像素 4 字节算,一个 tile 需要的存储是:
但也要把代价同时看清楚:PB 位于系统内存。也就是说,TBDR 并没有把「所有存储都搬上片」,它把最频繁的逐像素 RMW 搬上了片,代价是新增了一份 tile 级的中间数据要落在系统内存里,规模由这一帧的几何量决定,而不是由屏幕分辨率决定。这笔交换划不划算,取决于几何与像素哪一侧更重;第 10 章会把它的收益侧算成一个带宽数字。
10片上缓冲与固件:省下的带宽,和替驱动扛事的固件
第 4.1 节剩下的三小节,分别对应 TBDR 实现里的三处配套设计。第一处是片上缓冲的边界。原文说:颜色、深度与模板缓冲的读-改-写都在快速片上内存里完成,而不是像传统 IMR 那样依赖反复的系统内存访问;但应用选择保留的附件(attachment,例如颜色缓冲)会被写回系统内存。这句话把片上缓冲的语义划清了:它是工作区,不是存档区。渲染过程中的中间态留在片上,只有应用声明要留下的那些内容才最终落盘。
第二处是着色引擎(Shader Engine)的调度模型。原文的定义是:PowerVR 着色引擎基于一种大规模多线程、多任务的方式,由硬件管理并做负载均衡,用的是数据驱动的执行模型,以保证尽可能高的利用率。它的调度依据是「数据是否就绪」,具体表现为能在彼此独立的处理任务之间切换,从而「不惜一切代价避免数据依赖造成的停顿」。把这段话和第 4 章的图 2 对读,会发现它们是同一个母题的两种说法:图 2 讲的是不同任务之间共享处理模块,这里讲的是同一批处理模块内部怎么靠切换把等待时间填满。原文在简介里说过 SIMD 的优势「在结构正确的应用上」能发挥到极高效率,着色引擎的数据驱动调度,就是硬件侧为了「让结构不完美的负载也尽量跑满」而做的补偿。
第三处是固件(Firmware)。原文给了一个对照:在许多图形架构里,硬件图形事件是由 CPU 上的图形驱动处理的;而所有 PowerVR 图形核心都由固件管理,这让图形处理器能在内部处理绝大多数高层图形事件。收益写在后面:这样能把事件处理的延迟压到最低,并降低图形驱动的 CPU 开销。术语表把固件的角色补得更具体:它是跑在图形核心上的一个专用程序,负责处理硬件事件,例如「一个 tile 的处理完成了」。把这条和第 9 章的伪码接起来看就明白了:TBDR 的 Renderer 是「逐 tile 循环」,每个 tile 完成都会产生一个事件;如果这些事件的往返都要经过 CPU 驱动,那么在 tile 数量上是按屏幕分块数计的,次数相当可观。固件把这类事件收在 GPU 内部闭环,正是这套两段式结构能跑顺的必要条件。
顺着这条再补一层:固件的价值还体现在它和着色引擎的配合上。着色引擎由硬件按「数据是否就绪」来调度任务、在独立任务之间切换以避免依赖停顿,这类调度本身就会产生大量细粒度事件;这些事件如果没有一个常驻在图形核心上的程序来就地处理,整个调度环路就会被拉到 CPU 侧的驱动里去,延迟与开销都不可接受。换句话说,数据驱动的调度模型与固件是一对配套设计:前者制造了大量需要快速响应的事件,后者负责把它们消化在芯片内部。
最后把这一章的收益侧算成一个带宽数字。假设一个 1080p 画面(1920×1080 = 2,073,600 像素),颜色缓冲与深度/模板缓冲各按每像素 4 字节计,并且它们都像 IMR 那样住在系统内存、每次都要经历一次读加一次写:
把第 6 到第 10 章连起来看,PowerVR 的整套主张其实是一句话:把最频繁、最重复的那部分存储流量留在芯片上,把「算不算得值」的判断提前到着色之前。前者对应片上缓冲与 tile 分块,后者对应 HSR 与延迟着色,而 Tiler/Renderer 的两段式、PB 这份中间存储、以及把硬件事件收进固件,都是为这两件事服务的配套。下一章换到验证侧:这套主张在真实游戏里的兑现程度,用一款手游的实测数据来说。
11HSR 实测:不排序也能消掉 overdraw,而 Early-Z 做不到
前面十章讲的都是机制,第 4.2 节换到验证侧,用的是一场很小但很有代表性的对比。原文先摆出一个三物体的场景:绿球、红立方体、以及一个挡住它们一部分的黄锥体,然后说:在传统的 IMR 架构里,这个场景会导致球体与立方体在被黄锥体遮挡的那些区域照样算出绿色与红色。这句话就是 overdraw 的教科书式定义:颜色算出来了,但它最终不会出现在画面上。图 7 演示的就是这个场景。
Early-Z 那一路的补救,原文用一段话把它讲到了天花板。做法是:应用按从前到后的顺序提交绘制调用,这样深度缓冲会先被近处的物体填起来,距离相机更远、被遮挡的片段就能在着色之前被早退掉。收益是真的,但原文紧接着列了三笔代价,每一笔都指向「这条路走不到彻底」。
第一笔是排序本身的开销:这种提交顺序带来额外负担,因为只要相机或场景里的物体移动,绘制调用就得重新排序。第二笔是粒度的天花板:原文的理由写得很直接:按每次绘制调用(per-draw)排序太粗了,它无法解决物体互相穿插所造成的 overdraw;穿插情形下,同一个绘制调用内部既有可见部分又有被遮挡部分,按调用级排序对此无能为力。第三笔更反直觉:为了 Early-Z 的好处而重排绘制调用,等于放弃了另一类排序目标,即把图形 API 的状态切换压到最少。原文把这条单独列出来,说明它并不是次要问题:状态切换本身也是要花钱的,而两种排序目标互相冲突,应用必须在两者之间选一个。
对比项由此登场。原文用一句话给了 TBDR 的结论:在 PowerVR 的 TBDR 里,HSR 会完全消除 overdraw,且与绘制调用的提交顺序无关。这句话有两层意思要分开读。第一层是「完全消除」:原文的措辞是「完全消除」而非「减少」,代价结构因此从「按比例削减」变成「归零」。第二层是「与提交顺序无关」:这句话的真正价值不在性能,而在把排序这件事从应用侧拿掉了。第 5 章讲的那组两难(要 Early-Z 就得排序、排序又要牺牲状态切换)在 TBDR 上整个不成立,因为硬件不再依赖提交顺序来判可见性。
原文接着用一款真实手游来结账。图 8 是 MadFinger Games《Shadowgun》的一帧实机画面,作为对照组的基线帧。
这一章到这里,方法的设定已经完整:先有一个正常帧,再把「应用提交了多少色片」如实测出来(忽略一切减 overdraw 的优化),再测「PowerVR 实际算了多少色片」。两组数字之间的差额,就是 HSR 挣来的那份钱。原文给的两个数是 4.7 与 1.2,而它们的解释与复核放在下一章。
12把 75% 算清楚:4.7 与 1.2 之间是什么
实测的原始记录写在第 11 页的正文里,值得一句一句抄下来。第一个数是:在这一帧里,每个屏幕像素平均有 4.7 个片段被着色。第二个数是:在 PowerVR 设备上,每个屏幕像素平均着色 1.2 个片段。原文随附的结论是:这比应用提交的片段数少了 75%。
把这两个数放在一起,先做一次最简单的复核:削减率
换个尺度看这两个数,量级会更直观。把每像素色片数乘上屏幕像素数,就得到一帧要被着色多少次:
数字解释完了,位置也该看一眼。图 9 是把「忽略 Early-Z 与 HSR」的 overdraw 分布画成热力图,图 10 是同一帧在 PowerVR 上 HSR 之后的结果。原文给了读图的判据:像素越接近白色,说明 overdraw 越多。
两张图放在一起才有意义,而比对的前提是同一套色标与同一帧内容。原文在这一点上交代得很干净:一张记录的是忽略 Early-Z 与 HSR 时的 overdraw 分布,另一张记录的是同一帧经 HSR 处理后的结果,两者共用「越白越严重」这一条判读规则。先把这条前提记住,后一张图里残留的亮区才有解释的余地。
串起来看,这一组实测给出的结论其实有三层,而且一层比一层细。第一层是主结论:HSR 把每像素的平均着色片段数从 4.7 压到 1.2,约四分之三的着色工作被提前摘掉了。第二层是口径说明:这不是画质指标,被摘掉的是那些本来就不会出现在最终画面上的像素。第三层是一处反例,也是这一章最诚实的一句:1.2 不等于 1。原文自己解释了原因:场景并不是完全不透明,混合的 UI 元素也贡献了这个平均值。这句话把「HSR 能完全消除 overdraw」这条主张的适用范围当场划了出来:完全消除针对的是不透明的部分;一旦画面里有混合与半透明,平均值就回不到 1。下一章专门来算这类边界。
13边界与反例:这套账不是白拿的
这份讲义的说服力很大一部分来自它把账算得很干净,但干净的口径也意味着边界很窄。下面六条,多半并非原文标成「注意」的部分,它们是从数字、限定词与结构里读出来的。
边界一:零 overdraw 只对「完全不透明」成立
原文给 TBDR 的主张是「overdraw 可以被完全消除」,但同一句话里带着限定:针对的是完全不透明的渲染(completely opaque renders)。到了实测那一章,这个限定就兑现成了那个 1.2:场景并非完全不透明,混合的 UI 元素也被计入平均值,所以永远到不了 1:1。两个说法并不矛盾,但它们必须一起读:HSR 能彻底摘掉被不透明几何遮挡的片段,对半透明与混合部分则无能为力,因为那些像素本来就该参与最终颜色,不是「白算」的。把「零 overdraw」当成一整帧的属性去引用,会读高这条主张。
边界二:TBR 不等于 TBDR,它明确不碰 overdraw
「分块」和「消除 overdraw」是两件独立的事,而这份文档自己把它们分得很清楚。原文对 TBR 的判定是:虽然这个方案改进了传统 IMR,但它并不试图减少 overdraw;渲染每个 tile 时几何仍按提交顺序处理,被遮挡的片段照样会被处理,于是冗余的颜色计算与纹理数据读取照样发生。也就是说,TBR 挣的是存储位置那一笔(片上缓冲),overdraw 那一笔得等 TBDR 的 HSR 来挣。两者常被混为一谈,而原文用两节把它们切开,是很值得记的一点。
边界三:Early-Z 不是免费的,而且效果有天花板
原文对 Early-Z 的批评相当具体,可以拆成三条:一是排序成本,为了让 Early-Z 生效,应用必须从前到后提交,而只要相机或物体移动,每帧的排序就得重来;二是粒度天花板,按每次绘制调用排序太粗,解决不了物体互相穿插造成的 overdraw;三是目标冲突,为了 Early-Z 而重排绘制调用,就放弃了另一类排序目标,即把图形 API 的状态切换压到最少。这三条合起来说明:Early-Z 是一条「要应用配合、且配合了也到不了彻底」的路,而 TBDR 的价值恰恰在于它不在这条路上:原文用「不管提交顺序」结束这段对比。
边界四:片上缓冲是工作区,不是存档区
原文在讲片上缓冲时加了一句很容易被略过的限定:应用选择保留的附件(例如颜色缓冲)会被写进系统内存。这句话划出了片上缓冲的语义边界:它服务于渲染过程中的读-改-写,并不承诺把整帧的存储都留在芯片上。所以「缓冲全在片上」这种说法是不准确的;准确的说法是「逐像素的 RMW 在片上完成,最终要留下的内容按应用的声明写回」。第 9 章提到的那份额外的中间存储(Parameter Buffer,位于系统内存)也属于账单的另一半:它换来的是逐像素流量的下降,代价是每帧多出一份与几何量相关的中间数据。
边界五:这套实测是厂商口径、单一场景
4.7 与 1.2 这两个数,全部出自一款游戏(MadFinger Games《Shadowgun》)的一帧;原文没有给出第二款应用、也没有给出并列的基线平台数据。它给的是一条原理性结论(HSR 与提交顺序无关、能把不透明部分的 overdraw 消除)加上一组个案读数,而不是一份横向评测。因此这两个数的正确用法是「量级示例」,这也是本篇在第 10 章与第 12 章做复算时反复标注「量级估算」的原因:引用它们去推某一款具体芯片的性能,是这份文档本身不支持的用法。
边界六:tile 尺寸由硬件定死,应用无法调
术语表在解释 tile 时给了一句很硬的话:每个 PowerVR 图形核心的 tile 尺寸在硬件设计阶段就被决定,通常是 32×32 像素。这句话的分量在于它是不可协商的:片上缓冲的容量上限(第 9 章算过的那个约 8 KB 量级)随之被锁住,而 tile 的划分粒度也不再是应用可以调的参数。对开发者来说,这意味着「为 tile 优化」的正确做法是理解它、并让自己的几何提交与着色复杂度去适配这个既定粒度,而不是去改 tile 大小。这是这份文档少有的直接给出硬件常量的地方,值得单列一条。
把这六条放在一起,这份讲义的主张就完整了:它确实把「省带宽」这件事做到了架构级别,也确实为它付了相应的代价(PB 中间存储、两段式执行、tile 粒度的固定化,以及一条只覆盖不透明几何的零 overdraw 承诺)。读架构文档最容易犯的错,是只记住它的卖点而忘掉它的口径;这一章就是为了把口径补齐。
14术语表才是设计说明书:把每个词钉回一个部件
收尾回到文档的最后两页。Appendix A 是一张 20 条的术语表,看起来很朴素,但它其实是这份讲义最浓缩的一页:它把每一个词都钉在硬件的一个具体部件或一个具体阶段上。把它逐条读一遍,前面十四章讲的东西会重新排列成一张模块清单。
先看定义里直接写着硬件常量的两条。tile 的释义是「一个矩形像素组」,并给出 PowerVR 的典型规格 32×32 像素、且尺寸在硬件设计阶段确定;renderer 的释义被明确限定为「TBDR 管线中处理 tile 的那个阶段,包含光栅化与片段着色」,而 tiler 则被定义为「TBDR 管线中顶点着色、裁剪、投影、剔除与分块这些阶段的合称」。这三条一起,就把第 9 章那段两段式伪码的骨架写成了词条:tiler 段包含剔除(所以进入 PB 的已经是剔除过的几何),renderer 段以 tile 为单位循环。缩写表里同时躺着 TA(Tile Accelerator)、ISP(Image Synthesis Processor)、TSP(Texture and Shading Processor)、HSR(Hidden Surface Removal),把它们按顺序排开,就是 Tiler 段与 Renderer 段内部的执行顺序。
再看两条把边界写进词条的。fragment 的释义是「计算一个像素颜色所需的数据」,并附了一句关键补充:多个 fragment 可能贡献同一个像素的颜色,例如一个透明物体画在不透明物体前面时。这句话等于把第 13 章那条「1.2 为什么到不了 1」的边界直接写进了词表:词条本身就承认了 fragment 与 pixel 不是一一对应。而 overdraw 与第 5 章的定义一字不差地重复了一遍:「浪费地为那些对最终图像颜色没有贡献的像素着色」。同一份文档在正文与词表两处给出同一个定义,说明它是它希望读者记住的核心判据,而非随手写的补充。
还有几条把「谁在干什么」讲得比正文更直白。ALU 的释义是「算术逻辑单元,负责处理着色器指令」,它与第 3 章那条向量宽度 4 的效率账是同一件事的两面:浪费的宽度,浪费的就是 ALU 的运算能力。Early-Z 被定义成「一组优化的统称」,手段是「把深度测试提前到管线更早的位置以降低 overdraw」,注意用的是「统称(umbrella term)」,这与第 13 章那条「Early-Z 是一类做法、而非单一技术」的读法一致。firmware 的释义把角色落到了事件上:「跑在图形核心上的一个专用程序,处理硬件事件(例如一次 tile 处理操作完成)」,这正好解释了第 10 章为什么固件是这套两段式结构的必要配套。SIMD 的释义是「单指令在多个 ALU 上并发执行,每个 ALU 有各自的输入与输出」,比正文那句「同一段代码跑在很多线程上」更像一份接口说明。剩下几条(pixel、rasterization、render、graphics pipeline、scalar/vector shader architecture、IMR/TBR/TBDR)构成了这份文档与图形学通用词汇之间的对接层。
这张词表还有一处「缺席」值得记下来。正文里反复出现、并且承担了关键角色的两个内部存储:Parameter Buffer(PB,第 9 章那份额外的中间存储)与 Tag Buffer(第 8 章图 5 里新出现的那个框),都没有被收进术语表。同一份文档在正文里用它们讲完整条流程,却没给它们词条,这个取舍本身是有信息的:这份词表是按「图形学通用词汇 + 本架构对外可解释的阶段名」来选的,而不是把内部模块穷举一遍。对读者来说,这正好点出了一条阅读纪律:术语表能帮你把词对齐到部件,但真正承载架构细节的那几个存储,得回到正文里去读。
把这张词表当作一份设计说明书来读,能得到一个比「TBDR 更省带宽」更具体的结论:这份架构的每一处设计,都能在词表里找到一个对应的名词,而每个名词背后都有明确的存储位置、执行主体或时序位置。tile 有尺寸、PB 有归属、HSR 有执行者(ISP)、着色有执行者(TSP)、事件有处理者(firmware)、浪费有定义(overdraw)、效率有度量口径(fragment 与 pixel 的区分)。一份能让读者把每个词都指到具体部件的架构文档,本身就是这套架构清晰度的一部分。
最后回到开头那句主张。这篇讲义用十四章说明了一件事:在移动与嵌入式这类功耗极度敏感的场景里,最贵的资源是进出系统内存的那部分数据流,而非算力;而 TBDR 的全部设计,从分块、片上缓冲、隐藏面消除、延迟着色、Tiler/Renderer 两段式、以及把硬件事件收进固件,都是在为这一笔账服务。读完之后,如果只记一个判断,那大概是这句:判断一套渲染架构的取向,先看它把「不该算的东西」丢在哪一步。IMR 是算完再丢,TBR 是照算不丢,TBDR 是在着色之前就丢掉。
<image> 残留实测 0);图 6–7 源区混有低于 0.4MP 的内嵌位图,改用 3.5x 整区栅格化;图 8–10 取原生图流。全部原样引用、未修改像素、未重绘、不加中文标注、未裁切拼接,逐图四字段署名。
许可口径:原文档未附 CC 声明,按分区既有非 CC 做法逐件登记、逐图署名并标注残留风险。文中凡原文未展开的细节(片上缓冲的容量推算、IMR 管线中两处深度测试的分工、Tag Buffer 的职责等),均在正文或图注里显式标注「原文未展开」或说明其为量级估算,未替原文补写结论。
- 把三条管线放进同一张对比表:缓冲位置、RMW 是否跨出芯片、是否消除 overdraw、是否依赖提交顺序、着色时机、各自代价——六个维度一次对齐,避免把 TBR 与 TBDR 混为一谈。
- 把关键数字全部做成可复算的式子:向量效率 η = k / W(3/4 = 75%、1/4 = 25%);tile 容量 32×32×(4+4) = 8 KB;overdraw 削减率 (4.7−1.2)/4.7 ≈ 74.5%;以及一帧缓冲流量的量级估算与外推口径。
- 把「省带宽」这条主张拆成两段机制:存储侧(tile 分块 + 片上缓冲把逐像素 RMW 留在芯片内)与执行侧(HSR + 延迟着色把不该算的像素在着色前摘掉),并指出 PB 这份额外的中间存储是它的账单。
- 图注一律锚定官方 List of Figures 的编号与英文图注,读法取自各图紧邻的正文配文;原文未展开的细节(Tag Buffer 的作用、两处深度测试的分工)如实标注,不替原文编功能。
- 六条边界单列成章:零 overdraw 只覆盖完全不透明渲染、TBR 不碰 overdraw、Early-Z 的三笔代价、片上缓冲是工作区不是存档区、实测为单场景厂商口径、tile 尺寸在硬件设计阶段即定死。